diff options
Diffstat (limited to 'ssh.c')
-rw-r--r-- | ssh.c | 128 |
1 files changed, 88 insertions, 40 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.527 2020/04/10 00:52:07 dtucker Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.536 2020/09/21 07:29:09 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -16,7 +16,7 @@ | |||
16 | * Copyright (c) 1999 Niels Provos. All rights reserved. | 16 | * Copyright (c) 1999 Niels Provos. All rights reserved. |
17 | * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. | 17 | * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. |
18 | * | 18 | * |
19 | * Modified to work with SSL by Niels Provos <provos@citi.umich.edu> | 19 | * Modified to work with SSLeay by Niels Provos <provos@citi.umich.edu> |
20 | * in Canada (German citizen). | 20 | * in Canada (German citizen). |
21 | * | 21 | * |
22 | * Redistribution and use in source and binary forms, with or without | 22 | * Redistribution and use in source and binary forms, with or without |
@@ -137,11 +137,11 @@ int stdin_null_flag = 0; | |||
137 | 137 | ||
138 | /* | 138 | /* |
139 | * Flag indicating that the current process should be backgrounded and | 139 | * Flag indicating that the current process should be backgrounded and |
140 | * a new slave launched in the foreground for ControlPersist. | 140 | * a new mux-client launched in the foreground for ControlPersist. |
141 | */ | 141 | */ |
142 | int need_controlpersist_detach = 0; | 142 | int need_controlpersist_detach = 0; |
143 | 143 | ||
144 | /* Copies of flags for ControlPersist foreground slave */ | 144 | /* Copies of flags for ControlPersist foreground mux-client */ |
145 | int ostdin_null_flag, ono_shell_flag, otty_flag, orequest_tty; | 145 | int ostdin_null_flag, ono_shell_flag, otty_flag, orequest_tty; |
146 | 146 | ||
147 | /* | 147 | /* |
@@ -176,6 +176,7 @@ char *forward_agent_sock_path = NULL; | |||
176 | /* Various strings used to to percent_expand() arguments */ | 176 | /* Various strings used to to percent_expand() arguments */ |
177 | static char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; | 177 | static char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; |
178 | static char uidstr[32], *host_arg, *conn_hash_hex; | 178 | static char uidstr[32], *host_arg, *conn_hash_hex; |
179 | static const char *keyalias; | ||
179 | 180 | ||
180 | /* socket address the host resolves to */ | 181 | /* socket address the host resolves to */ |
181 | struct sockaddr_storage hostaddr; | 182 | struct sockaddr_storage hostaddr; |
@@ -235,6 +236,7 @@ tilde_expand_paths(char **paths, u_int num_paths) | |||
235 | "C", conn_hash_hex, \ | 236 | "C", conn_hash_hex, \ |
236 | "L", shorthost, \ | 237 | "L", shorthost, \ |
237 | "i", uidstr, \ | 238 | "i", uidstr, \ |
239 | "k", keyalias, \ | ||
238 | "l", thishost, \ | 240 | "l", thishost, \ |
239 | "n", host_arg, \ | 241 | "n", host_arg, \ |
240 | "p", portstr | 242 | "p", portstr |
@@ -260,6 +262,31 @@ default_client_percent_expand(const char *str, const char *homedir, | |||
260 | } | 262 | } |
261 | 263 | ||
262 | /* | 264 | /* |
265 | * Expands the set of percent_expand options used by the majority of keywords | ||
266 | * AND perform environment variable substitution. | ||
267 | * Caller must free returned string. | ||
268 | */ | ||
269 | static char * | ||
270 | default_client_percent_dollar_expand(const char *str, const char *homedir, | ||
271 | const char *remhost, const char *remuser, const char *locuser) | ||
272 | { | ||
273 | char *ret; | ||
274 | |||
275 | ret = percent_dollar_expand(str, | ||
276 | /* values from statics above */ | ||
277 | DEFAULT_CLIENT_PERCENT_EXPAND_ARGS, | ||
278 | /* values from arguments */ | ||
279 | "d", homedir, | ||
280 | "h", remhost, | ||
281 | "r", remuser, | ||
282 | "u", locuser, | ||
283 | (char *)NULL); | ||
284 | if (ret == NULL) | ||
285 | fatal("invalid environment variable expansion"); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | /* | ||
263 | * Attempt to resolve a host name / port to a set of addresses and | 290 | * Attempt to resolve a host name / port to a set of addresses and |
264 | * optionally return any CNAMEs encountered along the way. | 291 | * optionally return any CNAMEs encountered along the way. |
265 | * Returns NULL on failure. | 292 | * Returns NULL on failure. |
@@ -620,7 +647,7 @@ main(int ac, char **av) | |||
620 | struct ssh *ssh = NULL; | 647 | struct ssh *ssh = NULL; |
621 | int i, r, opt, exit_status, use_syslog, direct, timeout_ms; | 648 | int i, r, opt, exit_status, use_syslog, direct, timeout_ms; |
622 | int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; | 649 | int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; |
623 | char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile; | 650 | char *p, *cp, *line, *argv0, *logfile; |
624 | char cname[NI_MAXHOST]; | 651 | char cname[NI_MAXHOST]; |
625 | struct stat st; | 652 | struct stat st; |
626 | struct passwd *pw; | 653 | struct passwd *pw; |
@@ -629,6 +656,7 @@ main(int ac, char **av) | |||
629 | struct Forward fwd; | 656 | struct Forward fwd; |
630 | struct addrinfo *addrs = NULL; | 657 | struct addrinfo *addrs = NULL; |
631 | size_t n, len; | 658 | size_t n, len; |
659 | u_int j; | ||
632 | 660 | ||
633 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 661 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
634 | sanitise_stdfd(); | 662 | sanitise_stdfd(); |
@@ -1230,19 +1258,25 @@ main(int ac, char **av) | |||
1230 | /* Fill configuration defaults. */ | 1258 | /* Fill configuration defaults. */ |
1231 | fill_default_options(&options); | 1259 | fill_default_options(&options); |
1232 | 1260 | ||
1261 | if (options.user == NULL) | ||
1262 | options.user = xstrdup(pw->pw_name); | ||
1263 | |||
1233 | /* | 1264 | /* |
1234 | * If ProxyJump option specified, then construct a ProxyCommand now. | 1265 | * If ProxyJump option specified, then construct a ProxyCommand now. |
1235 | */ | 1266 | */ |
1236 | if (options.jump_host != NULL) { | 1267 | if (options.jump_host != NULL) { |
1237 | char port_s[8]; | 1268 | char port_s[8]; |
1238 | const char *sshbin = argv0; | 1269 | const char *jumpuser = options.jump_user, *sshbin = argv0; |
1239 | int port = options.port, jumpport = options.jump_port; | 1270 | int port = options.port, jumpport = options.jump_port; |
1240 | 1271 | ||
1241 | if (port <= 0) | 1272 | if (port <= 0) |
1242 | port = default_ssh_port(); | 1273 | port = default_ssh_port(); |
1243 | if (jumpport <= 0) | 1274 | if (jumpport <= 0) |
1244 | jumpport = default_ssh_port(); | 1275 | jumpport = default_ssh_port(); |
1245 | if (strcmp(options.jump_host, host) == 0 && port == jumpport) | 1276 | if (jumpuser == NULL) |
1277 | jumpuser = options.user; | ||
1278 | if (strcmp(options.jump_host, host) == 0 && port == jumpport && | ||
1279 | strcmp(options.user, jumpuser) == 0) | ||
1246 | fatal("jumphost loop via %s", options.jump_host); | 1280 | fatal("jumphost loop via %s", options.jump_host); |
1247 | 1281 | ||
1248 | /* | 1282 | /* |
@@ -1345,9 +1379,6 @@ main(int ac, char **av) | |||
1345 | tty_flag = 0; | 1379 | tty_flag = 0; |
1346 | } | 1380 | } |
1347 | 1381 | ||
1348 | if (options.user == NULL) | ||
1349 | options.user = xstrdup(pw->pw_name); | ||
1350 | |||
1351 | /* Set up strings used to percent_expand() arguments */ | 1382 | /* Set up strings used to percent_expand() arguments */ |
1352 | if (gethostname(thishost, sizeof(thishost)) == -1) | 1383 | if (gethostname(thishost, sizeof(thishost)) == -1) |
1353 | fatal("gethostname: %s", strerror(errno)); | 1384 | fatal("gethostname: %s", strerror(errno)); |
@@ -1356,6 +1387,7 @@ main(int ac, char **av) | |||
1356 | snprintf(portstr, sizeof(portstr), "%d", options.port); | 1387 | snprintf(portstr, sizeof(portstr), "%d", options.port); |
1357 | snprintf(uidstr, sizeof(uidstr), "%llu", | 1388 | snprintf(uidstr, sizeof(uidstr), "%llu", |
1358 | (unsigned long long)pw->pw_uid); | 1389 | (unsigned long long)pw->pw_uid); |
1390 | keyalias = options.host_key_alias ? options.host_key_alias : host_arg; | ||
1359 | 1391 | ||
1360 | conn_hash_hex = ssh_connection_hash(thishost, host, portstr, | 1392 | conn_hash_hex = ssh_connection_hash(thishost, host, portstr, |
1361 | options.user); | 1393 | options.user); |
@@ -1380,14 +1412,14 @@ main(int ac, char **av) | |||
1380 | if (options.control_path != NULL) { | 1412 | if (options.control_path != NULL) { |
1381 | cp = tilde_expand_filename(options.control_path, getuid()); | 1413 | cp = tilde_expand_filename(options.control_path, getuid()); |
1382 | free(options.control_path); | 1414 | free(options.control_path); |
1383 | options.control_path = default_client_percent_expand(cp, | 1415 | options.control_path = default_client_percent_dollar_expand(cp, |
1384 | pw->pw_dir, host, options.user, pw->pw_name); | 1416 | pw->pw_dir, host, options.user, pw->pw_name); |
1385 | free(cp); | 1417 | free(cp); |
1386 | } | 1418 | } |
1387 | 1419 | ||
1388 | if (options.identity_agent != NULL) { | 1420 | if (options.identity_agent != NULL) { |
1389 | p = tilde_expand_filename(options.identity_agent, getuid()); | 1421 | p = tilde_expand_filename(options.identity_agent, getuid()); |
1390 | cp = default_client_percent_expand(p, | 1422 | cp = default_client_percent_dollar_expand(p, |
1391 | pw->pw_dir, host, options.user, pw->pw_name); | 1423 | pw->pw_dir, host, options.user, pw->pw_name); |
1392 | free(p); | 1424 | free(p); |
1393 | free(options.identity_agent); | 1425 | free(options.identity_agent); |
@@ -1397,13 +1429,28 @@ main(int ac, char **av) | |||
1397 | if (options.forward_agent_sock_path != NULL) { | 1429 | if (options.forward_agent_sock_path != NULL) { |
1398 | p = tilde_expand_filename(options.forward_agent_sock_path, | 1430 | p = tilde_expand_filename(options.forward_agent_sock_path, |
1399 | getuid()); | 1431 | getuid()); |
1400 | cp = default_client_percent_expand(p, | 1432 | cp = default_client_percent_dollar_expand(p, |
1401 | pw->pw_dir, host, options.user, pw->pw_name); | 1433 | pw->pw_dir, host, options.user, pw->pw_name); |
1402 | free(p); | 1434 | free(p); |
1403 | free(options.forward_agent_sock_path); | 1435 | free(options.forward_agent_sock_path); |
1404 | options.forward_agent_sock_path = cp; | 1436 | options.forward_agent_sock_path = cp; |
1405 | } | 1437 | } |
1406 | 1438 | ||
1439 | for (j = 0; j < options.num_user_hostfiles; j++) { | ||
1440 | if (options.user_hostfiles[j] != NULL) { | ||
1441 | cp = tilde_expand_filename(options.user_hostfiles[j], | ||
1442 | getuid()); | ||
1443 | p = default_client_percent_dollar_expand(cp, | ||
1444 | pw->pw_dir, host, options.user, pw->pw_name); | ||
1445 | if (strcmp(options.user_hostfiles[j], p) != 0) | ||
1446 | debug3("expanded UserKnownHostsFile '%s' -> " | ||
1447 | "'%s'", options.user_hostfiles[j], p); | ||
1448 | free(options.user_hostfiles[j]); | ||
1449 | free(cp); | ||
1450 | options.user_hostfiles[j] = p; | ||
1451 | } | ||
1452 | } | ||
1453 | |||
1407 | for (i = 0; i < options.num_local_forwards; i++) { | 1454 | for (i = 0; i < options.num_local_forwards; i++) { |
1408 | if (options.local_forwards[i].listen_path != NULL) { | 1455 | if (options.local_forwards[i].listen_path != NULL) { |
1409 | cp = options.local_forwards[i].listen_path; | 1456 | cp = options.local_forwards[i].listen_path; |
@@ -1549,22 +1596,6 @@ main(int ac, char **av) | |||
1549 | } | 1596 | } |
1550 | } | 1597 | } |
1551 | 1598 | ||
1552 | /* Create ~/.ssh * directory if it doesn't already exist. */ | ||
1553 | if (config == NULL) { | ||
1554 | r = snprintf(buf, sizeof buf, "%s%s%s", pw->pw_dir, | ||
1555 | strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); | ||
1556 | if (r > 0 && (size_t)r < sizeof(buf) && stat(buf, &st) == -1) { | ||
1557 | #ifdef WITH_SELINUX | ||
1558 | ssh_selinux_setfscreatecon(buf); | ||
1559 | #endif | ||
1560 | if (mkdir(buf, 0700) < 0) | ||
1561 | error("Could not create directory '%.200s'.", | ||
1562 | buf); | ||
1563 | #ifdef WITH_SELINUX | ||
1564 | ssh_selinux_setfscreatecon(NULL); | ||
1565 | #endif | ||
1566 | } | ||
1567 | } | ||
1568 | /* load options.identity_files */ | 1599 | /* load options.identity_files */ |
1569 | load_public_identity_files(pw); | 1600 | load_public_identity_files(pw); |
1570 | 1601 | ||
@@ -1575,7 +1606,8 @@ main(int ac, char **av) | |||
1575 | unsetenv(SSH_AUTHSOCKET_ENV_NAME); | 1606 | unsetenv(SSH_AUTHSOCKET_ENV_NAME); |
1576 | } else { | 1607 | } else { |
1577 | cp = options.identity_agent; | 1608 | cp = options.identity_agent; |
1578 | if (cp[0] == '$') { | 1609 | /* legacy (limited) format */ |
1610 | if (cp[0] == '$' && cp[1] != '{') { | ||
1579 | if (!valid_env_name(cp + 1)) { | 1611 | if (!valid_env_name(cp + 1)) { |
1580 | fatal("Invalid IdentityAgent " | 1612 | fatal("Invalid IdentityAgent " |
1581 | "environment variable name %s", cp); | 1613 | "environment variable name %s", cp); |
@@ -1683,7 +1715,7 @@ control_persist_detach(void) | |||
1683 | /* Child: master process continues mainloop */ | 1715 | /* Child: master process continues mainloop */ |
1684 | break; | 1716 | break; |
1685 | default: | 1717 | default: |
1686 | /* Parent: set up mux slave to connect to backgrounded master */ | 1718 | /* Parent: set up mux client to connect to backgrounded master */ |
1687 | debug2("%s: background process is %ld", __func__, (long)pid); | 1719 | debug2("%s: background process is %ld", __func__, (long)pid); |
1688 | stdin_null_flag = ostdin_null_flag; | 1720 | stdin_null_flag = ostdin_null_flag; |
1689 | options.request_tty = orequest_tty; | 1721 | options.request_tty = orequest_tty; |
@@ -1715,12 +1747,26 @@ control_persist_detach(void) | |||
1715 | static void | 1747 | static void |
1716 | fork_postauth(void) | 1748 | fork_postauth(void) |
1717 | { | 1749 | { |
1750 | int devnull, keep_stderr; | ||
1751 | |||
1718 | if (need_controlpersist_detach) | 1752 | if (need_controlpersist_detach) |
1719 | control_persist_detach(); | 1753 | control_persist_detach(); |
1720 | debug("forking to background"); | 1754 | debug("forking to background"); |
1721 | fork_after_authentication_flag = 0; | 1755 | fork_after_authentication_flag = 0; |
1722 | if (daemon(1, 1) == -1) | 1756 | if (daemon(1, 1) == -1) |
1723 | fatal("daemon() failed: %.200s", strerror(errno)); | 1757 | fatal("daemon() failed: %.200s", strerror(errno)); |
1758 | if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) | ||
1759 | error("%s: open %s: %s", __func__, | ||
1760 | _PATH_DEVNULL, strerror(errno)); | ||
1761 | else { | ||
1762 | keep_stderr = log_is_on_stderr() && debug_flag; | ||
1763 | if (dup2(devnull, STDIN_FILENO) == -1 || | ||
1764 | dup2(devnull, STDOUT_FILENO) == -1 || | ||
1765 | (!keep_stderr && dup2(devnull, STDOUT_FILENO) == -1)) | ||
1766 | fatal("%s: dup2() stdio failed", __func__); | ||
1767 | if (devnull > STDERR_FILENO) | ||
1768 | close(devnull); | ||
1769 | } | ||
1724 | } | 1770 | } |
1725 | 1771 | ||
1726 | static void | 1772 | static void |
@@ -2062,9 +2108,9 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) | |||
2062 | /* | 2108 | /* |
2063 | * If we are in control persist mode and have a working mux listen | 2109 | * If we are in control persist mode and have a working mux listen |
2064 | * socket, then prepare to background ourselves and have a foreground | 2110 | * socket, then prepare to background ourselves and have a foreground |
2065 | * client attach as a control slave. | 2111 | * client attach as a control client. |
2066 | * NB. we must save copies of the flags that we override for | 2112 | * NB. we must save copies of the flags that we override for |
2067 | * the backgrounding, since we defer attachment of the slave until | 2113 | * the backgrounding, since we defer attachment of the client until |
2068 | * after the connection is fully established (in particular, | 2114 | * after the connection is fully established (in particular, |
2069 | * async rfwd replies have been received for ExitOnForwardFailure). | 2115 | * async rfwd replies have been received for ExitOnForwardFailure). |
2070 | */ | 2116 | */ |
@@ -2119,13 +2165,15 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) | |||
2119 | * as it may want to write to stdout. | 2165 | * as it may want to write to stdout. |
2120 | */ | 2166 | */ |
2121 | if (!need_controlpersist_detach) { | 2167 | if (!need_controlpersist_detach) { |
2122 | if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) | 2168 | if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) { |
2123 | error("%s: open %s: %s", __func__, | 2169 | error("%s: open %s: %s", __func__, |
2124 | _PATH_DEVNULL, strerror(errno)); | 2170 | _PATH_DEVNULL, strerror(errno)); |
2125 | if (dup2(devnull, STDOUT_FILENO) == -1) | 2171 | } else { |
2126 | fatal("%s: dup2() stdout failed", __func__); | 2172 | if (dup2(devnull, STDOUT_FILENO) == -1) |
2127 | if (devnull > STDERR_FILENO) | 2173 | fatal("%s: dup2() stdout failed", __func__); |
2128 | close(devnull); | 2174 | if (devnull > STDERR_FILENO) |
2175 | close(devnull); | ||
2176 | } | ||
2129 | } | 2177 | } |
2130 | 2178 | ||
2131 | /* | 2179 | /* |
@@ -2203,7 +2251,7 @@ load_public_identity_files(struct passwd *pw) | |||
2203 | continue; | 2251 | continue; |
2204 | } | 2252 | } |
2205 | cp = tilde_expand_filename(options.identity_files[i], getuid()); | 2253 | cp = tilde_expand_filename(options.identity_files[i], getuid()); |
2206 | filename = default_client_percent_expand(cp, | 2254 | filename = default_client_percent_dollar_expand(cp, |
2207 | pw->pw_dir, host, options.user, pw->pw_name); | 2255 | pw->pw_dir, host, options.user, pw->pw_name); |
2208 | free(cp); | 2256 | free(cp); |
2209 | check_load(sshkey_load_public(filename, &public, NULL), | 2257 | check_load(sshkey_load_public(filename, &public, NULL), |
@@ -2253,7 +2301,7 @@ load_public_identity_files(struct passwd *pw) | |||
2253 | for (i = 0; i < options.num_certificate_files; i++) { | 2301 | for (i = 0; i < options.num_certificate_files; i++) { |
2254 | cp = tilde_expand_filename(options.certificate_files[i], | 2302 | cp = tilde_expand_filename(options.certificate_files[i], |
2255 | getuid()); | 2303 | getuid()); |
2256 | filename = default_client_percent_expand(cp, | 2304 | filename = default_client_percent_dollar_expand(cp, |
2257 | pw->pw_dir, host, options.user, pw->pw_name); | 2305 | pw->pw_dir, host, options.user, pw->pw_name); |
2258 | free(cp); | 2306 | free(cp); |
2259 | 2307 | ||