summaryrefslogtreecommitdiffstats
path: root/src/responder/ssh/ssh_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/responder/ssh/ssh_cmd.c')
-rw-r--r--src/responder/ssh/ssh_cmd.c409
1 files changed, 409 insertions, 0 deletions
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;
+}