summaryrefslogtreecommitdiffstats
path: root/src/responder/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'src/responder/ssh')
-rw-r--r--src/responder/ssh/ssh_cert_to_ssh_key.c342
-rw-r--r--src/responder/ssh/ssh_cmd.c409
-rw-r--r--src/responder/ssh/ssh_known_hosts.c333
-rw-r--r--src/responder/ssh/ssh_private.h100
-rw-r--r--src/responder/ssh/ssh_protocol.c252
-rw-r--r--src/responder/ssh/ssh_reply.c429
-rw-r--r--src/responder/ssh/sshsrv.c235
7 files changed, 2100 insertions, 0 deletions
diff --git a/src/responder/ssh/ssh_cert_to_ssh_key.c b/src/responder/ssh/ssh_cert_to_ssh_key.c
new file mode 100644
index 0000000..b8bc8b7
--- /dev/null
+++ b/src/responder/ssh/ssh_cert_to_ssh_key.c
@@ -0,0 +1,342 @@
+/*
+ SSSD - certificate handling utils
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/child_common.h"
+#include "lib/certmap/sss_certmap.h"
+
+struct cert_to_ssh_key_state {
+ struct tevent_context *ev;
+ const char *logfile;
+ time_t timeout;
+ const char **extra_args;
+ const char **certs;
+ struct ldb_val *keys;
+ size_t cert_count;
+ size_t iter;
+ size_t valid_keys;
+
+ struct sss_child_ctx_old *child_ctx;
+ struct tevent_timer *timeout_handler;
+ struct child_io_fds *io;
+};
+
+static errno_t cert_to_ssh_key_step(struct tevent_req *req);
+static void cert_to_ssh_key_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt);
+
+struct tevent_req *cert_to_ssh_key_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *logfile, time_t timeout,
+ const char *ca_db,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ size_t cert_count,
+ struct ldb_val *bin_certs,
+ const char *verify_opts)
+{
+ struct tevent_req *req;
+ struct cert_to_ssh_key_state *state;
+ size_t arg_c;
+ size_t c;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct cert_to_ssh_key_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (ca_db == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing CA DB path.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->ev = ev;
+ state->logfile = logfile;
+ state->timeout = timeout;
+ state->io = talloc(state, struct child_io_fds);
+ if (state->io == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->io->write_to_child_fd = -1;
+ state->io->read_from_child_fd = -1;
+ talloc_set_destructor((void *) state->io, child_io_destructor);
+
+ state->keys = talloc_zero_array(state, struct ldb_val, cert_count);
+ if (state->keys == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->valid_keys = 0;
+
+ state->extra_args = talloc_zero_array(state, const char *, 8);
+ if (state->extra_args == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ /* extra_args are added in revers order, base64 encoded certificate is
+ * added at 0 */
+ arg_c = 1;
+ state->extra_args[arg_c++] = "--certificate";
+ state->extra_args[arg_c++] = ca_db;
+ state->extra_args[arg_c++] = "--ca_db";
+ if (verify_opts != NULL) {
+ state->extra_args[arg_c++] = verify_opts;
+ state->extra_args[arg_c++] = "--verify";
+ }
+ state->extra_args[arg_c++] = "--verification";
+
+ state->certs = talloc_zero_array(state, const char *, cert_count);
+ if (state->certs == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->cert_count = 0;
+ for (c = 0; c < cert_count; c++) {
+
+ if (sss_certmap_ctx != NULL) {
+ ret = sss_certmap_match_cert(sss_certmap_ctx, bin_certs[c].data,
+ bin_certs[c].length);
+ if (ret != 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "Certificate does not match matching "
+ "rules and is ignored.\n");
+ continue;
+ }
+ }
+ state->certs[state->cert_count] = sss_base64_encode(state->certs,
+ bin_certs[c].data,
+ bin_certs[c].length);
+ if (state->certs[state->cert_count] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->cert_count++;
+ }
+
+ state->iter = 0;
+
+ ret = cert_to_ssh_key_step(req);
+
+done:
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void p11_child_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct cert_to_ssh_key_state *state =
+ tevent_req_data(req, struct cert_to_ssh_key_state);
+
+ DEBUG(SSSDBG_MINOR_FAILURE, "Timeout reached for p11_child.\n");
+ child_handler_destroy(state->child_ctx);
+ state->child_ctx = NULL;
+ tevent_req_error(req, ERR_P11_CHILD_TIMEOUT);
+}
+
+static errno_t cert_to_ssh_key_step(struct tevent_req *req)
+{
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+ int ret;
+ int pipefd_from_child[2] = PIPE_INIT;
+ int pipefd_to_child[2] = PIPE_INIT;
+ pid_t child_pid;
+ struct timeval tv;
+
+ if (state->iter >= state->cert_count) {
+ return EOK;
+ }
+
+ state->extra_args[0] = state->certs[state->iter];
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ exec_child_ex(state, pipefd_to_child, pipefd_from_child, P11_CHILD_PATH,
+ state->logfile, state->extra_args, false,
+ STDIN_FILENO, STDOUT_FILENO);
+ /* We should never get here */
+ DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Could not exec p11 child\n");
+ } else if (child_pid > 0) { /* parent */
+
+ state->io->read_from_child_fd = pipefd_from_child[0];
+ PIPE_FD_CLOSE(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->io->read_from_child_fd);
+
+ state->io->write_to_child_fd = pipefd_to_child[1];
+ PIPE_FD_CLOSE(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->io->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(state->ev, child_pid, cert_to_ssh_key_done,
+ req, &state->child_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = sss_tevent_timeval_current_ofs_time_t(state->timeout);
+ state->timeout_handler = tevent_add_timer(state->ev, req, tv,
+ p11_child_timeout,
+ req);
+ if (state->timeout_handler == NULL) {
+ ret = ERR_P11_CHILD;
+ goto done;
+ }
+ /* Now either wait for the timeout to fire or the child to finish */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ return EAGAIN;
+
+done:
+ if (ret != EOK) {
+ PIPE_CLOSE(pipefd_from_child);
+ PIPE_CLOSE(pipefd_to_child);
+ }
+
+ return ret;
+}
+
+static void cert_to_ssh_key_done(int child_status,
+ struct tevent_signal *sige,
+ void *pvt)
+{
+ struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+ int ret;
+ bool valid = false;
+
+ PIPE_FD_CLOSE(state->io->read_from_child_fd);
+ PIPE_FD_CLOSE(state->io->write_to_child_fd);
+
+ if (WIFEXITED(child_status)) {
+ if (WEXITSTATUS(child_status) != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " failed with status [%d]\n", child_status);
+ } else {
+ valid = true;
+ }
+ }
+
+ if (WIFSIGNALED(child_status)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ P11_CHILD_PATH " was terminated by signal [%d]\n",
+ WTERMSIG(child_status));
+ }
+
+ if (valid) {
+ DEBUG(SSSDBG_TRACE_LIBS, "Certificate [%s] is valid.\n",
+ state->certs[state->iter]);
+ ret = get_ssh_key_from_derb64(state->keys,
+ state->certs[state->iter],
+ &state->keys[state->iter].data,
+ &state->keys[state->iter].length);
+ if (ret == EOK) {
+ state->valid_keys++;
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE, "get_ssh_key_from_cert failed, "
+ "skipping certificate [%s].\n",
+ state->certs[state->iter]);
+ state->keys[state->iter].data = NULL;
+ state->keys[state->iter].length = 0;
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Certificate [%s] is not valid.\n",
+ state->certs[state->iter]);
+ state->keys[state->iter].data = NULL;
+ state->keys[state->iter].length = 0;
+ }
+
+ state->iter++;
+ ret = cert_to_ssh_key_step(req);
+
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ }
+
+ return;
+}
+
+errno_t cert_to_ssh_key_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct ldb_val **keys, size_t *valid_keys)
+{
+ struct cert_to_ssh_key_state *state = tevent_req_data(req,
+ struct cert_to_ssh_key_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (keys != NULL) {
+ *keys = talloc_steal(mem_ctx, state->keys);
+ }
+
+ if (valid_keys != NULL) {
+ *valid_keys = state->valid_keys;
+ }
+
+ return EOK;
+}
diff --git a/src/responder/ssh/ssh_cmd.c b/src/responder/ssh/ssh_cmd.c
new file mode 100644
index 0000000..45ab57b
--- /dev/null
+++ b/src/responder/ssh/ssh_cmd.c
@@ -0,0 +1,409 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <talloc.h>
+#include <string.h>
+#include <pwd.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+#include "responder/pam/pam_helpers.h"
+#include "lib/certmap/sss_certmap.h"
+
+struct ssh_cmd_ctx {
+ struct cli_ctx *cli_ctx;
+ const char *name;
+ const char *alias;
+ const char *domain;
+};
+
+static errno_t
+ssh_check_non_sssd_user(const char *username)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam(username);
+ if (pwd != NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "%s is a non-SSSD user\n", username);
+ return ERR_NON_SSSD_USER;
+ }
+
+ return ENOENT;
+}
+
+
+static struct sss_domain_info *
+ssh_get_result_domain(struct resp_ctx *rctx,
+ struct cache_req_result *result,
+ const char *name)
+{
+ if (result != NULL) {
+ return result->domain;
+ }
+
+ return find_domain_by_name(rctx->domains, name, true);
+}
+
+static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq);
+
+static errno_t ssh_cmd_get_user_pubkeys(struct cli_ctx *cli_ctx)
+{
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY,
+ SYSDB_USER_CERT, NULL };
+
+ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+
+ ret = ssh_protocol_parse_user(cli_ctx, cli_ctx->rctx->default_domain,
+ &cmd_ctx->name, &cmd_ctx->domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requesting SSH user public keys for [%s] from [%s]\n",
+ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : "<ALL>");
+
+ if (strcmp(cmd_ctx->name, "root") == 0) {
+ ret = ERR_NON_SSSD_USER;
+ goto done;
+ }
+
+ subreq = cache_req_user_by_name_attrs_send(cmd_ctx, cli_ctx->ev,
+ cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ cmd_ctx->domain,
+ cmd_ctx->name, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ssh_cmd_get_user_pubkeys_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return ssh_protocol_done(cli_ctx, ret);
+ }
+
+ return ret;
+}
+
+struct priv_sss_debug {
+ int level;
+};
+
+static void ssh_ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+ int level = SSSDBG_OP_FAILURE;
+
+ if (data != NULL) {
+ level = data->level;
+ }
+
+ va_start(ap, format);
+ sss_vdebug_fn(file, line, function, level, APPEND_LINE_FEED,
+ format, ap);
+ va_end(ap);
+}
+
+static errno_t ssh_cmd_refresh_certmap_ctx(struct ssh_ctx *ssh_ctx,
+ struct sss_domain_info *domains)
+{
+
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ struct sss_domain_info *dom;
+ struct certmap_info **certmap_list;
+ size_t c;
+ int ret;
+ bool rule_added;
+ bool all_rules = false;
+ bool no_rules = false;
+ bool rules_present = false;
+
+ ssh_ctx->cert_rules_error = false;
+
+ if (ssh_ctx->cert_rules == NULL || ssh_ctx->cert_rules[0] == NULL) {
+ all_rules = true;
+ } else if (ssh_ctx->cert_rules[0] != NULL
+ && ssh_ctx->cert_rules[1] == NULL) {
+ if (strcmp(ssh_ctx->cert_rules[0], "all_rules") == 0) {
+ all_rules = true;
+ } else if (strcmp(ssh_ctx->cert_rules[0], "no_rules") == 0) {
+ no_rules = true;
+ }
+ }
+
+ if (!ssh_ctx->use_cert_keys
+ || ssh_ctx->certmap_last_read
+ >= ssh_ctx->rctx->get_domains_last_call.tv_sec
+ || no_rules) {
+ DEBUG(SSSDBG_TRACE_ALL, "No certmap update needed.\n");
+ return EOK;
+ }
+
+ ret = sss_certmap_init(ssh_ctx, ssh_ext_debug, NULL, &sss_certmap_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_certmap_init failed.\n");
+ goto done;
+ }
+
+ rule_added = false;
+ DLIST_FOR_EACH(dom, domains) {
+ certmap_list = dom->certmaps;
+ if (certmap_list == NULL || *certmap_list == NULL) {
+ continue;
+ }
+
+ for (c = 0; certmap_list[c] != NULL; c++) {
+ rules_present = true;
+
+ if (!all_rules && !string_in_list(certmap_list[c]->name,
+ ssh_ctx->cert_rules, true)) {
+ DEBUG(SSSDBG_TRACE_ALL, "Skipping matching rule [%s], it is "
+ "not listed in the ssh_use_certificate_matching_rules "
+ "option.\n", certmap_list[c]->name);
+ continue;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Trying to add rule [%s][%d][%s][%s].\n",
+ certmap_list[c]->name, certmap_list[c]->priority,
+ certmap_list[c]->match_rule, certmap_list[c]->map_rule);
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx,
+ certmap_list[c]->priority,
+ certmap_list[c]->match_rule,
+ certmap_list[c]->map_rule,
+ certmap_list[c]->domains);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_certmap_add_rule failed for rule [%s] "
+ "with error [%d][%s], skipping. "
+ "Please check for typos and if rule syntax is supported.\n",
+ certmap_list[c]->name, ret, sss_strerror(ret));
+ continue;
+ }
+ rule_added = true;
+ }
+ }
+
+ if (!rule_added) {
+ if (!rules_present) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "No rules available, trying to add default matching rule.\n");
+ ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
+ CERT_AUTH_DEFAULT_MATCHING_RULE,
+ NULL, NULL);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add default matching rule [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "No matching rule added, please check "
+ "ssh_use_certificate_matching_rules option values for "
+ "typos.\n");
+
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ sss_certmap_free_ctx(ssh_ctx->sss_certmap_ctx);
+ ssh_ctx->sss_certmap_ctx = sss_certmap_ctx;
+ ssh_ctx->certmap_last_read = ssh_ctx->rctx->get_domains_last_call.tv_sec;
+ } else {
+ sss_certmap_free_ctx(sss_certmap_ctx);
+ ssh_ctx->cert_rules_error = true;
+ }
+
+ return ret;
+}
+
+static void ssh_cmd_get_user_pubkeys_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result;
+ struct ssh_cmd_ctx *cmd_ctx;
+ errno_t ret;
+ struct ssh_ctx *ssh_ctx;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx);
+
+ ret = cache_req_user_by_name_attrs_recv(cmd_ctx, subreq, &result);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ /* Check if it is a non SSSD user. */
+ ret = ssh_check_non_sssd_user(cmd_ctx->name);
+ }
+
+ ssh_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+
+ ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+ ret = ssh_cmd_refresh_certmap_ctx(ssh_ctx, cmd_ctx->cli_ctx->rctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ssh_cmd_refresh_certmap_ctx failed, "
+ "certificate matching might not work as expected.\n");
+ }
+
+ ssh_protocol_reply(cmd_ctx->cli_ctx, result);
+
+done:
+ talloc_free(cmd_ctx);
+}
+
+static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq);
+
+static errno_t ssh_cmd_get_host_pubkeys(struct cli_ctx *cli_ctx)
+{
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct tevent_req *subreq;
+ errno_t ret;
+
+ static const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL };
+
+ cmd_ctx = talloc_zero(cli_ctx, struct ssh_cmd_ctx);
+ if (cmd_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cmd_ctx->cli_ctx = cli_ctx;
+
+ ret = ssh_protocol_parse_host(cli_ctx, &cmd_ctx->name, &cmd_ctx->alias,
+ &cmd_ctx->domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid request message!\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requesting SSH host public keys for [%s] from [%s]\n",
+ cmd_ctx->name, cmd_ctx->domain ? cmd_ctx->domain : "<ALL>");
+
+ subreq = cache_req_ssh_host_id_by_name_send(cmd_ctx, cli_ctx->ev,
+ cli_ctx->rctx,
+ cli_ctx->rctx->ncache, 0,
+ cmd_ctx->domain,
+ cmd_ctx->name,
+ cmd_ctx->alias, attrs);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ tevent_req_set_callback(subreq, ssh_cmd_get_host_pubkeys_done, cmd_ctx);
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(cmd_ctx);
+ return ssh_protocol_done(cli_ctx, ret);
+ }
+
+ return ret;
+}
+
+static void ssh_cmd_get_host_pubkeys_done(struct tevent_req *subreq)
+{
+ struct cache_req_result *result = NULL;
+ struct sss_domain_info *domain;
+ struct ssh_cmd_ctx *cmd_ctx;
+ struct ssh_ctx *ssh_ctx;
+ errno_t ret;
+
+ cmd_ctx = tevent_req_callback_data(subreq, struct ssh_cmd_ctx);
+ ssh_ctx = talloc_get_type(cmd_ctx->cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+
+ ret = cache_req_ssh_host_id_by_name_recv(cmd_ctx, subreq, &result);
+ talloc_zfree(subreq);
+
+ if (ret == EOK || ret == ENOENT) {
+ domain = ssh_get_result_domain(ssh_ctx->rctx, result, cmd_ctx->domain);
+
+ ssh_update_known_hosts_file(ssh_ctx->rctx->domains, domain,
+ cmd_ctx->name, ssh_ctx->hash_known_hosts,
+ ssh_ctx->known_hosts_timeout);
+ }
+
+ if (ret != EOK) {
+ ssh_protocol_done(cmd_ctx->cli_ctx, ret);
+ goto done;
+ }
+
+ ssh_protocol_reply(cmd_ctx->cli_ctx, result);
+
+done:
+ talloc_free(cmd_ctx);
+}
+
+struct cli_protocol_version *register_cli_protocol_version(void)
+{
+ static struct cli_protocol_version ssh_cli_protocol_version[] = {
+ {0, NULL, NULL}
+ };
+
+ return ssh_cli_protocol_version;
+}
+
+struct sss_cmd_table *get_ssh_cmds(void) {
+ static struct sss_cmd_table ssh_cmds[] = {
+ {SSS_GET_VERSION, sss_cmd_get_version},
+ {SSS_SSH_GET_USER_PUBKEYS, ssh_cmd_get_user_pubkeys},
+ {SSS_SSH_GET_HOST_PUBKEYS, ssh_cmd_get_host_pubkeys},
+ {SSS_CLI_NULL, NULL}
+ };
+
+ return ssh_cmds;
+}
diff --git a/src/responder/ssh/ssh_known_hosts.c b/src/responder/ssh/ssh_known_hosts.c
new file mode 100644
index 0000000..2be240b
--- /dev/null
+++ b/src/responder/ssh/ssh_known_hosts.c
@@ -0,0 +1,333 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_ssh.h"
+#include "db/sysdb.h"
+#include "db/sysdb_ssh.h"
+#include "responder/ssh/ssh_private.h"
+
+static char *
+ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_ent *ent)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *name, *pubkey;
+ char *result = NULL;
+ size_t i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ name = talloc_strdup(tmp_ctx, ent->name);
+ if (!name) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_aliases; i++) {
+ name = talloc_asprintf_append(name, ",%s", ent->aliases[i]);
+ if (!name) {
+ goto done;
+ }
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (!result) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_pubkeys; i++) {
+ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
+ if (ret != EOK) {
+ result = NULL;
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "%s %s\n", name, pubkey);
+ if (!result) {
+ goto done;
+ }
+
+ talloc_free(pubkey);
+ }
+
+ talloc_steal(mem_ctx, result);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+static char *
+ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx,
+ struct sss_ssh_ent *ent)
+{
+ TALLOC_CTX *tmp_ctx;
+ errno_t ret;
+ char *name, *pubkey, *saltstr, *hashstr, *result;
+ unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH];
+ size_t i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ return NULL;
+ }
+
+ result = talloc_strdup(tmp_ctx, "");
+ if (!result) {
+ goto done;
+ }
+
+ for (i = 0; i < ent->num_pubkeys; i++) {
+ ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey);
+ if (ret != EOK) {
+ result = NULL;
+ goto done;
+ }
+
+ for (j = 0; j <= ent->num_aliases; j++) {
+ name = (j == 0 ? ent->name : ent->aliases[j-1]);
+
+ ret = sss_generate_csprng_buffer((uint8_t *)salt, SSS_SHA1_LENGTH);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_generate_csprng_buffer() failed (%d)\n", ret);
+ result = NULL;
+ goto done;
+ }
+
+ ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH,
+ (unsigned char *)name, strlen(name),
+ hash);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_hmac_sha1() failed (%d): %s\n",
+ ret, strerror(ret));
+ result = NULL;
+ goto done;
+ }
+
+ saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH);
+ if (!saltstr) {
+ result = NULL;
+ goto done;
+ }
+
+ hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH);
+ if (!hashstr) {
+ result = NULL;
+ goto done;
+ }
+
+ result = talloc_asprintf_append(result, "|1|%s|%s %s\n",
+ saltstr, hashstr, pubkey);
+ if (!result) {
+ goto done;
+ }
+
+ talloc_free(saltstr);
+ talloc_free(hashstr);
+ }
+
+ talloc_free(pubkey);
+ }
+
+ talloc_steal(mem_ctx, result);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return result;
+}
+
+static errno_t
+ssh_write_known_hosts(struct sss_domain_info *domains,
+ bool hash_known_hosts,
+ time_t now,
+ int fd)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom;
+ struct ldb_message **hosts;
+ struct sysdb_ctx *sysdb;
+ struct sss_ssh_ent *ent;
+ char *entstr;
+ size_t num_hosts;
+ size_t i;
+ ssize_t wret;
+ errno_t ret;
+
+ static const char *attrs[] = {
+ SYSDB_NAME,
+ SYSDB_NAME_ALIAS,
+ SYSDB_SSH_PUBKEY,
+ NULL
+ };
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ for (dom = domains; dom != NULL; dom = get_next_domain(dom, false)) {
+ sysdb = dom->sysdb;
+ if (sysdb == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Fatal: Sysdb CTX not found for this domain!\n");
+ ret = EFAULT;
+ goto done;
+ }
+
+ ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs,
+ &hosts, &num_hosts);
+ if (ret == ENOENT) {
+ continue;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Host search failed for domain "
+ "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret));
+ continue;
+ }
+
+ for (i = 0; i < num_hosts; i++) {
+ ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to get SSH host public keys\n");
+ continue;
+ }
+
+ if (hash_known_hosts) {
+ entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent);
+ } else {
+ entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent);
+ }
+
+ if (entstr == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to format known_hosts data "
+ "for [%s]\n", ent->name);
+ continue;
+ }
+
+ wret = sss_atomic_write_s(fd, entstr, strlen(entstr));
+ if (wret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ talloc_free(ent);
+ }
+
+ talloc_free(hosts);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+ssh_update_known_hosts_file(struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ const char *name,
+ bool hash_known_hosts,
+ int known_hosts_timeout)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *filename;
+ errno_t ret;
+ time_t now;
+ int fd = -1;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ now = time(NULL);
+
+ /* Update host's expiration time. */
+ if (domain != NULL) {
+ ret = sysdb_update_ssh_known_host_expire(domain, name, now,
+ known_hosts_timeout);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+ }
+
+ /* Create temporary known hosts file. */
+ filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL);
+ if (filename == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret);
+ if (fd == -1) {
+ filename = NULL;
+ goto done;
+ }
+
+ /* Write contents. */
+ ret = ssh_write_known_hosts(domains, hash_known_hosts, now, fd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write known hosts file "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+
+ /* Rename to SSH known hosts file. */
+ ret = fchmod(fd, 0644);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH);
+ if (ret == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (fd != -1) {
+ close(fd);
+ }
+
+ return ret;
+}
diff --git a/src/responder/ssh/ssh_private.h b/src/responder/ssh/ssh_private.h
new file mode 100644
index 0000000..0e4ed10
--- /dev/null
+++ b/src/responder/ssh/ssh_private.h
@@ -0,0 +1,100 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSHSRV_PRIVATE_H_
+#define _SSHSRV_PRIVATE_H_
+
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req.h"
+
+#define SSS_SSH_KNOWN_HOSTS_PATH PUBCONF_PATH"/known_hosts"
+#define SSS_SSH_KNOWN_HOSTS_TEMP_TMPL PUBCONF_PATH"/.known_hosts.XXXXXX"
+
+struct ssh_ctx {
+ struct resp_ctx *rctx;
+ struct sss_names_ctx *snctx;
+
+ bool hash_known_hosts;
+ int known_hosts_timeout;
+ char *ca_db;
+ bool use_cert_keys;
+
+ time_t certmap_last_read;
+ struct sss_certmap_ctx *sss_certmap_ctx;
+ char **cert_rules;
+ bool cert_rules_error;
+};
+
+struct sss_cmd_table *get_ssh_cmds(void);
+
+errno_t
+ssh_protocol_parse_user(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_domain);
+
+errno_t
+ssh_protocol_parse_host(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_alias,
+ const char **_domain);
+
+void ssh_protocol_reply(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result);
+
+errno_t
+ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error);
+
+struct tevent_req * ssh_get_output_keys_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg);
+
+errno_t ssh_get_output_keys_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sized_string *name,
+ struct ldb_message_element ***elements,
+ uint32_t *num_keys);
+
+errno_t
+ssh_protocol_build_reply(struct sss_packet *packet,
+ struct sized_string name,
+ struct ldb_message_element **elements,
+ uint32_t num_keys);
+
+errno_t
+ssh_update_known_hosts_file(struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ const char *name,
+ bool hash_known_hosts,
+ int known_hosts_timeout);
+
+struct tevent_req *cert_to_ssh_key_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *logfile, time_t timeout,
+ const char *ca_db,
+ struct sss_certmap_ctx *sss_certmap_ctx,
+ size_t cert_count,
+ struct ldb_val *bin_certs,
+ const char *verify_opts);
+
+errno_t cert_to_ssh_key_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct ldb_val **keys, size_t *valid_keys);
+#endif /* _SSHSRV_PRIVATE_H_ */
diff --git a/src/responder/ssh/ssh_protocol.c b/src/responder/ssh/ssh_protocol.c
new file mode 100644
index 0000000..5a9081b
--- /dev/null
+++ b/src/responder/ssh/ssh_protocol.c
@@ -0,0 +1,252 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <talloc.h>
+
+#include "util/util.h"
+#include "util/sss_ssh.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+
+errno_t
+ssh_protocol_done(struct cli_ctx *cli_ctx, errno_t error)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ switch (error) {
+ case EOK:
+ /* Create empty packet if none was provided. */
+ if (pctx->creq->out == NULL) {
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: success\n");
+ ret = EOK;
+ goto done;
+ default:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: error [%d]: %s\n",
+ error, sss_strerror(error));
+ ret = sss_cmd_send_error(cli_ctx, error);
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send reply [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ sss_cmd_done(cli_ctx, NULL);
+ return EOK;
+}
+
+static void got_ssh_keys(struct tevent_req *req);
+void ssh_protocol_reply(struct cli_ctx *cli_ctx,
+ struct cache_req_result *result)
+{
+ errno_t ret;
+ struct tevent_req *req;
+
+ /* Make sure we have the results around until the end of the request. To
+ * avoid copying and memory allocation the keys and certificates from the
+ * result will be referenced during the next requests, so they should not
+ * be freed too early. */
+ result = talloc_steal(cli_ctx, result);
+
+ req = ssh_get_output_keys_send(cli_ctx, cli_ctx->ev, cli_ctx,
+ result->domain, result->msgs[0]);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_output_keys_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(req, got_ssh_keys, cli_ctx);
+
+ return;
+
+done:
+ ssh_protocol_done(cli_ctx, ret);
+}
+
+static void got_ssh_keys(struct tevent_req *req)
+{
+ errno_t ret;
+ struct cli_ctx *cli_ctx = tevent_req_callback_data(req, struct cli_ctx);
+ struct cli_protocol *pctx;
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ struct ldb_message_element **elements;
+ uint32_t num_keys;
+ struct sized_string name;
+
+ ret = ssh_get_output_keys_recv(req, cli_ctx, &name, &elements, &num_keys);
+ talloc_zfree(req);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_get_output_keys_revc failed");
+ goto done;
+ }
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = ssh_protocol_build_reply(pctx->creq->out, name, elements, num_keys);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+
+done:
+ ssh_protocol_done(cli_ctx, ret);
+}
+
+static errno_t
+ssh_protocol_parse_request(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_alias,
+ const char **_domain)
+{
+ struct cli_protocol *pctx;
+ const char *name = NULL;
+ const char *alias = NULL;
+ const char *domain = NULL;
+ uint32_t flags;
+ uint32_t name_len;
+ uint32_t alias_len;
+ uint32_t domain_len;
+ size_t body_len;
+ uint8_t *body;
+ size_t c = 0;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &body_len);
+
+ SAFEALIGN_COPY_UINT32_CHECK(&flags, body + c, body_len, &c);
+ if (flags & ~(uint32_t)SSS_SSH_REQ_MASK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid flags received [0x%x]\n", flags);
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32_CHECK(&name_len, body + c, body_len, &c);
+ if (name_len == 0 || name_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid name length\n");
+ return EINVAL;
+ }
+
+ name = (const char *)(body + c);
+ if (!sss_utf8_check((const uint8_t *)name, name_len-1) ||
+ name[name_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Name is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += name_len;
+
+ if (flags & SSS_SSH_REQ_ALIAS) {
+ SAFEALIGN_COPY_UINT32_CHECK(&alias_len, body + c, body_len, &c);
+ if (alias_len == 0 || alias_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid alias length\n");
+ return EINVAL;
+ }
+
+ alias = (const char *)(body+c);
+ if (!sss_utf8_check((const uint8_t *)alias, alias_len - 1) ||
+ alias[alias_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Alias is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += alias_len;
+ }
+
+ if (flags & SSS_SSH_REQ_DOMAIN) {
+ SAFEALIGN_COPY_UINT32_CHECK(&domain_len, body + c, body_len, &c);
+ if (domain_len > 0) {
+ if (domain_len > body_len - c) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid domain length\n");
+ return EINVAL;
+ }
+
+ domain = (const char *)(body + c);
+ if (!sss_utf8_check((const uint8_t *)domain, domain_len - 1) ||
+ domain[domain_len - 1] != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Domain is not valid UTF-8 string\n");
+ return EINVAL;
+ }
+ c += domain_len;
+ } else {
+ domain = default_domain;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Requested domain [%s]\n", domain ? domain : "<ALL>");
+ }
+
+ if (_name != NULL) {
+ *_name = name;
+ }
+
+ if (_alias != NULL) {
+ *_alias = alias;
+ }
+
+ if (_domain != NULL) {
+ *_domain = domain;
+ }
+
+ return EOK;
+}
+
+errno_t
+ssh_protocol_parse_user(struct cli_ctx *cli_ctx,
+ const char *default_domain,
+ const char **_name,
+ const char **_domain)
+{
+ return ssh_protocol_parse_request(cli_ctx, default_domain,
+ _name, NULL, _domain);
+}
+
+errno_t
+ssh_protocol_parse_host(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_alias,
+ const char **_domain)
+{
+ return ssh_protocol_parse_request(cli_ctx, NULL, _name, _alias, _domain);
+}
diff --git a/src/responder/ssh/ssh_reply.c b/src/responder/ssh/ssh_reply.c
new file mode 100644
index 0000000..edeb287
--- /dev/null
+++ b/src/responder/ssh/ssh_reply.c
@@ -0,0 +1,429 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <talloc.h>
+#include <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "util/sss_ssh.h"
+#include "util/cert.h"
+#include "responder/common/responder.h"
+#include "responder/common/responder_packet.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/ssh/ssh_private.h"
+
+/* Locally used flag for libldb's ldb_message_element structure to indicate
+ * binary data. Since the related data is only used in memory it is safe. If
+ * should be used with care if libldb's I/O operations are involved. */
+#define SSS_EL_FLAG_BIN_DATA (1<<4)
+
+static errno_t decode_and_add_base64_data(struct sss_packet *packet,
+ struct ldb_message_element *el,
+ bool skip_base64_decode,
+ size_t fqname_len,
+ const char *fqname,
+ size_t *c)
+{
+ uint8_t *key;
+ size_t key_len;
+ uint8_t *body;
+ size_t body_len;
+ int ret;
+ size_t d;
+ TALLOC_CTX *tmp_ctx;
+
+ if (el == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Mssing element, nothing to do.\n");
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
+ return ENOMEM;
+ }
+
+ for (d = 0; d < el->num_values; d++) {
+ if (el->values[d].length == 0 && el->values[d].data == NULL) {
+ /* skip empty keys, e.g. due to invalid certificate */
+ continue;
+ }
+ if (skip_base64_decode || (el->flags & SSS_EL_FLAG_BIN_DATA)) {
+ key = el->values[d].data;
+ key_len = el->values[d].length;
+ } else {
+ key = sss_base64_decode(tmp_ctx, (const char *) el->values[d].data,
+ &key_len);
+ if (key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_decode failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_packet_grow(packet,
+ 3*sizeof(uint32_t) + key_len + fqname_len);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
+ goto done;
+ }
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(body+(*c), 0, c);
+ SAFEALIGN_SET_UINT32(body+(*c), fqname_len, c);
+ safealign_memcpy(body+(*c), fqname, fqname_len, c);
+ SAFEALIGN_SET_UINT32(body+(*c), key_len, c);
+ safealign_memcpy(body+(*c), key, key_len, c);
+
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+struct ssh_get_output_keys_state {
+ struct tevent_context *ev;
+ struct cli_ctx *cli_ctx;
+ struct ldb_message *msg;
+ char *cert_verification_opts;
+ int p11_child_timeout;
+ struct ssh_ctx *ssh_ctx;
+ struct ldb_message_element *user_cert;
+ struct ldb_message_element *user_cert_override;
+ struct ldb_message_element *current_cert;
+
+ const char *name;
+ struct ldb_message_element **elements;
+ uint32_t num_keys;
+ size_t iter;
+};
+
+void ssh_get_output_keys_done(struct tevent_req *subreq);
+
+struct tevent_req *ssh_get_output_keys_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_ctx *cli_ctx,
+ struct sss_domain_info *domain,
+ struct ldb_message *msg)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ errno_t ret;
+ struct ssh_get_output_keys_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct ssh_get_output_keys_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->cli_ctx = cli_ctx;
+ state->msg = msg;
+ state->num_keys = 0;
+ state->iter = 0;
+ state->ssh_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct ssh_ctx);
+ if (state->ssh_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing ssh responder context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->name = ldb_msg_find_attr_as_string(state->msg, SYSDB_NAME, NULL);
+ if (state->name == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Missing name.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ state->elements = talloc_zero_array(state, struct ldb_message_element *, 6);
+ if (state->elements == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ ORIGINALAD_PREFIX SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+
+ if (DOM_HAS_VIEWS(domain)) {
+ state->elements[state->iter] = ldb_msg_find_element(state->msg,
+ OVERRIDE_PREFIX SYSDB_SSH_PUBKEY);
+ if (state->elements[state->iter] != NULL) {
+ state->num_keys += state->elements[state->iter]->num_values;
+ state->iter++;
+ }
+ }
+
+ if (!state->ssh_ctx->use_cert_keys) {
+ DEBUG(SSSDBG_TRACE_ALL, "Skipping keys from certificates.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ if (state->ssh_ctx->cert_rules_error) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Skipping keys from certificates because there was an error "
+ "while processing matching rules.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ ret = confdb_get_string(cli_ctx->rctx->cdb, state,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_CERT_VERIFICATION, NULL,
+ &state->cert_verification_opts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read verification options from confdb: [%d] %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ state->p11_child_timeout = -1;
+ ret = confdb_get_int(cli_ctx->rctx->cdb, CONFDB_SSH_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT, -1,
+ &state->p11_child_timeout);
+ if (ret != EOK || state->p11_child_timeout == -1) {
+ /* check pam configuration as well or use default */
+ ret = confdb_get_int(cli_ctx->rctx->cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_P11_CHILD_TIMEOUT,
+ P11_CHILD_TIMEOUT_DEFAULT,
+ &state->p11_child_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read p11_child_timeout from confdb: [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ state->user_cert = ldb_msg_find_element(state->msg, SYSDB_USER_CERT);
+ if (DOM_HAS_VIEWS(domain)) {
+ state->user_cert_override = ldb_msg_find_element(state->msg,
+ OVERRIDE_PREFIX SYSDB_USER_CERT);
+ }
+
+ if (state->user_cert == NULL && state->user_cert_override == NULL) {
+ /* no certificates to convert, we are done */
+ ret = EOK;
+ goto done;
+ }
+
+ state->current_cert = state->user_cert != NULL ? state->user_cert
+ : state->user_cert_override;
+
+ subreq = cert_to_ssh_key_send(state, state->ev,
+ P11_CHILD_LOG_FILE,
+ state->p11_child_timeout,
+ state->ssh_ctx->ca_db,
+ state->ssh_ctx->sss_certmap_ctx,
+ state->current_cert->num_values,
+ state->current_cert->values,
+ state->cert_verification_opts);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ssh_get_output_keys_done, req);
+
+ ret = EAGAIN;
+
+done:
+ if (ret != EAGAIN) {
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+void ssh_get_output_keys_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ssh_get_output_keys_state *state = tevent_req_data(req,
+ struct ssh_get_output_keys_state);
+ int ret;
+ struct ldb_val *keys;
+ size_t valid_keys;
+
+ ret = cert_to_ssh_key_recv(subreq, state, &keys, &valid_keys);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ if (ret == ERR_P11_CHILD_TIMEOUT) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "cert_to_ssh_key request timeout, "
+ "consider increasing p11_child_timeout.\n");
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "cert_to_ssh_key request failed, ssh keys derived "
+ "from certificates will be skipped.\n");
+ }
+ /* Ignore ssh keys from certificates and return what we already have */
+ tevent_req_done(req);
+ return;
+ }
+
+ state->elements[state->iter] = talloc_zero(state->elements,
+ struct ldb_message_element);
+ if (state->elements[state->iter] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ state->elements[state->iter]->values = talloc_steal(
+ state->elements[state->iter],
+ keys);
+ state->elements[state->iter]->num_values = state->current_cert->num_values;
+ state->elements[state->iter]->flags |= SSS_EL_FLAG_BIN_DATA;
+ state->num_keys += valid_keys;
+
+ if (state->current_cert == state->user_cert) {
+ state->current_cert = state->user_cert_override;
+ } else if (state->current_cert == state->user_cert_override) {
+ state->current_cert = NULL;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected certificate pointer.\n");
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ if (state->current_cert == NULL) {
+ /* done */
+ ret = EOK;
+ goto done;
+ }
+
+ subreq = cert_to_ssh_key_send(state, state->ev, NULL,
+ state->p11_child_timeout,
+ state->ssh_ctx->ca_db,
+ state->ssh_ctx->sss_certmap_ctx,
+ state->current_cert->num_values,
+ state->current_cert->values,
+ state->cert_verification_opts);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "cert_to_ssh_key_send failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, ssh_get_output_keys_done, req);
+ return;
+done:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+
+ return;
+}
+
+errno_t ssh_get_output_keys_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sized_string *name,
+ struct ldb_message_element ***elements,
+ uint32_t *num_keys)
+{
+ struct ssh_get_output_keys_state *state = tevent_req_data(req,
+ struct ssh_get_output_keys_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (name != NULL) {
+ name->str = talloc_strdup(mem_ctx, state->name);
+ name->len = strlen(name->str) + 1;
+ }
+
+ if (elements != NULL) {
+ *elements = talloc_steal(mem_ctx, state->elements);
+ }
+
+ if (num_keys != NULL) {
+ *num_keys = state->num_keys;
+ }
+
+ return EOK;
+}
+
+errno_t
+ssh_protocol_build_reply(struct sss_packet *packet,
+ struct sized_string name,
+ struct ldb_message_element **elements,
+ uint32_t num_keys)
+{
+ size_t body_len;
+ uint8_t *body;
+ size_t c = 0;
+ errno_t ret;
+ int i;
+
+ ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_get_body(packet, &body, &body_len);
+
+ SAFEALIGN_SET_UINT32(&body[c], num_keys, &c);
+ SAFEALIGN_SET_UINT32(&body[c], 0, &c);
+
+ if (num_keys == 0) {
+ ret = EOK;
+ goto done;
+ }
+
+ for (i = 0; elements[i] != NULL; i++) {
+ ret = decode_and_add_base64_data(packet, elements[i], false,
+ name.len, name.str, &c);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "decode_and_add_base64_data failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+
+ return ret;
+}
diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c
new file mode 100644
index 0000000..91fb77b
--- /dev/null
+++ b/src/responder/ssh/sshsrv.c
@@ -0,0 +1,235 @@
+/*
+ Authors:
+ Jan Cholasta <jcholast@redhat.com>
+
+ Copyright (C) 2012 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+
+#include "util/util.h"
+#include "util/child_common.h"
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/ssh/ssh_private.h"
+#include "providers/data_provider.h"
+#include "sss_iface/sss_iface_async.h"
+
+int ssh_process_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct confdb_ctx *cdb)
+{
+ struct resp_ctx *rctx;
+ struct sss_cmd_table *ssh_cmds;
+ struct ssh_ctx *ssh_ctx;
+ int ret;
+
+ ssh_cmds = get_ssh_cmds();
+ ret = sss_process_init(mem_ctx, ev, cdb,
+ ssh_cmds,
+ SSS_SSH_SOCKET_NAME, -1, NULL, -1,
+ CONFDB_SSH_CONF_ENTRY,
+ SSS_BUS_SSH, SSS_SSH_SBUS_SERVICE_NAME,
+ sss_connection_setup,
+ &rctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "sss_process_init() failed\n");
+ return ret;
+ }
+
+ ssh_ctx = talloc_zero(rctx, struct ssh_ctx);
+ if (!ssh_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing ssh_ctx\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ssh_ctx->rctx = rctx;
+ ssh_ctx->rctx->pvt_ctx = ssh_ctx;
+
+ ret = sss_names_init_from_args(ssh_ctx,
+ SSS_DEFAULT_RE,
+ "%1$s@%2$s", &ssh_ctx->snctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing regex data\n");
+ goto fail;
+ }
+
+ /* Get responder options */
+
+ /* Get ssh_hash_known_hosts option */
+ ret = confdb_get_bool(ssh_ctx->rctx->cdb,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_HASH_KNOWN_HOSTS,
+ CONFDB_DEFAULT_SSH_HASH_KNOWN_HOSTS,
+ &ssh_ctx->hash_known_hosts);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ /* Get ssh_known_hosts_timeout option */
+ ret = confdb_get_int(ssh_ctx->rctx->cdb,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_KNOWN_HOSTS_TIMEOUT,
+ CONFDB_DEFAULT_SSH_KNOWN_HOSTS_TIMEOUT,
+ &ssh_ctx->known_hosts_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_string(ssh_ctx->rctx->cdb, ssh_ctx,
+ CONFDB_SSH_CONF_ENTRY, CONFDB_SSH_CA_DB,
+ CONFDB_DEFAULT_SSH_CA_DB, &ssh_ctx->ca_db);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading CA DB from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_bool(ssh_ctx->rctx->cdb, CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SSH_USE_CERT_KEYS,
+ CONFDB_DEFAULT_SSH_USE_CERT_KEYS,
+ &ssh_ctx->use_cert_keys);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,"Error reading option "
+ CONFDB_SSH_USE_CERT_KEYS
+ "from confdb (%d) [%s]\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ ret = confdb_get_string_as_list(ssh_ctx->rctx->cdb, ssh_ctx,
+ CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SSH_USE_CERT_RULES,
+ &ssh_ctx->cert_rules);
+ if (ret == ENOENT) {
+ ssh_ctx->cert_rules = NULL;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Error reading " CONFDB_SSH_USE_CERT_RULES
+ " from confdb (%d) [%s].\n", ret,
+ sss_strerror(ret));
+ goto fail;
+ }
+
+ ret = schedule_get_domains_task(rctx, rctx->ev, rctx, NULL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "schedule_get_domains_tasks failed.\n");
+ goto fail;
+ }
+
+ /* The responder is initialized. Now tell it to the monitor. */
+ ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_SSH,
+ SSS_SSH_SBUS_SERVICE_NAME,
+ SSS_SSH_SBUS_SERVICE_VERSION,
+ MT_SVC_SERVICE,
+ &rctx->last_request_time, &rctx->mon_conn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "fatal error setting up message bus\n");
+ goto fail;
+ }
+
+ ret = sss_resp_register_service_iface(rctx);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "SSH Initialization complete\n");
+
+ return EOK;
+
+fail:
+ talloc_free(rctx);
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ char *opt_logger = NULL;
+ struct main_context *main_ctx;
+ int ret;
+ uid_t uid = 0;
+ gid_t gid = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ SSSD_LOGGER_OPTS
+ SSSD_SERVER_OPTS(uid, gid)
+ SSSD_RESPONDER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ umask(DFL_RSP_UMASK);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ /* set up things like debug, signals, daemonization, etc. */
+ debug_log_file = "sssd_ssh";
+ DEBUG_INIT(debug_level, opt_logger);
+
+ /* server_setup() might switch to an unprivileged user, so the permissions
+ * for p11_child.log have to be fixed first. We might call p11_child to
+ * validate certificates. */
+ ret = chown_debug_file("p11_child", uid, gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot chown the p11_child debug file, "
+ "debugging might not work!\n");
+ }
+
+ ret = server_setup("ssh", true, 0, uid, gid,
+ CONFDB_SSH_CONF_ENTRY, &main_ctx, true);
+ if (ret != EOK) {
+ return 2;
+ }
+
+ ret = die_if_parent_died();
+ if (ret != EOK) {
+ /* This is not fatal, don't return */
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit "
+ "when parent process does\n");
+ }
+
+ ret = ssh_process_init(main_ctx,
+ main_ctx->event_ctx,
+ main_ctx->confdb_ctx);
+ if (ret != EOK) {
+ return 3;
+ }
+
+ /* loop on main */
+ server_loop(main_ctx);
+
+ return 0;
+}