summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am15
-rw-r--r--src/confdb/confdb.h3
-rw-r--r--src/monitor/monitor.c3
-rw-r--r--src/providers/data_provider_be.c5
-rw-r--r--src/providers/dp_backend.h1
-rw-r--r--src/responder/ssh/sshsrv.c193
-rw-r--r--src/responder/ssh/sshsrv_cmd.c631
-rw-r--r--src/responder/ssh/sshsrv_private.h48
-rw-r--r--src/sss_client/sss_cli.h4
9 files changed, 902 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index edf583cc0..644adcbec 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 bc8f6f237..095cfd51e 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 264256f04..190417c35 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 7dd4b2794..a48ba107e 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 2923a8d7d..fc79d45b3 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 000000000..e5bb5b09c
--- /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 000000000..1e30bf3ec
--- /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 000000000..26ed6b92e
--- /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 591361f43..491af9adb 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.