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