aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssh/ssh.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssh/ssh.c')
-rw-r--r--crypto/openssh/ssh.c224
1 files changed, 163 insertions, 61 deletions
diff --git a/crypto/openssh/ssh.c b/crypto/openssh/ssh.c
index 860ad31954a0..044557ad39f9 100644
--- a/crypto/openssh/ssh.c
+++ b/crypto/openssh/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.574 2022/03/30 04:33:09 djm Exp $ */
+/* $OpenBSD: ssh.c,v 1.600 2024/01/11 01:45:36 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -130,10 +130,11 @@ int tty_flag = 0;
* Flag indicating that the current process should be backgrounded and
* a new mux-client launched in the foreground for ControlPersist.
*/
-int need_controlpersist_detach = 0;
+static int need_controlpersist_detach = 0;
/* Copies of flags for ControlPersist foreground mux-client */
-int ostdin_null_flag, osession_type, otty_flag, orequest_tty;
+static int ostdin_null_flag, osession_type, otty_flag, orequest_tty;
+static int ofork_after_authentication;
/*
* General data structure for command line options and options configurable
@@ -179,13 +180,14 @@ static void
usage(void)
{
fprintf(stderr,
-"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]\n"
-" [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]\n"
-" [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]\n"
-" [-i identity_file] [-J [user@]host[:port]] [-L address]\n"
-" [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
-" [-Q query_option] [-R address] [-S ctl_path] [-W host:port]\n"
-" [-w local_tun[:remote_tun]] destination [command [argument ...]]\n"
+"usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address]\n"
+" [-c cipher_spec] [-D [bind_address:]port] [-E log_file]\n"
+" [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]\n"
+" [-J destination] [-L address] [-l login_name] [-m mac_spec]\n"
+" [-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address]\n"
+" [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n"
+" destination [command [argument ...]]\n"
+" ssh [-Q query_option]\n"
);
exit(255);
}
@@ -251,6 +253,7 @@ static struct addrinfo *
resolve_host(const char *name, int port, int logerr, char *cname, size_t clen)
{
char strport[NI_MAXSERV];
+ const char *errstr = NULL;
struct addrinfo hints, *res;
int gaierr;
LogLevel loglevel = SYSLOG_LEVEL_DEBUG1;
@@ -276,7 +279,10 @@ resolve_host(const char *name, int port, int logerr, char *cname, size_t clen)
return NULL;
}
if (cname != NULL && res->ai_canonname != NULL) {
- if (strlcpy(cname, res->ai_canonname, clen) >= clen) {
+ if (!valid_domain(res->ai_canonname, 0, &errstr)) {
+ error("ignoring bad CNAME \"%s\" for host \"%s\": %s",
+ res->ai_canonname, name, errstr);
+ } else if (strlcpy(cname, res->ai_canonname, clen) >= clen) {
error_f("host \"%s\" cname \"%s\" too long (max %lu)",
name, res->ai_canonname, (u_long)clen);
if (clen > 0)
@@ -457,7 +463,7 @@ resolve_canonicalize(char **hostp, int port)
* a proxy unless the user specifically requests so.
*/
direct = option_clear_or_none(options.proxy_command) &&
- options.jump_host == NULL;
+ option_clear_or_none(options.jump_host);
if (!direct &&
options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS)
return NULL;
@@ -516,14 +522,22 @@ resolve_canonicalize(char **hostp, int port)
}
/*
- * Check the result of hostkey loading, ignoring some errors and
- * fatal()ing for others.
+ * Check the result of hostkey loading, ignoring some errors and either
+ * discarding the key or fatal()ing for others.
*/
static void
-check_load(int r, const char *path, const char *message)
+check_load(int r, struct sshkey **k, const char *path, const char *message)
{
switch (r) {
case 0:
+ /* Check RSA keys size and discard if undersized */
+ if (k != NULL && *k != NULL &&
+ (r = sshkey_check_rsa_length(*k,
+ options.required_rsa_size)) != 0) {
+ error_r(r, "load %s \"%s\"", message, path);
+ free(*k);
+ *k = NULL;
+ }
break;
case SSH_ERR_INTERNAL_ERROR:
case SSH_ERR_ALLOC_FAIL:
@@ -608,9 +622,45 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo)
free(cinfo->remuser);
free(cinfo->homedir);
free(cinfo->locuser);
+ free(cinfo->jmphost);
free(cinfo);
}
+static int
+valid_hostname(const char *s)
+{
+ size_t i;
+
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL ||
+ isspace((u_char)s[i]) || iscntrl((u_char)s[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+valid_ruser(const char *s)
+{
+ size_t i;
+
+ if (*s == '-')
+ return 0;
+ for (i = 0; s[i] != 0; i++) {
+ if (strchr("'`\";&<>|(){}", s[i]) != NULL)
+ return 0;
+ /* Disallow '-' after whitespace */
+ if (isspace((u_char)s[i]) && s[i + 1] == '-')
+ return 0;
+ /* Disallow \ in last position */
+ if (s[i] == '\\' && s[i + 1] == '\0')
+ return 0;
+ }
+ return 1;
+}
+
/*
* Main program for the ssh client.
*/
@@ -620,7 +670,7 @@ main(int ac, char **av)
struct ssh *ssh = NULL;
int i, r, opt, exit_status, use_syslog, direct, timeout_ms;
int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0;
- char *p, *cp, *line, *argv0, *logfile, *host_arg;
+ char *p, *cp, *line, *argv0, *logfile;
char cname[NI_MAXHOST], thishost[NI_MAXHOST];
struct stat st;
struct passwd *pw;
@@ -671,7 +721,7 @@ main(int ac, char **av)
* writable only by the owner, which is ok for all files for which we
* don't set the modes explicitly.
*/
- umask(022);
+ umask(022 | umask(077));
msetlocale();
@@ -696,7 +746,7 @@ main(int ac, char **av)
again:
while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
- "AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { /* HUZdhjruz */
+ "AB:CD:E:F:GI:J:KL:MNO:P:Q:R:S:TVw:W:XYy")) != -1) { /* HUZdhjruz */
switch (opt) {
case '1':
fatal("SSH protocol v.1 is no longer supported");
@@ -760,7 +810,9 @@ main(int ac, char **av)
else
fatal("Invalid multiplex command.");
break;
- case 'P': /* deprecated */
+ case 'P':
+ if (options.tag == NULL)
+ options.tag = xstrdup(optarg);
break;
case 'Q':
cp = NULL;
@@ -781,6 +833,9 @@ main(int ac, char **av)
cp = sshkey_alg_list(1, 0, 0, '\n');
else if (strcmp(optarg, "key-plain") == 0)
cp = sshkey_alg_list(0, 1, 0, '\n');
+ else if (strcmp(optarg, "key-ca-sign") == 0 ||
+ strcasecmp(optarg, "CASignatureAlgorithms") == 0)
+ cp = sshkey_alg_list(0, 1, 1, '\n');
else if (strcmp(optarg, "key-sig") == 0 ||
strcasecmp(optarg, "PubkeyAcceptedKeyTypes") == 0 || /* deprecated name */
strcasecmp(optarg, "PubkeyAcceptedAlgorithms") == 0 ||
@@ -872,16 +927,9 @@ main(int ac, char **av)
}
break;
case 'V':
- if (options.version_addendum != NULL &&
- *options.version_addendum != '\0')
- fprintf(stderr, "%s %s, %s\n", SSH_RELEASE,
- options.version_addendum,
- SSH_OPENSSL_VERSION);
- else
- fprintf(stderr, "%s, %s\n", SSH_RELEASE,
- SSH_OPENSSL_VERSION);
- if (opt == 'V')
- exit(0);
+ fprintf(stderr, "%s, %s\n",
+ SSH_RELEASE, SSH_OPENSSL_VERSION);
+ exit(0);
break;
case 'w':
if (options.tun_open == -1)
@@ -899,7 +947,9 @@ main(int ac, char **av)
if (muxclient_command != 0)
fatal("Cannot specify stdio forward with -O");
if (parse_forward(&fwd, optarg, 1, 0)) {
- options.stdio_forward_host = fwd.listen_host;
+ options.stdio_forward_host =
+ fwd.listen_port == PORT_STREAMLOCAL ?
+ fwd.listen_path : fwd.listen_host;
options.stdio_forward_port = fwd.listen_port;
free(fwd.connect_host);
} else {
@@ -1103,7 +1153,11 @@ main(int ac, char **av)
if (!host)
usage();
- host_arg = xstrdup(host);
+ if (!valid_hostname(host))
+ fatal("hostname contains invalid characters");
+ if (options.user != NULL && !valid_ruser(options.user))
+ fatal("remote username contains invalid characters");
+ options.host_arg = xstrdup(host);
/* Initialize the command to execute on remote host. */
if ((command = sshbuf_new()) == NULL)
@@ -1130,6 +1184,8 @@ main(int ac, char **av)
}
}
+ ssh_signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
+
/*
* Initialize "log" output. Since we are the client all output
* goes to stderr unless otherwise specified by -y or -E.
@@ -1146,11 +1202,10 @@ main(int ac, char **av)
!use_syslog);
if (debug_flag)
- /* version_addendum is always NULL at this point */
logit("%s, %s", SSH_RELEASE, SSH_OPENSSL_VERSION);
/* Parse the configuration files */
- process_config_files(host_arg, pw, 0, &want_final_pass);
+ process_config_files(options.host_arg, pw, 0, &want_final_pass);
if (want_final_pass)
debug("configuration requests final Match pass");
@@ -1193,7 +1248,7 @@ main(int ac, char **av)
* CanonicalizeHostname=always
*/
direct = option_clear_or_none(options.proxy_command) &&
- options.jump_host == NULL;
+ option_clear_or_none(options.jump_host);
if (addrs == NULL && config_has_permitted_cnames(&options) && (direct ||
options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) {
if ((addrs = resolve_host(host, options.port,
@@ -1219,7 +1274,7 @@ main(int ac, char **av)
debug("re-parsing configuration");
free(options.hostname);
options.hostname = xstrdup(host);
- process_config_files(host_arg, pw, 1, NULL);
+ process_config_files(options.host_arg, pw, 1, NULL);
/*
* Address resolution happens early with canonicalisation
* enabled and the port number may have changed since, so
@@ -1372,28 +1427,33 @@ main(int ac, char **av)
xasprintf(&cinfo->uidstr, "%llu",
(unsigned long long)pw->pw_uid);
cinfo->keyalias = xstrdup(options.host_key_alias ?
- options.host_key_alias : host_arg);
- cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost, host,
- cinfo->portstr, options.user);
- cinfo->host_arg = xstrdup(host_arg);
+ options.host_key_alias : options.host_arg);
+ cinfo->host_arg = xstrdup(options.host_arg);
cinfo->remhost = xstrdup(host);
cinfo->remuser = xstrdup(options.user);
cinfo->homedir = xstrdup(pw->pw_dir);
cinfo->locuser = xstrdup(pw->pw_name);
+ cinfo->jmphost = xstrdup(options.jump_host == NULL ?
+ "" : options.jump_host);
+ cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost,
+ cinfo->remhost, cinfo->portstr, cinfo->remuser, cinfo->jmphost);
/* Find canonic host name. */
- if (strchr(host, '.') == 0) {
+ if (strchr(host, '.') == NULL) {
struct addrinfo hints;
struct addrinfo *ai = NULL;
int errgai;
+
memset(&hints, 0, sizeof(hints));
hints.ai_family = options.address_family;
hints.ai_flags = AI_CANONNAME;
hints.ai_socktype = SOCK_STREAM;
errgai = getaddrinfo(host, NULL, &hints, &ai);
if (errgai == 0) {
- if (ai->ai_canonname != NULL)
+ if (ai->ai_canonname != NULL) {
+ free(host);
host = xstrdup(ai->ai_canonname);
+ }
freeaddrinfo(ai);
}
}
@@ -1431,6 +1491,14 @@ main(int ac, char **av)
options.identity_agent = cp;
}
+ if (options.revoked_host_keys != NULL) {
+ p = tilde_expand_filename(options.revoked_host_keys, getuid());
+ cp = default_client_percent_dollar_expand(p, cinfo);
+ free(p);
+ free(options.revoked_host_keys);
+ options.revoked_host_keys = cp;
+ }
+
if (options.forward_agent_sock_path != NULL) {
p = tilde_expand_filename(options.forward_agent_sock_path,
getuid());
@@ -1568,9 +1636,23 @@ main(int ac, char **av)
else
timeout_ms = options.connection_timeout * 1000;
+ /* Apply channels timeouts, if set */
+ channel_clear_timeouts(ssh);
+ for (j = 0; j < options.num_channel_timeouts; j++) {
+ debug3("applying channel timeout %s",
+ options.channel_timeouts[j]);
+ if (parse_pattern_interval(options.channel_timeouts[j],
+ &cp, &i) != 0) {
+ fatal_f("internal error: bad timeout %s",
+ options.channel_timeouts[j]);
+ }
+ channel_add_timeout(ssh, cp, i);
+ free(cp);
+ }
+
/* Open a connection to the remote host. */
- if (ssh_connect(ssh, host, host_arg, addrs, &hostaddr, options.port,
- options.connection_attempts,
+ if (ssh_connect(ssh, host, options.host_arg, addrs, &hostaddr,
+ options.port, options.connection_attempts,
&timeout_ms, options.tcp_keep_alive) != 0)
exit(255);
@@ -1591,40 +1673,54 @@ main(int ac, char **av)
sensitive_data.nkeys = 0;
sensitive_data.keys = NULL;
if (options.hostbased_authentication) {
+ int loaded = 0;
+
sensitive_data.nkeys = 10;
sensitive_data.keys = xcalloc(sensitive_data.nkeys,
- sizeof(struct sshkey));
+ sizeof(*sensitive_data.keys));
/* XXX check errors? */
#define L_PUBKEY(p,o) do { \
if ((o) >= sensitive_data.nkeys) \
fatal_f("pubkey out of array bounds"); \
check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \
- p, "pubkey"); \
- if (sensitive_data.keys[o] != NULL) \
+ &(sensitive_data.keys[o]), p, "pubkey"); \
+ if (sensitive_data.keys[o] != NULL) { \
debug2("hostbased key %d: %s key from \"%s\"", o, \
sshkey_ssh_name(sensitive_data.keys[o]), p); \
+ loaded++; \
+ } \
} while (0)
#define L_CERT(p,o) do { \
if ((o) >= sensitive_data.nkeys) \
fatal_f("cert out of array bounds"); \
- check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \
- if (sensitive_data.keys[o] != NULL) \
+ check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \
+ &(sensitive_data.keys[o]), p, "cert"); \
+ if (sensitive_data.keys[o] != NULL) { \
debug2("hostbased key %d: %s cert from \"%s\"", o, \
sshkey_ssh_name(sensitive_data.keys[o]), p); \
+ loaded++; \
+ } \
} while (0)
if (options.hostbased_authentication == 1) {
L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0);
L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1);
L_CERT(_PATH_HOST_RSA_KEY_FILE, 2);
+#ifdef WITH_DSA
L_CERT(_PATH_HOST_DSA_KEY_FILE, 3);
+#endif
L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4);
L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5);
L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6);
+#ifdef WITH_DSA
L_PUBKEY(_PATH_HOST_DSA_KEY_FILE, 7);
+#endif
L_CERT(_PATH_HOST_XMSS_KEY_FILE, 8);
L_PUBKEY(_PATH_HOST_XMSS_KEY_FILE, 9);
+ if (loaded == 0)
+ debug("HostbasedAuthentication enabled but no "
+ "local public host keys could be loaded.");
}
}
@@ -1676,7 +1772,6 @@ main(int ac, char **av)
options.num_system_hostfiles);
tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles);
- ssh_signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
ssh_signal(SIGCHLD, main_sigchld_handler);
/* Log into the remote system. Never returns if the login fails. */
@@ -1744,16 +1839,20 @@ control_persist_detach(void)
/* Child: master process continues mainloop */
break;
default:
- /* Parent: set up mux client to connect to backgrounded master */
+ /*
+ * Parent: set up mux client to connect to backgrounded
+ * master.
+ */
debug2_f("background process is %ld", (long)pid);
options.stdin_null = ostdin_null_flag;
options.request_tty = orequest_tty;
tty_flag = otty_flag;
+ options.fork_after_authentication = ofork_after_authentication;
options.session_type = osession_type;
close(muxserver_sock);
muxserver_sock = -1;
options.control_master = SSHCTL_MASTER_NO;
- muxclient(options.control_path);
+ (void)muxclient(options.control_path);
/* muxclient() doesn't return on success. */
fatal("Failed to connect to new control master");
}
@@ -1856,7 +1955,7 @@ ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt)
}
static void
-client_cleanup_stdio_fwd(struct ssh *ssh, int id, void *arg)
+client_cleanup_stdio_fwd(struct ssh *ssh, int id, int force, void *arg)
{
debug("stdio forwarding: done");
cleanup_exit(0);
@@ -2042,7 +2141,7 @@ ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg)
char *proto = NULL, *data = NULL;
if (!success)
- return; /* No need for error message, channels code sens one */
+ return; /* No need for error message, channels code sends one */
display = getenv("DISPLAY");
if (display == NULL && options.forward_x11)
@@ -2122,7 +2221,7 @@ ssh_session2_open(struct ssh *ssh)
static int
ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
{
- int r, id = -1;
+ int r, interactive, id = -1;
char *cp, *tun_fwd_ifname = NULL;
/* XXX should be pre-session */
@@ -2160,11 +2259,11 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
osession_type = options.session_type;
orequest_tty = options.request_tty;
otty_flag = tty_flag;
+ ofork_after_authentication = options.fork_after_authentication;
options.stdin_null = 1;
options.session_type = SESSION_TYPE_NONE;
tty_flag = 0;
- if (!options.fork_after_authentication &&
- (osession_type != SESSION_TYPE_NONE ||
+ if ((osession_type != SESSION_TYPE_NONE ||
options.stdio_forward_host != NULL))
need_controlpersist_detach = 1;
options.fork_after_authentication = 1;
@@ -2179,8 +2278,11 @@ ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo)
if (options.session_type != SESSION_TYPE_NONE)
id = ssh_session2_open(ssh);
else {
- ssh_packet_set_interactive(ssh,
- options.control_master == SSHCTL_MASTER_NO,
+ interactive = options.control_master == SSHCTL_MASTER_NO;
+ /* ControlPersist may have clobbered ControlMaster, so check */
+ if (need_controlpersist_detach)
+ interactive = otty_flag != 0;
+ ssh_packet_set_interactive(ssh, interactive,
options.ip_qos_interactive, options.ip_qos_bulk);
}
@@ -2288,7 +2390,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
filename = default_client_percent_dollar_expand(cp, cinfo);
free(cp);
check_load(sshkey_load_public(filename, &public, NULL),
- filename, "pubkey");
+ &public, filename, "pubkey");
debug("identity file %s type %d", filename,
public ? public->type : -1);
free(options.identity_files[i]);
@@ -2307,7 +2409,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
continue;
xasprintf(&cp, "%s-cert", filename);
check_load(sshkey_load_public(cp, &public, NULL),
- filename, "pubkey");
+ &public, filename, "pubkey");
debug("identity file %s type %d", cp,
public ? public->type : -1);
if (public == NULL) {
@@ -2338,7 +2440,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
free(cp);
check_load(sshkey_load_public(filename, &public, NULL),
- filename, "certificate");
+ &public, filename, "certificate");
debug("certificate file %s type %d", filename,
public ? public->type : -1);
free(options.certificate_files[i]);