From 74aa0bc6779af38018a03fd2cf4419fe85917904 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 07:31:45 +0200 Subject: Adding upstream version 2.9.4. Signed-off-by: Daniel Baumann --- src/responder/ssh/ssh_reply.c | 429 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 src/responder/ssh/ssh_reply.c (limited to 'src/responder/ssh/ssh_reply.c') 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 + + 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 . +*/ + +#include "config.h" + +#include +#include + +#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; +} -- cgit v1.2.3