diff options
author | Jan Cholasta <jcholast@redhat.com> | 2012-02-03 22:48:28 +0100 |
---|---|---|
committer | Jakub Hrozek <jhrozek@redhat.com> | 2012-02-07 00:26:57 +0100 |
commit | e7311aec8d691e5427317442387af1bc8fff3742 (patch) | |
tree | 3d565438942b685ff5e6bc64e2c15ebc7a799623 | |
parent | bd3cf7d6414171fcd605d9f831965be107e411d7 (diff) | |
download | sssd-e7311aec8d691e5427317442387af1bc8fff3742.tar.gz sssd-e7311aec8d691e5427317442387af1bc8fff3742.tar.bz2 sssd-e7311aec8d691e5427317442387af1bc8fff3742.zip |
SSH: Responder
-rw-r--r-- | Makefile.am | 15 | ||||
-rw-r--r-- | src/confdb/confdb.h | 3 | ||||
-rw-r--r-- | src/monitor/monitor.c | 3 | ||||
-rw-r--r-- | src/providers/data_provider_be.c | 5 | ||||
-rw-r--r-- | src/providers/dp_backend.h | 1 | ||||
-rw-r--r-- | src/responder/ssh/sshsrv.c | 193 | ||||
-rw-r--r-- | src/responder/ssh/sshsrv_cmd.c | 631 | ||||
-rw-r--r-- | src/responder/ssh/sshsrv_private.h | 48 | ||||
-rw-r--r-- | src/sss_client/sss_cli.h | 4 |
9 files changed, 902 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index edf583cc..644adcbe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -86,6 +86,9 @@ endif if BUILD_AUTOFS sssdlibexec_PROGRAMS += sssd_autofs endif +if BUILD_SSH +sssdlibexec_PROGRAMS += sssd_ssh +endif if HAVE_CHECK non_interactive_check_based_tests = \ @@ -223,6 +226,7 @@ AM_CPPFLAGS = \ -DSSS_PAM_PRIV_SOCKET_NAME=\"$(pipepath)/private/pam\" \ -DSSS_SUDO_SOCKET_NAME=\"$(pipepath)/sudo\" \ -DSSS_AUTOFS_SOCKET_NAME=\"$(pipepath)/autofs\" \ + -DSSS_SSH_SOCKET_NAME=\"$(pipepath)/ssh\" \ -DLOCALEDIR=\"$(localedir)\" EXTRA_DIST = build/config.rpath @@ -329,6 +333,7 @@ dist_noinst_HEADERS = \ src/responder/common/negcache.h \ src/responder/sudo/sudosrv_private.h \ src/responder/autofs/autofs_private.h \ + src/responder/ssh/sshsrv_private.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_private.h \ @@ -513,6 +518,16 @@ sssd_autofs_LDADD = \ libsss_util.la endif +if BUILD_SSH +sssd_ssh_SOURCES = \ + src/responder/ssh/sshsrv.c \ + src/responder/ssh/sshsrv_cmd.c \ + $(SSSD_RESPONDER_OBJ) +sssd_ssh_LDADD = \ + $(SSSD_LIBS) \ + libsss_util.la +endif + sssd_be_SOURCES = \ src/providers/data_provider_be.c \ src/providers/data_provider_fo.c \ diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index bc8f6f23..095cfd51 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -102,6 +102,9 @@ #define CONFDB_AUTOFS_CONF_ENTRY "config/autofs" #define CONFDB_AUTOFS_MAP_NEG_TIMEOUT "autofs_negative_timeout" +/* SSH */ +#define CONFDB_SSH_CONF_ENTRY "config/ssh" + /* Data Provider */ #define CONFDB_DP_CONF_ENTRY "config/dp" diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c index 264256f0..190417c3 100644 --- a/src/monitor/monitor.c +++ b/src/monitor/monitor.c @@ -754,7 +754,8 @@ static int check_local_domain_unique(struct sss_domain_info *domains) static char *check_services(char **services) { - const char *known_services[] = { "nss", "pam", "sudo", "autofs", NULL }; + const char *known_services[] = { "nss", "pam", "sudo", "autofs", "ssh", + NULL }; int i; int ii; diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 7dd4b279..a48ba107 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -1260,6 +1260,9 @@ static int be_client_destructor(void *ctx) } else if (becli->bectx->autofs_cli == becli) { DEBUG(SSSDBG_TRACE_FUNC, ("Removed autofs client\n")); becli->bectx->autofs_cli = NULL; + } else if (becli->bectx->ssh_cli == becli) { + DEBUG(SSSDBG_TRACE_FUNC, ("Removed SSH client\n")); + becli->bectx->ssh_cli = NULL; } else { DEBUG(SSSDBG_CRIT_FAILURE, ("Unknown client removed ...\n")); } @@ -1312,6 +1315,8 @@ static int client_registration(DBusMessage *message, becli->bectx->sudo_cli = becli; } else if (strcasecmp(cli_name, "autofs") == 0) { becli->bectx->autofs_cli = becli; + } else if (strcasecmp(cli_name, "SSH") == 0) { + becli->bectx->ssh_cli = becli; } else { DEBUG(1, ("Unknown client! [%s]\n", cli_name)); } diff --git a/src/providers/dp_backend.h b/src/providers/dp_backend.h index 2923a8d7..fc79d45b 100644 --- a/src/providers/dp_backend.h +++ b/src/providers/dp_backend.h @@ -116,6 +116,7 @@ struct be_ctx { struct be_client *pam_cli; struct be_client *sudo_cli; struct be_client *autofs_cli; + struct be_client *ssh_cli; struct loaded_be loaded_be[BET_MAX]; struct bet_info bet_info[BET_MAX]; diff --git a/src/responder/ssh/sshsrv.c b/src/responder/ssh/sshsrv.c new file mode 100644 index 00000000..e5bb5b09 --- /dev/null +++ b/src/responder/ssh/sshsrv.c @@ -0,0 +1,193 @@ +/* + 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 "confdb/confdb.h" +#include "monitor/monitor_interfaces.h" +#include "responder/common/responder.h" +#include "responder/ssh/sshsrv_private.h" +#include "providers/data_provider.h" + +struct sbus_method monitor_ssh_methods[] = { + { MON_CLI_METHOD_PING, monitor_common_pong }, + { MON_CLI_METHOD_RES_INIT, monitor_common_res_init }, + { MON_CLI_METHOD_ROTATE, responder_logrotate }, + { NULL, NULL } +}; + +struct sbus_interface monitor_ssh_interface = { + MONITOR_INTERFACE, + MONITOR_PATH, + SBUS_DEFAULT_VTABLE, + monitor_ssh_methods, + NULL +}; + +static struct sbus_method ssh_dp_methods[] = { + { NULL, NULL } +}; + +struct sbus_interface ssh_dp_interface = { + DP_INTERFACE, + DP_PATH, + SBUS_DEFAULT_VTABLE, + ssh_dp_methods, + NULL +}; + +static void ssh_dp_reconnect_init(struct sbus_connection *conn, + int status, void *pvt) +{ + struct be_conn *be_conn = talloc_get_type(pvt, struct be_conn); + int ret; + + /* Did we reconnect successfully? */ + if (status == SBUS_RECONNECT_SUCCESS) { + DEBUG(SSSDBG_TRACE_FUNC, ("Reconnected to the Data Provider.\n")); + + /* Identify ourselves to the data provider */ + ret = dp_common_send_id(be_conn->conn, + DATA_PROVIDER_VERSION, + "SSH"); + /* all fine */ + if (ret == EOK) { + handle_requests_after_reconnect(); + return; + } + } + + /* Failed to reconnect */ + DEBUG(SSSDBG_FATAL_FAILURE, ("Could not reconnect to %s provider.\n", + be_conn->domain->name)); +} + +int ssh_process_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct confdb_ctx *cdb) +{ + struct sss_cmd_table *ssh_cmds; + struct ssh_ctx *ssh_ctx; + struct be_conn *iter; + int ret; + int max_retries; + + ssh_ctx = talloc_zero(mem_ctx, struct ssh_ctx); + if (!ssh_ctx) { + DEBUG(SSSDBG_FATAL_FAILURE, ("fatal error initializing ssh_ctx\n")); + return ENOMEM; + } + + ssh_cmds = get_ssh_cmds(); + ret = sss_process_init(ssh_ctx, ev, cdb, + ssh_cmds, + SSS_SSH_SOCKET_NAME, NULL, + CONFDB_SSH_CONF_ENTRY, + SSS_SSH_SBUS_SERVICE_NAME, + SSS_SSH_SBUS_SERVICE_VERSION, + &monitor_ssh_interface, + "SSH", + &ssh_dp_interface, + &ssh_ctx->rctx); + if (ret != EOK) { + return ret; + } + ssh_ctx->rctx->pvt_ctx = ssh_ctx; + + /* Enable automatic reconnection to the Data Provider */ + ret = confdb_get_int(ssh_ctx->rctx->cdb, ssh_ctx->rctx, + CONFDB_SSH_CONF_ENTRY, + CONFDB_SERVICE_RECON_RETRIES, + 3, &max_retries); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Failed to set up automatic reconnection\n")); + return ret; + } + + for (iter = ssh_ctx->rctx->be_conns; iter; iter = iter->next) { + sbus_reconnect_init(iter->conn, max_retries, + ssh_dp_reconnect_init, iter); + } + + DEBUG(SSSDBG_TRACE_FUNC, ("SSH Initialization complete\n")); + + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + struct main_context *main_ctx; + int ret; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + POPT_TABLEEND + }; + + /* Set debug level to invalid value so we can deside if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + 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); + + CONVERT_AND_SET_DEBUG_LEVEL(debug_level); + + /* set up things like debug, signals, daemonization, etc... */ + debug_log_file = "sssd_ssh"; + + ret = server_setup("sssd[ssh]", 0, CONFDB_SSH_CONF_ENTRY, &main_ctx); + 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; +} diff --git a/src/responder/ssh/sshsrv_cmd.c b/src/responder/ssh/sshsrv_cmd.c new file mode 100644 index 00000000..1e30bf3e --- /dev/null +++ b/src/responder/ssh/sshsrv_cmd.c @@ -0,0 +1,631 @@ +/* + 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 <talloc.h> + +#include "util/util.h" +#include "util/crypto/sss_crypto.h" +#include "db/sysdb.h" +#include "db/sysdb_ssh.h" +#include "providers/data_provider.h" +#include "responder/common/responder.h" +#include "responder/common/responder_packet.h" +#include "responder/ssh/sshsrv_private.h" + +static errno_t +ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx); + +static errno_t +ssh_cmd_done(struct ssh_cmd_ctx *cmd_ctx, + errno_t ret); + +static errno_t +ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx); + +int +sss_ssh_cmd_get_user_pubkeys(struct cli_ctx *cctx) +{ + errno_t ret; + struct ssh_cmd_ctx *cmd_ctx; + + cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx); + if (!cmd_ctx) { + return ENOMEM; + } + cmd_ctx->cctx = cctx; + cmd_ctx->type = SSS_DP_USER; + + ret = ssh_cmd_parse_request(cmd_ctx); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requesting SSH user public keys for [%s] from [%s]\n", + cmd_ctx->name, cmd_ctx->domname ? cmd_ctx->domname : "<ALL>")); + + if (cmd_ctx->domname) { + cmd_ctx->domain = responder_get_domain(cctx->rctx->domains, + cmd_ctx->domname); + if (!cmd_ctx->domain) { + ret = ENOENT; + goto done; + } + } else { + cmd_ctx->domain = cctx->rctx->domains; + cmd_ctx->check_next = true; + } + + ret = ssh_user_pubkeys_search(cmd_ctx); + +done: + return ssh_cmd_done(cmd_ctx, ret); +} + +static errno_t +ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx); + +static int +sss_ssh_cmd_get_host_pubkeys(struct cli_ctx *cctx) +{ + struct ssh_cmd_ctx *cmd_ctx; + errno_t ret; + + cmd_ctx = talloc_zero(cctx, struct ssh_cmd_ctx); + if (!cmd_ctx) { + return ENOMEM; + } + cmd_ctx->cctx = cctx; + cmd_ctx->type = SSS_DP_HOST; + + ret = ssh_cmd_parse_request(cmd_ctx); + if (ret != EOK) { + goto done; + } + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requesting SSH host public keys for [%s] from [%s]\n", + cmd_ctx->name, cmd_ctx->domname ? cmd_ctx->domname : "<ALL>")); + + if (cmd_ctx->domname) { + cmd_ctx->domain = responder_get_domain(cctx->rctx->domains, + cmd_ctx->domname); + if (!cmd_ctx->domain) { + ret = ENOENT; + goto done; + } + } else { + cmd_ctx->domain = cctx->rctx->domains; + cmd_ctx->check_next = true; + } + + ret = ssh_host_pubkeys_search(cmd_ctx); + +done: + return ssh_cmd_done(cmd_ctx, ret); +} + +static void +ssh_dp_send_req_done(struct tevent_req *req) +{ + struct dp_callback_ctx *cb_ctx = + tevent_req_callback_data(req, struct dp_callback_ctx); + + errno_t ret; + dbus_uint16_t err_maj; + dbus_uint32_t err_min; + char *err_msg; + + ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req, + &err_maj, &err_min, + &err_msg); + talloc_zfree(req); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Fatal error, killing connection!\n")); + talloc_free(cb_ctx->cctx); + return; + } + + cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr); +} + +static errno_t +ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx); +static void +ssh_user_pubkeys_search_dp_callback(uint16_t err_maj, + uint32_t err_min, + const char *err_msg, + void *ptr); + +static errno_t +ssh_user_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx) +{ + struct tevent_req *req; + struct dp_callback_ctx *cb_ctx; + + /* if it is a domainless search, skip domains that require fully + * qualified names instead */ + while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) { + cmd_ctx->domain = cmd_ctx->domain->next; + } + + if (!cmd_ctx->domain) { + DEBUG(SSSDBG_OP_FAILURE, + ("No matching domain found for [%s], fail!\n", cmd_ctx->name)); + return ENOENT; + } + + /* refresh the user's cache entry */ + if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) { + req = sss_dp_get_account_send(cmd_ctx, cmd_ctx->cctx->rctx, + cmd_ctx->domain, false, SSS_DP_USER, + cmd_ctx->name, 0, NULL); + if (!req) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending data provider request\n")); + return ENOMEM; + } + + cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx); + if (!cb_ctx) { + talloc_zfree(req); + return ENOMEM; + } + + cb_ctx->callback = ssh_user_pubkeys_search_dp_callback; + cb_ctx->ptr = cmd_ctx; + cb_ctx->cctx = cmd_ctx->cctx; + cb_ctx->mem_ctx = cmd_ctx; + + tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx); + + /* tell caller we are in an async call */ + return EAGAIN; + } + + return ssh_user_pubkeys_search_next(cmd_ctx); +} + +static errno_t +ssh_user_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx) +{ + errno_t ret; + struct cli_ctx *cctx = cmd_ctx->cctx; + struct sysdb_ctx *sysdb; + const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL }; + struct ldb_result *res; + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requesting SSH user public keys for [%s@%s]\n", + cmd_ctx->name, cmd_ctx->domain->name)); + + ret = sysdb_get_ctx_from_list(cctx->rctx->db_list, cmd_ctx->domain, &sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Fatal: Sysdb CTX not found for this domain!\n")); + return EFAULT; + } + + ret = sysdb_get_user_attr(cmd_ctx, sysdb, + cmd_ctx->name, attrs, &res); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + cmd_ctx->results = res->msgs; + cmd_ctx->results_len = res->count; + + if (cmd_ctx->results_len > 1) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("User search by name (%s) returned > 1 results!\n", + cmd_ctx->name)); + return ENOENT; + } + + if (cmd_ctx->results_len == 0) { + /* if a multidomain search, try with next */ + if (cmd_ctx->check_next) { + cmd_ctx->domain = cmd_ctx->domain->next; + return ssh_user_pubkeys_search(cmd_ctx); + } + + DEBUG(SSSDBG_OP_FAILURE, + ("No attributes for user [%s] found.\n", cmd_ctx->name)); + + return ENOENT; + } + + /* one result found */ + return EOK; +} + +static void +ssh_user_pubkeys_search_dp_callback(uint16_t err_maj, + uint32_t err_min, + const char *err_msg, + void *ptr) +{ + struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx); + errno_t ret; + + if (err_maj) { + DEBUG(SSSDBG_OP_FAILURE, + ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = ssh_user_pubkeys_search_next(cmd_ctx); + ssh_cmd_done(cmd_ctx, ret); +} + +static errno_t +ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx); +static void +ssh_host_pubkeys_search_dp_callback(uint16_t err_maj, + uint32_t err_min, + const char *err_msg, + void *ptr); + +static errno_t +ssh_host_pubkeys_search(struct ssh_cmd_ctx *cmd_ctx) +{ + struct tevent_req *req; + struct dp_callback_ctx *cb_ctx; + + /* if it is a domainless search, skip domains that require fully + * qualified names instead */ + while (cmd_ctx->domain && cmd_ctx->check_next && cmd_ctx->domain->fqnames) { + cmd_ctx->domain = cmd_ctx->domain->next; + } + + if (!cmd_ctx->domain) { + DEBUG(SSSDBG_OP_FAILURE, + ("No matching domain found for [%s], fail!\n", cmd_ctx->name)); + return ENOENT; + } + + /* refresh the host's cache entry */ + if (NEED_CHECK_PROVIDER(cmd_ctx->domain->provider)) { + req = sss_dp_get_account_send(cmd_ctx, cmd_ctx->cctx->rctx, + cmd_ctx->domain, false, SSS_DP_HOST, + cmd_ctx->name, 0, NULL); + if (!req) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Out of memory sending data provider request\n")); + return ENOMEM; + } + + cb_ctx = talloc_zero(cmd_ctx, struct dp_callback_ctx); + if (!cb_ctx) { + talloc_zfree(req); + return ENOMEM; + } + + cb_ctx->callback = ssh_host_pubkeys_search_dp_callback; + cb_ctx->ptr = cmd_ctx; + cb_ctx->cctx = cmd_ctx->cctx; + cb_ctx->mem_ctx = cmd_ctx; + + tevent_req_set_callback(req, ssh_dp_send_req_done, cb_ctx); + + /* tell caller we are in an async call */ + return EAGAIN; + } + + return ssh_host_pubkeys_search_next(cmd_ctx); +} + +static errno_t +ssh_host_pubkeys_search_next(struct ssh_cmd_ctx *cmd_ctx) +{ + errno_t ret; + struct cli_ctx *cctx = cmd_ctx->cctx; + struct sysdb_ctx *sysdb; + const char *attrs[] = { SYSDB_NAME, SYSDB_SSH_PUBKEY, NULL }; + + DEBUG(SSSDBG_TRACE_FUNC, + ("Requesting SSH host public keys for [%s@%s]\n", + cmd_ctx->name, cmd_ctx->domain->name)); + + ret = sysdb_get_ctx_from_list(cctx->rctx->db_list, cmd_ctx->domain, &sysdb); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Fatal: Sysdb CTX not found for this domain!\n")); + return EFAULT; + } + + ret = sysdb_search_ssh_hosts(cmd_ctx, sysdb, + cmd_ctx->name, attrs, + &cmd_ctx->results, &cmd_ctx->results_len); + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Failed to make request to our cache!\n")); + return EIO; + } + + if (cmd_ctx->results_len > 1) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("Host search by name (%s) returned > 1 results!\n", + cmd_ctx->name)); + return ENOENT; + } + + if (cmd_ctx->results_len == 0) { + /* if a multidomain search, try with next */ + if (cmd_ctx->check_next) { + cmd_ctx->domain = cmd_ctx->domain->next; + return ssh_host_pubkeys_search(cmd_ctx); + } + + DEBUG(SSSDBG_OP_FAILURE, + ("No attributes for host [%s] found.\n", cmd_ctx->name)); + + return ENOENT; + } + + /* one result found */ + return EOK; +} + +static void +ssh_host_pubkeys_search_dp_callback(uint16_t err_maj, + uint32_t err_min, + const char *err_msg, + void *ptr) +{ + struct ssh_cmd_ctx *cmd_ctx = talloc_get_type(ptr, struct ssh_cmd_ctx); + errno_t ret; + + if (err_maj) { + DEBUG(SSSDBG_OP_FAILURE, + ("Unable to get information from Data Provider\n" + "Error: %u, %u, %s\n", + (unsigned int)err_maj, (unsigned int)err_min, err_msg)); + } + + ret = ssh_host_pubkeys_search_next(cmd_ctx); + ssh_cmd_done(cmd_ctx, ret); +} + +static errno_t +ssh_cmd_parse_request(struct ssh_cmd_ctx *cmd_ctx) +{ + struct cli_ctx *cctx = cmd_ctx->cctx; + errno_t ret; + uint8_t *body; + size_t body_len; + size_t c = 0; + uint32_t reserved; + uint32_t name_len; + char *name; + + sss_packet_get_body(cctx->creq->in, &body, &body_len); + + SAFEALIGN_COPY_UINT32_CHECK(&reserved, body+c, body_len, &c); + if (reserved != 0) { + return EINVAL; + } + + SAFEALIGN_COPY_UINT32_CHECK(&name_len, body+c, body_len, &c); + if (name_len == 0) { + return EINVAL; + } + + name = (char *)(body+c); + if (strnlen(name, name_len) != name_len-1) { + return EINVAL; + } + c += name_len; + + ret = sss_parse_name(cmd_ctx, cctx->rctx->names, name, + &cmd_ctx->domname, &cmd_ctx->name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, ("Invalid name received [%s]\n", name)); + return ENOENT; + } + + return EOK; +} + +static errno_t +ssh_cmd_build_reply(struct ssh_cmd_ctx *cmd_ctx) +{ + struct cli_ctx *cctx = cmd_ctx->cctx; + errno_t ret; + uint8_t *body; + size_t body_len; + size_t c = 0; + size_t i; + unsigned int j; + struct ldb_message_element *el; + uint32_t count = 0; + const char *name; + char *fqname; + uint32_t fqname_len; + uint8_t *key; + size_t key_len; + + ret = sss_packet_new(cctx->creq, 0, + sss_packet_get_cmd(cctx->creq->in), + &cctx->creq->out); + if (ret != EOK) { + return ret; + } + + /* count number of results */ + for (i = 0; i < cmd_ctx->results_len; i++) { + el = ldb_msg_find_element(cmd_ctx->results[i], SYSDB_SSH_PUBKEY); + if (!el) { + continue; + } + + count += el->num_values; + } + + ret = sss_packet_grow(cctx->creq->out, 2*sizeof(uint32_t)); + if (ret != EOK) { + return ret; + } + sss_packet_get_body(cctx->creq->out, &body, &body_len); + + SAFEALIGN_SET_UINT32(body+c, count, &c); + SAFEALIGN_SET_UINT32(body+c, 0, &c); + + for (i = 0; i < cmd_ctx->results_len; i++) { + name = ldb_msg_find_attr_as_string(cmd_ctx->results[i], + SYSDB_NAME, NULL); + if (!name) { + DEBUG(SSSDBG_OP_FAILURE, + ("Got unnamed result for [%s@%s]\n", + cmd_ctx->name, cmd_ctx->domain->name)); + return ENOENT; + } + + fqname = talloc_asprintf(cmd_ctx, "%s@%s", + name, cmd_ctx->domain->name); + if (!fqname) { + return ENOMEM; + } + + fqname_len = strlen(fqname)+1; + + el = ldb_msg_find_element(cmd_ctx->results[i], SYSDB_SSH_PUBKEY); + if (!el) { + /* this object has no SSH public keys */ + continue; + } + + for (j = 0; j < el->num_values; j++) { + key = sss_base64_decode(cmd_ctx, + (const char *)el->values[j].data, + &key_len); + if (!key) { + return ENOMEM; + } + + ret = sss_packet_grow(cctx->creq->out, + 3*sizeof(uint32_t) + key_len + fqname_len); + if (ret != EOK) { + talloc_free(key); + return ret; + } + sss_packet_get_body(cctx->creq->out, &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); + + talloc_free(key); + count++; + } + } + + return EOK; +} + +static errno_t +ssh_cmd_send_error(struct ssh_cmd_ctx *cmd_ctx, + errno_t error) +{ + struct cli_ctx *cctx = cmd_ctx->cctx; + errno_t ret; + + ret = sss_cmd_send_error(cctx, error); + if (ret != EOK) { + return ret; + } + + sss_cmd_done(cctx, cmd_ctx); + + return EOK; +} + +static errno_t +ssh_cmd_send_reply(struct ssh_cmd_ctx *cmd_ctx) +{ + struct cli_ctx *cctx = cmd_ctx->cctx; + errno_t ret; + + /* create response packet */ + ret = ssh_cmd_build_reply(cmd_ctx); + if (ret != EOK) { + return ret; + } + + sss_packet_set_error(cctx->creq->out, EOK); + sss_cmd_done(cctx, cmd_ctx); + + return EOK; +} + +static errno_t +ssh_cmd_done(struct ssh_cmd_ctx *cmd_ctx, + errno_t ret) +{ + switch (ret) { + case EOK: + ret = ssh_cmd_send_reply(cmd_ctx); + break; + + case EAGAIN: + return EOK; + + case EFAULT: + break; + + default: + ret = ssh_cmd_send_error(cmd_ctx, ret); + break; + } + + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Fatal error, killing connection!\n")); + talloc_free(cmd_ctx->cctx); + return EFAULT; + } + + return EOK; +} + +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, sss_ssh_cmd_get_user_pubkeys}, + {SSS_SSH_GET_HOST_PUBKEYS, sss_ssh_cmd_get_host_pubkeys}, + {SSS_CLI_NULL, NULL} + }; + + return ssh_cmds; +} diff --git a/src/responder/ssh/sshsrv_private.h b/src/responder/ssh/sshsrv_private.h new file mode 100644 index 00000000..26ed6b92 --- /dev/null +++ b/src/responder/ssh/sshsrv_private.h @@ -0,0 +1,48 @@ +/* + 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" + +#define SSS_SSH_SBUS_SERVICE_VERSION 0x0001 +#define SSS_SSH_SBUS_SERVICE_NAME "ssh" + +struct ssh_ctx { + struct resp_ctx *rctx; +}; + +struct ssh_cmd_ctx { + struct cli_ctx *cctx; + enum sss_dp_acct_type type; + char *name; + char *domname; + + struct sss_domain_info *domain; + bool check_next; + + struct ldb_message **results; + size_t results_len; +}; + +struct sss_cmd_table *get_ssh_cmds(void); + +#endif /* _SSHSRV_PRIVATE_H_ */ diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 591361f4..491af9ad 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -170,6 +170,10 @@ enum sss_cli_command { SSS_AUTOFS_GETAUTOMNTBYNAME = 0x00D3, SSS_AUTOFS_ENDAUTOMNTENT = 0x00D4, +/* SSH */ + SSS_SSH_GET_USER_PUBKEYS = 0x00E1, + SSS_SSH_GET_HOST_PUBKEYS = 0x00E2, + /* PAM related calls */ SSS_PAM_AUTHENTICATE = 0x00F1, /**< see pam_sm_authenticate(3) for * details. |