diff options
author | 2020-12-22 00:15:22 +0000 | |
---|---|---|
committer | 2020-12-22 00:15:22 +0000 | |
commit | ed5bd12992d0177d950b623e5a19fbf338754995 (patch) | |
tree | f84403506e4d0487f23e82cd37adff246194e55d /usr.bin/ssh/sshconnect.c | |
parent | move subprocess() from auth.c to misc.c (diff) | |
download | wireguard-openbsd-ed5bd12992d0177d950b623e5a19fbf338754995.tar.xz wireguard-openbsd-ed5bd12992d0177d950b623e5a19fbf338754995.zip |
add a ssh_config KnownHostsCommand that allows the client to obtain
known_hosts data from a command in addition to the usual files.
The command accepts bunch of %-expansions, including details of the
connection and the offered server host key. Note that the command may
be invoked up to three times per connection (see the manpage for
details).
ok markus@
Diffstat (limited to 'usr.bin/ssh/sshconnect.c')
-rw-r--r-- | usr.bin/ssh/sshconnect.c | 110 |
1 files changed, 104 insertions, 6 deletions
diff --git a/usr.bin/ssh/sshconnect.c b/usr.bin/ssh/sshconnect.c index 934fff2c5a9..65f2773870b 100644 --- a/usr.bin/ssh/sshconnect.c +++ b/usr.bin/ssh/sshconnect.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect.c,v 1.348 2020/12/20 23:40:19 djm Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.349 2020/12/22 00:15:23 djm Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -827,6 +827,84 @@ other_hostkeys_message(const char *host, const char *ip, return ret; } +void +load_hostkeys_command(struct hostkeys *hostkeys, const char *command_template, + const char *invocation, const struct ssh_conn_info *cinfo, + const struct sshkey *host_key, const char *hostfile_hostname) +{ + int r, i, ac = 0; + char *key_fp = NULL, *keytext = NULL, *tmp; + char *command = NULL, *tag = NULL, **av = NULL; + FILE *f = NULL; + pid_t pid; + void (*osigchld)(int); + + xasprintf(&tag, "KnownHostsCommand-%s", invocation); + + if (host_key != NULL) { + if ((key_fp = sshkey_fingerprint(host_key, + options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) + fatal_f("sshkey_fingerprint failed"); + if ((r = sshkey_to_base64(host_key, &keytext)) != 0) + fatal_fr(r, "sshkey_to_base64 failed"); + } + /* + * NB. all returns later this function should go via "out" to + * ensure the original SIGCHLD handler is restored properly. + */ + osigchld = ssh_signal(SIGCHLD, SIG_DFL); + + /* Turn the command into an argument vector */ + if (argv_split(command_template, &ac, &av) != 0) { + error("%s \"%s\" contains invalid quotes", tag, + command_template); + goto out; + } + if (ac == 0) { + error("%s \"%s\" yielded no arguments", tag, + command_template); + goto out; + } + for (i = 1; i < ac; i++) { + tmp = percent_dollar_expand(av[i], + DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), + "H", hostfile_hostname, + "I", invocation, + "t", host_key == NULL ? "NONE" : sshkey_ssh_name(host_key), + "f", key_fp == NULL ? "NONE" : key_fp, + "K", keytext == NULL ? "NONE" : keytext, + (char *)NULL); + if (tmp == NULL) + fatal_f("percent_expand failed"); + free(av[i]); + av[i] = tmp; + } + /* Prepare a printable command for logs, etc. */ + command = argv_assemble(ac, av); + + if ((pid = subprocess(tag, command, ac, av, &f, + SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_UNSAFE_PATH| + SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL)) == 0) + goto out; + + load_hostkeys_file(hostkeys, hostfile_hostname, tag, f, 1); + + if (exited_cleanly(pid, tag, command, 0) != 0) + fatal("KnownHostsCommand failed"); + + out: + if (f != NULL) + fclose(f); + ssh_signal(SIGCHLD, osigchld); + for (i = 0; i < ac; i++) + free(av[i]); + free(av); + free(tag); + free(command); + free(key_fp); + free(keytext); +} + /* * check whether the supplied host key is valid, return -1 if the key * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. @@ -839,7 +917,8 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, struct sockaddr *hostaddr, u_short port, struct sshkey *host_key, int readonly, int clobber_port, char **user_hostfiles, u_int num_user_hostfiles, - char **system_hostfiles, u_int num_system_hostfiles) + char **system_hostfiles, u_int num_system_hostfiles, + const char *hostfile_command) { HostStatus host_status = -1, ip_status = -1; struct sshkey *raw_key = NULL; @@ -891,6 +970,10 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, load_hostkeys(host_hostkeys, host, user_hostfiles[i], 0); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(host_hostkeys, host, system_hostfiles[i], 0); + if (hostfile_command != NULL && !clobber_port) { + load_hostkeys_command(host_hostkeys, hostfile_command, + "HOSTNAME", cinfo, host_key, host); + } ip_hostkeys = NULL; if (!want_cert && options.check_host_ip) { @@ -899,6 +982,10 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, load_hostkeys(ip_hostkeys, ip, user_hostfiles[i], 0); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(ip_hostkeys, ip, system_hostfiles[i], 0); + if (hostfile_command != NULL && !clobber_port) { + load_hostkeys_command(ip_hostkeys, hostfile_command, + "ADDRESS", cinfo, host_key, ip); + } } retry: @@ -913,8 +1000,12 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, host_status = check_key_in_hostkeys(host_hostkeys, host_key, &host_found); - /* If no host files were specified, then don't try to touch them */ - if (!readonly && num_user_hostfiles == 0) + /* + * If there are no hostfiles, or if the hostkey was found via + * KnownHostsCommand, then don't try to touch the disk. + */ + if (!readonly && (num_user_hostfiles == 0 || + (host_found != NULL && host_found->note != 0))) readonly = RDONLY; /* @@ -955,6 +1046,11 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, debug3_f("host key found in GlobalKnownHostsFile; " "disabling UpdateHostkeys"); } + if (options.update_hostkeys != 0 && host_found->note) { + options.update_hostkeys = 0; + debug3_f("host key found via KnownHostsCommand; " + "disabling UpdateHostkeys"); + } if (options.check_host_ip && ip_status == HOST_NEW) { if (readonly || want_cert) logit("%s host key for IP address " @@ -990,7 +1086,8 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo, if (check_host_key(hostname, cinfo, hostaddr, 0, host_key, ROQUIET, 1, user_hostfiles, num_user_hostfiles, - system_hostfiles, num_system_hostfiles) == 0) { + system_hostfiles, num_system_hostfiles, + hostfile_command) == 0) { debug("found matching key w/out port"); break; } @@ -1400,7 +1497,8 @@ verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key, } r = check_host_key(host, cinfo, hostaddr, options.port, host_key, RDRW, 0, options.user_hostfiles, options.num_user_hostfiles, - options.system_hostfiles, options.num_system_hostfiles); + options.system_hostfiles, options.num_system_hostfiles, + options.known_hosts_command); out: sshkey_free(plain); |