diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/responder/ssh/ssh_known_hosts.c | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/responder/ssh/ssh_known_hosts.c')
-rw-r--r-- | src/responder/ssh/ssh_known_hosts.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/src/responder/ssh/ssh_known_hosts.c b/src/responder/ssh/ssh_known_hosts.c new file mode 100644 index 0000000..2be240b --- /dev/null +++ b/src/responder/ssh/ssh_known_hosts.c @@ -0,0 +1,333 @@ +/* + 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 "config.h" + +#include <talloc.h> + +#include "util/util.h" +#include "util/crypto/sss_crypto.h" +#include "util/sss_ssh.h" +#include "db/sysdb.h" +#include "db/sysdb_ssh.h" +#include "responder/ssh/ssh_private.h" + +static char * +ssh_host_pubkeys_format_known_host_plain(TALLOC_CTX *mem_ctx, + struct sss_ssh_ent *ent) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + char *name, *pubkey; + char *result = NULL; + size_t i; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return NULL; + } + + name = talloc_strdup(tmp_ctx, ent->name); + if (!name) { + goto done; + } + + for (i = 0; i < ent->num_aliases; i++) { + name = talloc_asprintf_append(name, ",%s", ent->aliases[i]); + if (!name) { + goto done; + } + } + + result = talloc_strdup(tmp_ctx, ""); + if (!result) { + goto done; + } + + for (i = 0; i < ent->num_pubkeys; i++) { + ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); + if (ret != EOK) { + result = NULL; + goto done; + } + + result = talloc_asprintf_append(result, "%s %s\n", name, pubkey); + if (!result) { + goto done; + } + + talloc_free(pubkey); + } + + talloc_steal(mem_ctx, result); + +done: + talloc_free(tmp_ctx); + + return result; +} + +static char * +ssh_host_pubkeys_format_known_host_hashed(TALLOC_CTX *mem_ctx, + struct sss_ssh_ent *ent) +{ + TALLOC_CTX *tmp_ctx; + errno_t ret; + char *name, *pubkey, *saltstr, *hashstr, *result; + unsigned char salt[SSS_SHA1_LENGTH], hash[SSS_SHA1_LENGTH]; + size_t i, j; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return NULL; + } + + result = talloc_strdup(tmp_ctx, ""); + if (!result) { + goto done; + } + + for (i = 0; i < ent->num_pubkeys; i++) { + ret = sss_ssh_format_pubkey(tmp_ctx, &ent->pubkeys[i], &pubkey); + if (ret != EOK) { + result = NULL; + goto done; + } + + for (j = 0; j <= ent->num_aliases; j++) { + name = (j == 0 ? ent->name : ent->aliases[j-1]); + + ret = sss_generate_csprng_buffer((uint8_t *)salt, SSS_SHA1_LENGTH); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_generate_csprng_buffer() failed (%d)\n", ret); + result = NULL; + goto done; + } + + ret = sss_hmac_sha1(salt, SSS_SHA1_LENGTH, + (unsigned char *)name, strlen(name), + hash); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "sss_hmac_sha1() failed (%d): %s\n", + ret, strerror(ret)); + result = NULL; + goto done; + } + + saltstr = sss_base64_encode(tmp_ctx, salt, SSS_SHA1_LENGTH); + if (!saltstr) { + result = NULL; + goto done; + } + + hashstr = sss_base64_encode(tmp_ctx, hash, SSS_SHA1_LENGTH); + if (!hashstr) { + result = NULL; + goto done; + } + + result = talloc_asprintf_append(result, "|1|%s|%s %s\n", + saltstr, hashstr, pubkey); + if (!result) { + goto done; + } + + talloc_free(saltstr); + talloc_free(hashstr); + } + + talloc_free(pubkey); + } + + talloc_steal(mem_ctx, result); + +done: + talloc_free(tmp_ctx); + + return result; +} + +static errno_t +ssh_write_known_hosts(struct sss_domain_info *domains, + bool hash_known_hosts, + time_t now, + int fd) +{ + TALLOC_CTX *tmp_ctx; + struct sss_domain_info *dom; + struct ldb_message **hosts; + struct sysdb_ctx *sysdb; + struct sss_ssh_ent *ent; + char *entstr; + size_t num_hosts; + size_t i; + ssize_t wret; + errno_t ret; + + static const char *attrs[] = { + SYSDB_NAME, + SYSDB_NAME_ALIAS, + SYSDB_SSH_PUBKEY, + NULL + }; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + for (dom = domains; dom != NULL; dom = get_next_domain(dom, false)) { + sysdb = dom->sysdb; + if (sysdb == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Fatal: Sysdb CTX not found for this domain!\n"); + ret = EFAULT; + goto done; + } + + ret = sysdb_get_ssh_known_hosts(tmp_ctx, dom, now, attrs, + &hosts, &num_hosts); + if (ret == ENOENT) { + continue; + } else if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Host search failed for domain " + "%s [%d]: %s\n", dom->name, ret, sss_strerror(ret)); + continue; + } + + for (i = 0; i < num_hosts; i++) { + ret = sss_ssh_make_ent(tmp_ctx, hosts[i], &ent); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get SSH host public keys\n"); + continue; + } + + if (hash_known_hosts) { + entstr = ssh_host_pubkeys_format_known_host_hashed(ent, ent); + } else { + entstr = ssh_host_pubkeys_format_known_host_plain(ent, ent); + } + + if (entstr == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to format known_hosts data " + "for [%s]\n", ent->name); + continue; + } + + wret = sss_atomic_write_s(fd, entstr, strlen(entstr)); + if (wret == -1) { + ret = errno; + goto done; + } + + talloc_free(ent); + } + + talloc_free(hosts); + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + return ret; +} + +errno_t +ssh_update_known_hosts_file(struct sss_domain_info *domains, + struct sss_domain_info *domain, + const char *name, + bool hash_known_hosts, + int known_hosts_timeout) +{ + TALLOC_CTX *tmp_ctx; + char *filename; + errno_t ret; + time_t now; + int fd = -1; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n"); + return ENOMEM; + } + + now = time(NULL); + + /* Update host's expiration time. */ + if (domain != NULL) { + ret = sysdb_update_ssh_known_host_expire(domain, name, now, + known_hosts_timeout); + if (ret != EOK && ret != ENOENT) { + goto done; + } + } + + /* Create temporary known hosts file. */ + filename = talloc_strdup(tmp_ctx, SSS_SSH_KNOWN_HOSTS_TEMP_TMPL); + if (filename == NULL) { + ret = ENOMEM; + goto done; + } + + fd = sss_unique_file_ex(tmp_ctx, filename, 0133, &ret); + if (fd == -1) { + filename = NULL; + goto done; + } + + /* Write contents. */ + ret = ssh_write_known_hosts(domains, hash_known_hosts, now, fd); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write known hosts file " + "[%d]: %s\n", ret, sss_strerror(ret)); + goto done; + } + + + /* Rename to SSH known hosts file. */ + ret = fchmod(fd, 0644); + if (ret == -1) { + ret = errno; + goto done; + } + + ret = rename(filename, SSS_SSH_KNOWN_HOSTS_PATH); + if (ret == -1) { + ret = errno; + goto done; + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + + if (fd != -1) { + close(fd); + } + + return ret; +} |