]>
Commit | Line | Data |
---|---|---|
4e838120 | 1 | /* $OpenBSD: ssh.c,v 1.600 2024/01/11 01:45:36 djm Exp $ */ |
d4a8b7e3 | 2 | /* |
95def098 DM |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | |
5 | * All rights reserved | |
95def098 DM |
6 | * Ssh client program. This program can be used to log into a remote machine. |
7 | * The software supports strong authentication, encryption, and forwarding | |
8 | * of X11, TCP/IP, and authentication connections. | |
9 | * | |
e4340be5 DM |
10 | * As far as I am concerned, the code I have written for this software |
11 | * can be used freely for any purpose. Any derived versions of this | |
12 | * software must be clearly marked as such, and if the derived work is | |
13 | * incompatible with the protocol description in the RFC file, it must be | |
14 | * called by a name other than "ssh" or "Secure Shell". | |
15 | * | |
16 | * Copyright (c) 1999 Niels Provos. All rights reserved. | |
0a118da0 | 17 | * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. |
e4340be5 | 18 | * |
2f648cf2 | 19 | * Modified to work with SSLeay by Niels Provos <provos@citi.umich.edu> |
e4340be5 DM |
20 | * in Canada (German citizen). |
21 | * | |
22 | * Redistribution and use in source and binary forms, with or without | |
23 | * modification, are permitted provided that the following conditions | |
24 | * are met: | |
25 | * 1. Redistributions of source code must retain the above copyright | |
26 | * notice, this list of conditions and the following disclaimer. | |
27 | * 2. Redistributions in binary form must reproduce the above copyright | |
28 | * notice, this list of conditions and the following disclaimer in the | |
29 | * documentation and/or other materials provided with the distribution. | |
30 | * | |
31 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
32 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
33 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
34 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
35 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
36 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
37 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
38 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
39 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
40 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
95def098 | 41 | */ |
d4a8b7e3 DM |
42 | |
43 | #include "includes.h" | |
cd4223c2 | 44 | |
f17883e6 DM |
45 | #include <sys/types.h> |
46 | #ifdef HAVE_SYS_STAT_H | |
47 | # include <sys/stat.h> | |
48 | #endif | |
cd4223c2 | 49 | #include <sys/resource.h> |
17e91c0f | 50 | #include <sys/ioctl.h> |
e3b60b52 | 51 | #include <sys/socket.h> |
857b02e3 | 52 | #include <sys/wait.h> |
03e2003a | 53 | |
c7b06369 | 54 | #include <ctype.h> |
ba724050 | 55 | #include <errno.h> |
57cf6385 | 56 | #include <fcntl.h> |
b8fe89c4 | 57 | #include <netdb.h> |
6645e7a7 | 58 | #ifdef HAVE_PATHS_H |
03e2003a | 59 | #include <paths.h> |
6645e7a7 | 60 | #endif |
9f2abc47 | 61 | #include <pwd.h> |
6ff3cadd | 62 | #include <signal.h> |
ded319cc | 63 | #include <stdarg.h> |
2d00e63c | 64 | #include <stddef.h> |
a7a73ee3 | 65 | #include <stdio.h> |
e7a1e5cf | 66 | #include <stdlib.h> |
e3476ed0 | 67 | #include <string.h> |
189550f5 | 68 | #include <stdarg.h> |
e6b3b610 | 69 | #include <unistd.h> |
087266ec | 70 | #include <limits.h> |
65c6c6b5 | 71 | #include <locale.h> |
eba71bab | 72 | |
46aa3e0c DT |
73 | #include <netinet/in.h> |
74 | #include <arpa/inet.h> | |
75 | ||
1f0311c7 | 76 | #ifdef WITH_OPENSSL |
eba71bab | 77 | #include <openssl/evp.h> |
0bc1bd81 | 78 | #include <openssl/err.h> |
1f0311c7 | 79 | #endif |
bfaaf960 | 80 | #include "openbsd-compat/openssl-compat.h" |
b84886ba | 81 | #include "openbsd-compat/sys-queue.h" |
d4a8b7e3 | 82 | |
d7834353 | 83 | #include "xmalloc.h" |
d4a8b7e3 | 84 | #include "ssh.h" |
226cfa03 | 85 | #include "ssh2.h" |
b96c441e | 86 | #include "canohost.h" |
226cfa03 BL |
87 | #include "compat.h" |
88 | #include "cipher.h" | |
d4a8b7e3 | 89 | #include "packet.h" |
cecee2d6 | 90 | #include "sshbuf.h" |
c763767f | 91 | #include "channels.h" |
5467fbcb | 92 | #include "sshkey.h" |
994cf142 | 93 | #include "authfd.h" |
eba71bab | 94 | #include "authfile.h" |
226cfa03 | 95 | #include "pathnames.h" |
0e220dbf | 96 | #include "dispatch.h" |
bf555ba6 | 97 | #include "clientloop.h" |
226cfa03 | 98 | #include "log.h" |
7acefbbc | 99 | #include "misc.h" |
226cfa03 BL |
100 | #include "readconf.h" |
101 | #include "sshconnect.h" | |
06b33aa0 BL |
102 | #include "kex.h" |
103 | #include "mac.h" | |
06f2bd8b | 104 | #include "sshpty.h" |
46bc0754 | 105 | #include "match.h" |
0e220dbf | 106 | #include "msg.h" |
b757677d | 107 | #include "version.h" |
141efe49 | 108 | #include "ssherr.h" |
f9eca249 | 109 | #include "myproposal.h" |
dda78a03 | 110 | #include "utf8.h" |
d4a8b7e3 | 111 | |
7ea845e4 DM |
112 | #ifdef ENABLE_PKCS11 |
113 | #include "ssh-pkcs11.h" | |
bcc1808b | 114 | #endif |
c5b68001 | 115 | |
3f905872 | 116 | extern char *__progname; |
3f905872 | 117 | |
ea2c1a4d DM |
118 | /* Saves a copy of argv for setproctitle emulation */ |
119 | #ifndef HAVE_SETPROCTITLE | |
120 | static char **saved_av; | |
121 | #endif | |
122 | ||
d6173c0b | 123 | /* Flag indicating whether debug mode is on. May be set on the command line. */ |
d4a8b7e3 DM |
124 | int debug_flag = 0; |
125 | ||
21771e22 | 126 | /* Flag indicating whether a tty should be requested */ |
d4a8b7e3 DM |
127 | int tty_flag = 0; |
128 | ||
e11e1ea5 DM |
129 | /* |
130 | * Flag indicating that the current process should be backgrounded and | |
6d755706 | 131 | * a new mux-client launched in the foreground for ControlPersist. |
e11e1ea5 | 132 | */ |
2d34205d | 133 | static int need_controlpersist_detach = 0; |
e11e1ea5 | 134 | |
6d755706 | 135 | /* Copies of flags for ControlPersist foreground mux-client */ |
2d34205d | 136 | static int ostdin_null_flag, osession_type, otty_flag, orequest_tty; |
137 | static int ofork_after_authentication; | |
e11e1ea5 | 138 | |
5428f646 DM |
139 | /* |
140 | * General data structure for command line options and options configurable | |
141 | * in configuration files. See readconf.h. | |
142 | */ | |
d4a8b7e3 DM |
143 | Options options; |
144 | ||
14f31ab9 BL |
145 | /* optional user configfile */ |
146 | char *config = NULL; | |
147 | ||
5428f646 DM |
148 | /* |
149 | * Name of the host we are connecting to. This is the name given on the | |
7349149d | 150 | * command line, or the Hostname specified for the user-supplied name in a |
5428f646 DM |
151 | * configuration file. |
152 | */ | |
d4a8b7e3 DM |
153 | char *host; |
154 | ||
40be78f5 | 155 | /* |
156 | * A config can specify a path to forward, overriding SSH_AUTH_SOCK. If this is | |
157 | * not NULL, forward the socket at this path instead. | |
158 | */ | |
159 | char *forward_agent_sock_path = NULL; | |
160 | ||
d4a8b7e3 | 161 | /* socket address the host resolves to */ |
34132e54 | 162 | struct sockaddr_storage hostaddr; |
d4a8b7e3 | 163 | |
5eabda30 | 164 | /* Private host keys. */ |
1bad2568 | 165 | Sensitive sensitive_data; |
d4a8b7e3 | 166 | |
1383bd8e | 167 | /* command to be executed */ |
cecee2d6 | 168 | struct sshbuf *command; |
1383bd8e | 169 | |
2797f7f0 | 170 | /* # of replies received for global requests */ |
663e84bb | 171 | static int forward_confirms_pending = -1; |
2797f7f0 | 172 | |
b1cbfa25 DM |
173 | /* mux.c */ |
174 | extern int muxserver_sock; | |
175 | extern u_int muxclient_command; | |
0e220dbf | 176 | |
d4a8b7e3 DM |
177 | /* Prints a help message to the user. This function never returns. */ |
178 | ||
bba81213 | 179 | static void |
31ca54aa | 180 | usage(void) |
d4a8b7e3 | 181 | { |
5095510e | 182 | fprintf(stderr, |
6d6e185b | 183 | "usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface] [-b bind_address]\n" |
184 | " [-c cipher_spec] [-D [bind_address:]port] [-E log_file]\n" | |
185 | " [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file]\n" | |
186 | " [-J destination] [-L address] [-l login_name] [-m mac_spec]\n" | |
531b27a0 | 187 | " [-O ctl_cmd] [-o option] [-P tag] [-p port] [-R address]\n" |
188 | " [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" | |
6d6e185b | 189 | " destination [command [argument ...]]\n" |
531b27a0 | 190 | " ssh [-Q query_option]\n" |
5095510e | 191 | ); |
e9a9b71c | 192 | exit(255); |
d4a8b7e3 DM |
193 | } |
194 | ||
d060bc7f | 195 | static int ssh_session2(struct ssh *, const struct ssh_conn_info *); |
196 | static void load_public_identity_files(const struct ssh_conn_info *); | |
857b02e3 | 197 | static void main_sigchld_handler(int); |
b1cbfa25 | 198 | |
295ee63a DM |
199 | /* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */ |
200 | static void | |
201 | tilde_expand_paths(char **paths, u_int num_paths) | |
202 | { | |
203 | u_int i; | |
204 | char *cp; | |
205 | ||
206 | for (i = 0; i < num_paths; i++) { | |
e655ee04 | 207 | cp = tilde_expand_filename(paths[i], getuid()); |
a627d42e | 208 | free(paths[i]); |
295ee63a DM |
209 | paths[i] = cp; |
210 | } | |
211 | } | |
212 | ||
990687a0 | 213 | /* |
214 | * Expands the set of percent_expand options used by the majority of keywords | |
215 | * in the client that support percent expansion. | |
216 | * Caller must free returned string. | |
217 | */ | |
218 | static char * | |
d060bc7f | 219 | default_client_percent_expand(const char *str, |
220 | const struct ssh_conn_info *cinfo) | |
990687a0 | 221 | { |
222 | return percent_expand(str, | |
0f504f59 | 223 | DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), |
990687a0 | 224 | (char *)NULL); |
225 | } | |
226 | ||
4a1b46e6 | 227 | /* |
228 | * Expands the set of percent_expand options used by the majority of keywords | |
229 | * AND perform environment variable substitution. | |
230 | * Caller must free returned string. | |
231 | */ | |
232 | static char * | |
d060bc7f | 233 | default_client_percent_dollar_expand(const char *str, |
234 | const struct ssh_conn_info *cinfo) | |
4a1b46e6 | 235 | { |
236 | char *ret; | |
237 | ||
238 | ret = percent_dollar_expand(str, | |
0f504f59 | 239 | DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), |
4a1b46e6 | 240 | (char *)NULL); |
241 | if (ret == NULL) | |
242 | fatal("invalid environment variable expansion"); | |
243 | return ret; | |
244 | } | |
245 | ||
13f97b22 DM |
246 | /* |
247 | * Attempt to resolve a host name / port to a set of addresses and | |
248 | * optionally return any CNAMEs encountered along the way. | |
249 | * Returns NULL on failure. | |
250 | * NB. this function must operate with a options having undefined members. | |
251 | */ | |
0faf747e | 252 | static struct addrinfo * |
13f97b22 | 253 | resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) |
0faf747e DM |
254 | { |
255 | char strport[NI_MAXSERV]; | |
44536343 | 256 | const char *errstr = NULL; |
0faf747e | 257 | struct addrinfo hints, *res; |
3a7db919 | 258 | int gaierr; |
259 | LogLevel loglevel = SYSLOG_LEVEL_DEBUG1; | |
0faf747e | 260 | |
13f97b22 DM |
261 | if (port <= 0) |
262 | port = default_ssh_port(); | |
31f1ee54 | 263 | if (cname != NULL) |
264 | *cname = '\0'; | |
a4bee193 | 265 | debug3_f("lookup %s:%d", name, port); |
13f97b22 | 266 | |
b1d38a3c | 267 | snprintf(strport, sizeof strport, "%d", port); |
1d2c4564 | 268 | memset(&hints, 0, sizeof(hints)); |
13f97b22 DM |
269 | hints.ai_family = options.address_family == -1 ? |
270 | AF_UNSPEC : options.address_family; | |
0faf747e DM |
271 | hints.ai_socktype = SOCK_STREAM; |
272 | if (cname != NULL) | |
273 | hints.ai_flags = AI_CANONNAME; | |
274 | if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { | |
275 | if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) | |
276 | loglevel = SYSLOG_LEVEL_ERROR; | |
277 | do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", | |
278 | __progname, name, ssh_gai_strerror(gaierr)); | |
279 | return NULL; | |
280 | } | |
281 | if (cname != NULL && res->ai_canonname != NULL) { | |
44536343 | 282 | if (!valid_domain(res->ai_canonname, 0, &errstr)) { |
283 | error("ignoring bad CNAME \"%s\" for host \"%s\": %s", | |
284 | res->ai_canonname, name, errstr); | |
285 | } else if (strlcpy(cname, res->ai_canonname, clen) >= clen) { | |
816036f1 | 286 | error_f("host \"%s\" cname \"%s\" too long (max %lu)", |
287 | name, res->ai_canonname, (u_long)clen); | |
0faf747e DM |
288 | if (clen > 0) |
289 | *cname = '\0'; | |
290 | } | |
291 | } | |
292 | return res; | |
293 | } | |
294 | ||
fc21ea97 | 295 | /* Returns non-zero if name can only be an address and not a hostname */ |
296 | static int | |
297 | is_addr_fast(const char *name) | |
298 | { | |
299 | return (strchr(name, '%') != NULL || strchr(name, ':') != NULL || | |
300 | strspn(name, "0123456789.") == strlen(name)); | |
301 | } | |
302 | ||
303 | /* Returns non-zero if name represents a valid, single address */ | |
304 | static int | |
305 | is_addr(const char *name) | |
306 | { | |
307 | char strport[NI_MAXSERV]; | |
308 | struct addrinfo hints, *res; | |
309 | ||
310 | if (is_addr_fast(name)) | |
311 | return 1; | |
312 | ||
313 | snprintf(strport, sizeof strport, "%u", default_ssh_port()); | |
314 | memset(&hints, 0, sizeof(hints)); | |
315 | hints.ai_family = options.address_family == -1 ? | |
316 | AF_UNSPEC : options.address_family; | |
317 | hints.ai_socktype = SOCK_STREAM; | |
318 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; | |
319 | if (getaddrinfo(name, strport, &hints, &res) != 0) | |
320 | return 0; | |
321 | if (res == NULL || res->ai_next != NULL) { | |
322 | freeaddrinfo(res); | |
323 | return 0; | |
324 | } | |
325 | freeaddrinfo(res); | |
326 | return 1; | |
327 | } | |
328 | ||
90109029 | 329 | /* |
330 | * Attempt to resolve a numeric host address / port to a single address. | |
331 | * Returns a canonical address string. | |
332 | * Returns NULL on failure. | |
333 | * NB. this function must operate with a options having undefined members. | |
334 | */ | |
335 | static struct addrinfo * | |
336 | resolve_addr(const char *name, int port, char *caddr, size_t clen) | |
337 | { | |
338 | char addr[NI_MAXHOST], strport[NI_MAXSERV]; | |
339 | struct addrinfo hints, *res; | |
340 | int gaierr; | |
341 | ||
342 | if (port <= 0) | |
343 | port = default_ssh_port(); | |
344 | snprintf(strport, sizeof strport, "%u", port); | |
345 | memset(&hints, 0, sizeof(hints)); | |
346 | hints.ai_family = options.address_family == -1 ? | |
347 | AF_UNSPEC : options.address_family; | |
348 | hints.ai_socktype = SOCK_STREAM; | |
349 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; | |
350 | if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { | |
816036f1 | 351 | debug2_f("could not resolve name %.100s as address: %s", |
352 | name, ssh_gai_strerror(gaierr)); | |
90109029 | 353 | return NULL; |
354 | } | |
355 | if (res == NULL) { | |
816036f1 | 356 | debug_f("getaddrinfo %.100s returned no addresses", name); |
90109029 | 357 | return NULL; |
358 | } | |
359 | if (res->ai_next != NULL) { | |
816036f1 | 360 | debug_f("getaddrinfo %.100s returned multiple addresses", name); |
90109029 | 361 | goto fail; |
362 | } | |
363 | if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen, | |
364 | addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) { | |
816036f1 | 365 | debug_f("Could not format address for name %.100s: %s", |
366 | name, ssh_gai_strerror(gaierr)); | |
90109029 | 367 | goto fail; |
368 | } | |
369 | if (strlcpy(caddr, addr, clen) >= clen) { | |
816036f1 | 370 | error_f("host \"%s\" addr \"%s\" too long (max %lu)", |
371 | name, addr, (u_long)clen); | |
90109029 | 372 | if (clen > 0) |
373 | *caddr = '\0'; | |
374 | fail: | |
375 | freeaddrinfo(res); | |
376 | return NULL; | |
377 | } | |
378 | return res; | |
379 | } | |
380 | ||
0faf747e DM |
381 | /* |
382 | * Check whether the cname is a permitted replacement for the hostname | |
383 | * and perform the replacement if it is. | |
13f97b22 | 384 | * NB. this function must operate with a options having undefined members. |
0faf747e DM |
385 | */ |
386 | static int | |
ed877ef6 | 387 | check_follow_cname(int direct, char **namep, const char *cname) |
0faf747e DM |
388 | { |
389 | int i; | |
390 | struct allowed_cname *rule; | |
391 | ||
a4bee193 | 392 | if (*cname == '\0' || !config_has_permitted_cnames(&options) || |
0faf747e DM |
393 | strcmp(*namep, cname) == 0) |
394 | return 0; | |
3850559b | 395 | if (options.canonicalize_hostname == SSH_CANONICALISE_NO) |
0faf747e DM |
396 | return 0; |
397 | /* | |
3850559b | 398 | * Don't attempt to canonicalize names that will be interpreted by |
ed877ef6 | 399 | * a proxy or jump host unless the user specifically requests so. |
0faf747e | 400 | */ |
ed877ef6 | 401 | if (!direct && |
3850559b | 402 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) |
0faf747e | 403 | return 0; |
816036f1 | 404 | debug3_f("check \"%s\" CNAME \"%s\"", *namep, cname); |
0faf747e DM |
405 | for (i = 0; i < options.num_permitted_cnames; i++) { |
406 | rule = options.permitted_cnames + i; | |
e661a863 | 407 | if (match_pattern_list(*namep, rule->source_list, 1) != 1 || |
408 | match_pattern_list(cname, rule->target_list, 1) != 1) | |
0faf747e | 409 | continue; |
3850559b | 410 | verbose("Canonicalized DNS aliased hostname " |
0faf747e DM |
411 | "\"%s\" => \"%s\"", *namep, cname); |
412 | free(*namep); | |
413 | *namep = xstrdup(cname); | |
414 | return 1; | |
415 | } | |
416 | return 0; | |
417 | } | |
418 | ||
419 | /* | |
420 | * Attempt to resolve the supplied hostname after applying the user's | |
51682faa DM |
421 | * canonicalization rules. Returns the address list for the host or NULL |
422 | * if no name was found after canonicalization. | |
13f97b22 | 423 | * NB. this function must operate with a options having undefined members. |
0faf747e DM |
424 | */ |
425 | static struct addrinfo * | |
13f97b22 | 426 | resolve_canonicalize(char **hostp, int port) |
0faf747e | 427 | { |
ed877ef6 | 428 | int i, direct, ndots; |
90109029 | 429 | char *cp, *fullhost, newname[NI_MAXHOST]; |
0faf747e DM |
430 | struct addrinfo *addrs; |
431 | ||
0faf747e | 432 | /* |
fc21ea97 | 433 | * Attempt to canonicalise addresses, regardless of |
434 | * whether hostname canonicalisation was requested | |
0faf747e | 435 | */ |
90109029 | 436 | if ((addrs = resolve_addr(*hostp, port, |
437 | newname, sizeof(newname))) != NULL) { | |
816036f1 | 438 | debug2_f("hostname %.100s is address", *hostp); |
90109029 | 439 | if (strcasecmp(*hostp, newname) != 0) { |
816036f1 | 440 | debug2_f("canonicalised address \"%s\" => \"%s\"", |
441 | *hostp, newname); | |
90109029 | 442 | free(*hostp); |
443 | *hostp = xstrdup(newname); | |
444 | } | |
445 | return addrs; | |
446 | } | |
447 | ||
fc21ea97 | 448 | /* |
449 | * If this looks like an address but didn't parse as one, it might | |
450 | * be an address with an invalid interface scope. Skip further | |
451 | * attempts at canonicalisation. | |
452 | */ | |
453 | if (is_addr_fast(*hostp)) { | |
816036f1 | 454 | debug_f("hostname %.100s is an unrecognised address", *hostp); |
fc21ea97 | 455 | return NULL; |
456 | } | |
457 | ||
458 | if (options.canonicalize_hostname == SSH_CANONICALISE_NO) | |
459 | return NULL; | |
460 | ||
461 | /* | |
462 | * Don't attempt to canonicalize names that will be interpreted by | |
463 | * a proxy unless the user specifically requests so. | |
464 | */ | |
465 | direct = option_clear_or_none(options.proxy_command) && | |
c8eb3941 | 466 | option_clear_or_none(options.jump_host); |
fc21ea97 | 467 | if (!direct && |
468 | options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) | |
469 | return NULL; | |
470 | ||
5ee0063f | 471 | /* If domain name is anchored, then resolve it now */ |
472 | if ((*hostp)[strlen(*hostp) - 1] == '.') { | |
816036f1 | 473 | debug3_f("name is fully qualified"); |
5ee0063f | 474 | fullhost = xstrdup(*hostp); |
475 | if ((addrs = resolve_host(fullhost, port, 0, | |
476 | newname, sizeof(newname))) != NULL) | |
477 | goto found; | |
478 | free(fullhost); | |
479 | goto notfound; | |
480 | } | |
481 | ||
51682faa | 482 | /* Don't apply canonicalization to sufficiently-qualified hostnames */ |
0faf747e DM |
483 | ndots = 0; |
484 | for (cp = *hostp; *cp != '\0'; cp++) { | |
485 | if (*cp == '.') | |
486 | ndots++; | |
487 | } | |
3850559b | 488 | if (ndots > options.canonicalize_max_dots) { |
816036f1 | 489 | debug3_f("not canonicalizing hostname \"%s\" (max dots %d)", |
490 | *hostp, options.canonicalize_max_dots); | |
0faf747e DM |
491 | return NULL; |
492 | } | |
493 | /* Attempt each supplied suffix */ | |
494 | for (i = 0; i < options.num_canonical_domains; i++) { | |
ea9e45c8 | 495 | if (strcasecmp(options.canonical_domains[i], "none") == 0) |
496 | break; | |
0faf747e DM |
497 | xasprintf(&fullhost, "%s.%s.", *hostp, |
498 | options.canonical_domains[i]); | |
816036f1 | 499 | debug3_f("attempting \"%s\" => \"%s\"", *hostp, fullhost); |
13f97b22 | 500 | if ((addrs = resolve_host(fullhost, port, 0, |
90109029 | 501 | newname, sizeof(newname))) == NULL) { |
0faf747e DM |
502 | free(fullhost); |
503 | continue; | |
504 | } | |
5ee0063f | 505 | found: |
0faf747e DM |
506 | /* Remove trailing '.' */ |
507 | fullhost[strlen(fullhost) - 1] = '\0'; | |
508 | /* Follow CNAME if requested */ | |
ed877ef6 | 509 | if (!check_follow_cname(direct, &fullhost, newname)) { |
3850559b | 510 | debug("Canonicalized hostname \"%s\" => \"%s\"", |
0faf747e DM |
511 | *hostp, fullhost); |
512 | } | |
513 | free(*hostp); | |
514 | *hostp = fullhost; | |
515 | return addrs; | |
516 | } | |
5ee0063f | 517 | notfound: |
3850559b | 518 | if (!options.canonicalize_fallback_local) |
13f97b22 | 519 | fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); |
816036f1 | 520 | debug2_f("host %s not found in any suffix", *hostp); |
0faf747e DM |
521 | return NULL; |
522 | } | |
523 | ||
5467fbcb | 524 | /* |
54b333d1 | 525 | * Check the result of hostkey loading, ignoring some errors and either |
526 | * discarding the key or fatal()ing for others. | |
5467fbcb | 527 | */ |
528 | static void | |
54b333d1 | 529 | check_load(int r, struct sshkey **k, const char *path, const char *message) |
5467fbcb | 530 | { |
531 | switch (r) { | |
532 | case 0: | |
54b333d1 | 533 | /* Check RSA keys size and discard if undersized */ |
534 | if (k != NULL && *k != NULL && | |
535 | (r = sshkey_check_rsa_length(*k, | |
536 | options.required_rsa_size)) != 0) { | |
537 | error_r(r, "load %s \"%s\"", message, path); | |
538 | free(*k); | |
539 | *k = NULL; | |
540 | } | |
5467fbcb | 541 | break; |
542 | case SSH_ERR_INTERNAL_ERROR: | |
543 | case SSH_ERR_ALLOC_FAIL: | |
816036f1 | 544 | fatal_r(r, "load %s \"%s\"", message, path); |
5467fbcb | 545 | case SSH_ERR_SYSTEM_ERROR: |
546 | /* Ignore missing files */ | |
547 | if (errno == ENOENT) | |
548 | break; | |
549 | /* FALLTHROUGH */ | |
550 | default: | |
816036f1 | 551 | error_r(r, "load %s \"%s\"", message, path); |
5467fbcb | 552 | break; |
553 | } | |
554 | } | |
555 | ||
13f97b22 DM |
556 | /* |
557 | * Read per-user configuration file. Ignore the system wide config | |
558 | * file if the user specifies a config file on the command line. | |
559 | */ | |
560 | static void | |
9e34e0c5 | 561 | process_config_files(const char *host_name, struct passwd *pw, int final_pass, |
562 | int *want_final_pass) | |
13f97b22 | 563 | { |
087266ec | 564 | char buf[PATH_MAX]; |
13f97b22 DM |
565 | int r; |
566 | ||
567 | if (config != NULL) { | |
568 | if (strcasecmp(config, "none") != 0 && | |
b7548b12 | 569 | !read_config_file(config, pw, host, host_name, &options, |
9e34e0c5 | 570 | SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), |
571 | want_final_pass)) | |
13f97b22 DM |
572 | fatal("Can't open user config file %.100s: " |
573 | "%.100s", config, strerror(errno)); | |
574 | } else { | |
575 | r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, | |
576 | _PATH_SSH_USER_CONFFILE); | |
577 | if (r > 0 && (size_t)r < sizeof(buf)) | |
b7548b12 | 578 | (void)read_config_file(buf, pw, host, host_name, |
957fbceb | 579 | &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | |
9e34e0c5 | 580 | (final_pass ? SSHCONF_FINAL : 0), want_final_pass); |
13f97b22 DM |
581 | |
582 | /* Read systemwide configuration file after user config. */ | |
957fbceb | 583 | (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, |
b7548b12 | 584 | host, host_name, &options, |
9e34e0c5 | 585 | final_pass ? SSHCONF_FINAL : 0, want_final_pass); |
957fbceb | 586 | } |
587 | } | |
588 | ||
589 | /* Rewrite the port number in an addrinfo list of addresses */ | |
590 | static void | |
591 | set_addrinfo_port(struct addrinfo *addrs, int port) | |
592 | { | |
593 | struct addrinfo *addr; | |
594 | ||
595 | for (addr = addrs; addr != NULL; addr = addr->ai_next) { | |
596 | switch (addr->ai_family) { | |
597 | case AF_INET: | |
598 | ((struct sockaddr_in *)addr->ai_addr)-> | |
599 | sin_port = htons(port); | |
600 | break; | |
601 | case AF_INET6: | |
602 | ((struct sockaddr_in6 *)addr->ai_addr)-> | |
603 | sin6_port = htons(port); | |
604 | break; | |
605 | } | |
13f97b22 DM |
606 | } |
607 | } | |
608 | ||
d060bc7f | 609 | static void |
610 | ssh_conn_info_free(struct ssh_conn_info *cinfo) | |
611 | { | |
612 | if (cinfo == NULL) | |
613 | return; | |
614 | free(cinfo->conn_hash_hex); | |
615 | free(cinfo->shorthost); | |
616 | free(cinfo->uidstr); | |
617 | free(cinfo->keyalias); | |
618 | free(cinfo->thishost); | |
619 | free(cinfo->host_arg); | |
620 | free(cinfo->portstr); | |
621 | free(cinfo->remhost); | |
622 | free(cinfo->remuser); | |
623 | free(cinfo->homedir); | |
624 | free(cinfo->locuser); | |
98fc34df | 625 | free(cinfo->jmphost); |
d060bc7f | 626 | free(cinfo); |
627 | } | |
628 | ||
7ef3787c | 629 | static int |
630 | valid_hostname(const char *s) | |
631 | { | |
632 | size_t i; | |
633 | ||
634 | if (*s == '-') | |
635 | return 0; | |
636 | for (i = 0; s[i] != 0; i++) { | |
637 | if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL || | |
638 | isspace((u_char)s[i]) || iscntrl((u_char)s[i])) | |
639 | return 0; | |
640 | } | |
641 | return 1; | |
642 | } | |
643 | ||
644 | static int | |
645 | valid_ruser(const char *s) | |
646 | { | |
647 | size_t i; | |
648 | ||
649 | if (*s == '-') | |
650 | return 0; | |
651 | for (i = 0; s[i] != 0; i++) { | |
652 | if (strchr("'`\";&<>|(){}", s[i]) != NULL) | |
653 | return 0; | |
654 | /* Disallow '-' after whitespace */ | |
655 | if (isspace((u_char)s[i]) && s[i + 1] == '-') | |
656 | return 0; | |
657 | /* Disallow \ in last position */ | |
658 | if (s[i] == '\\' && s[i + 1] == '\0') | |
659 | return 0; | |
660 | } | |
661 | return 1; | |
662 | } | |
663 | ||
95def098 DM |
664 | /* |
665 | * Main program for the ssh client. | |
666 | */ | |
d4a8b7e3 DM |
667 | int |
668 | main(int ac, char **av) | |
669 | { | |
95767262 | 670 | struct ssh *ssh = NULL; |
643c2ad8 | 671 | int i, r, opt, exit_status, use_syslog, direct, timeout_ms; |
9e34e0c5 | 672 | int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; |
41f36dd8 | 673 | char *p, *cp, *line, *argv0, *logfile; |
d060bc7f | 674 | char cname[NI_MAXHOST], thishost[NI_MAXHOST]; |
95def098 | 675 | struct stat st; |
086cf214 | 676 | struct passwd *pw; |
5ccf63a9 | 677 | extern int optind, optreset; |
4f8e6692 | 678 | extern char *optarg; |
7acefbbc | 679 | struct Forward fwd; |
0faf747e | 680 | struct addrinfo *addrs = NULL; |
7f8e66fe | 681 | size_t n, len; |
c4f23994 | 682 | u_int j; |
d060bc7f | 683 | struct ssh_conn_info *cinfo = NULL; |
684 | ||
ce321d8a DT |
685 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
686 | sanitise_stdfd(); | |
687 | ||
c9f7bba2 DT |
688 | /* |
689 | * Discard other fds that are hanging around. These can cause problem | |
690 | * with backgrounded ssh processes started by ControlPersist. | |
691 | */ | |
692 | closefrom(STDERR_FILENO + 1); | |
693 | ||
59d3d5b8 | 694 | __progname = ssh_get_progname(av[0]); |
f9b625c3 | 695 | |
ea2c1a4d DM |
696 | #ifndef HAVE_SETPROCTITLE |
697 | /* Prepare for later setproctitle emulation */ | |
698 | /* Save argv so it isn't clobbered by setproctitle() emulation */ | |
699 | saved_av = xcalloc(ac + 1, sizeof(*saved_av)); | |
700 | for (i = 0; i < ac; i++) | |
701 | saved_av[i] = xstrdup(av[i]); | |
702 | saved_av[i] = NULL; | |
703 | compat_init_setproctitle(ac, av); | |
704 | av = saved_av; | |
705 | #endif | |
706 | ||
42c5ec4b DM |
707 | seed_rng(); |
708 | ||
3fcf1a22 | 709 | /* Get user data. */ |
e655ee04 | 710 | pw = getpwuid(getuid()); |
3fcf1a22 | 711 | if (!pw) { |
e655ee04 | 712 | logit("No user exists for uid %lu", (u_long)getuid()); |
e9a9b71c | 713 | exit(255); |
3fcf1a22 BL |
714 | } |
715 | /* Take a copy of the returned structure. */ | |
716 | pw = pwcopy(pw); | |
717 | ||
5428f646 DM |
718 | /* |
719 | * Set our umask to something reasonable, as some files are created | |
720 | * with the default umask. This will make them world-readable but | |
721 | * writable only by the owner, which is ok for all files for which we | |
722 | * don't set the modes explicitly. | |
723 | */ | |
5ee2b8cc | 724 | umask(022 | umask(077)); |
95def098 | 725 | |
dda78a03 | 726 | msetlocale(); |
65c6c6b5 | 727 | |
d6173c0b DT |
728 | /* |
729 | * Initialize option structure to indicate that no values have been | |
730 | * set. | |
731 | */ | |
95def098 DM |
732 | initialize_options(&options); |
733 | ||
dbee4119 | 734 | /* |
735 | * Prepare main ssh transport/connection structures | |
736 | */ | |
737 | if ((ssh = ssh_alloc_session_state()) == NULL) | |
738 | fatal("Couldn't allocate session state"); | |
739 | channel_init_channels(ssh); | |
dbee4119 | 740 | |
95def098 DM |
741 | /* Parse command-line arguments. */ |
742 | host = NULL; | |
e272a5bb | 743 | use_syslog = 0; |
03d4d7e6 | 744 | logfile = NULL; |
72efd74d | 745 | argv0 = av[0]; |
95def098 | 746 | |
2ecb6bd9 | 747 | again: |
d6173c0b | 748 | while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" |
919bc3d3 | 749 | "AB:CD:E:F:GI:J:KL:MNO:P:Q:R:S:TVw:W:XYy")) != -1) { /* HUZdhjruz */ |
95def098 | 750 | switch (opt) { |
1e7d3061 | 751 | case '1': |
99f95ba8 | 752 | fatal("SSH protocol v.1 is no longer supported"); |
1e7d3061 | 753 | break; |
4af51306 | 754 | case '2': |
99f95ba8 | 755 | /* Ignored */ |
4af51306 | 756 | break; |
34132e54 | 757 | case '4': |
0a4f04b5 | 758 | options.address_family = AF_INET; |
34132e54 | 759 | break; |
34132e54 | 760 | case '6': |
0a4f04b5 | 761 | options.address_family = AF_INET6; |
34132e54 | 762 | break; |
95def098 | 763 | case 'n': |
e0c5088f | 764 | options.stdin_null = 1; |
95def098 | 765 | break; |
95def098 | 766 | case 'f': |
a917e973 | 767 | options.fork_after_authentication = 1; |
e0c5088f | 768 | options.stdin_null = 1; |
95def098 | 769 | break; |
95def098 DM |
770 | case 'x': |
771 | options.forward_x11 = 0; | |
772 | break; | |
95def098 DM |
773 | case 'X': |
774 | options.forward_x11 = 1; | |
775 | break; | |
e272a5bb DM |
776 | case 'y': |
777 | use_syslog = 1; | |
778 | break; | |
03d4d7e6 | 779 | case 'E': |
4f7cc2f8 | 780 | logfile = optarg; |
03d4d7e6 | 781 | break; |
957fbceb | 782 | case 'G': |
783 | config_test = 1; | |
784 | break; | |
0a118da0 DT |
785 | case 'Y': |
786 | options.forward_x11 = 1; | |
787 | options.forward_x11_trusted = 1; | |
788 | break; | |
95def098 | 789 | case 'g': |
7acefbbc | 790 | options.fwd_opts.gateway_ports = 1; |
95def098 | 791 | break; |
7ebfc108 | 792 | case 'O': |
8543ff3f | 793 | if (options.stdio_forward_host != NULL) |
e1537f95 DM |
794 | fatal("Cannot specify multiplexing " |
795 | "command with -W"); | |
796 | else if (muxclient_command != 0) | |
797 | fatal("Multiplexing command already specified"); | |
7ebfc108 | 798 | if (strcmp(optarg, "check") == 0) |
b1cbfa25 | 799 | muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; |
388f6fc4 DM |
800 | else if (strcmp(optarg, "forward") == 0) |
801 | muxclient_command = SSHMUX_COMMAND_FORWARD; | |
7ebfc108 | 802 | else if (strcmp(optarg, "exit") == 0) |
b1cbfa25 | 803 | muxclient_command = SSHMUX_COMMAND_TERMINATE; |
6c3eec7a DM |
804 | else if (strcmp(optarg, "stop") == 0) |
805 | muxclient_command = SSHMUX_COMMAND_STOP; | |
f6dff7cd DM |
806 | else if (strcmp(optarg, "cancel") == 0) |
807 | muxclient_command = SSHMUX_COMMAND_CANCEL_FWD; | |
8d057847 | 808 | else if (strcmp(optarg, "proxy") == 0) |
809 | muxclient_command = SSHMUX_COMMAND_PROXY; | |
7ebfc108 DT |
810 | else |
811 | fatal("Invalid multiplex command."); | |
812 | break; | |
919bc3d3 | 813 | case 'P': |
814 | if (options.tag == NULL) | |
815 | options.tag = xstrdup(optarg); | |
95def098 | 816 | break; |
d937dc08 | 817 | case 'Q': |
ea11119e | 818 | cp = NULL; |
d4d9e1d4 | 819 | if (strcmp(optarg, "cipher") == 0 || |
820 | strcasecmp(optarg, "Ciphers") == 0) | |
0fde8acd | 821 | cp = cipher_alg_list('\n', 0); |
d937dc08 | 822 | else if (strcmp(optarg, "cipher-auth") == 0) |
0fde8acd | 823 | cp = cipher_alg_list('\n', 1); |
d4d9e1d4 | 824 | else if (strcmp(optarg, "mac") == 0 || |
825 | strcasecmp(optarg, "MACs") == 0) | |
690d9890 | 826 | cp = mac_alg_list('\n'); |
d4d9e1d4 | 827 | else if (strcmp(optarg, "kex") == 0 || |
828 | strcasecmp(optarg, "KexAlgorithms") == 0) | |
690d9890 | 829 | cp = kex_alg_list('\n'); |
d937dc08 | 830 | else if (strcmp(optarg, "key") == 0) |
183ba55a | 831 | cp = sshkey_alg_list(0, 0, 0, '\n'); |
5be9d9e3 | 832 | else if (strcmp(optarg, "key-cert") == 0) |
183ba55a | 833 | cp = sshkey_alg_list(1, 0, 0, '\n'); |
5be9d9e3 | 834 | else if (strcmp(optarg, "key-plain") == 0) |
183ba55a | 835 | cp = sshkey_alg_list(0, 1, 0, '\n'); |
4e73cd0f | 836 | else if (strcmp(optarg, "key-ca-sign") == 0 || |
837 | strcasecmp(optarg, "CASignatureAlgorithms") == 0) | |
838 | cp = sshkey_alg_list(0, 1, 1, '\n'); | |
d4d9e1d4 | 839 | else if (strcmp(optarg, "key-sig") == 0 || |
ee9c0da8 | 840 | strcasecmp(optarg, "PubkeyAcceptedKeyTypes") == 0 || /* deprecated name */ |
841 | strcasecmp(optarg, "PubkeyAcceptedAlgorithms") == 0 || | |
d4d9e1d4 | 842 | strcasecmp(optarg, "HostKeyAlgorithms") == 0 || |
e9f78d6b | 843 | strcasecmp(optarg, "HostbasedKeyTypes") == 0 || /* deprecated name */ |
844 | strcasecmp(optarg, "HostbasedAcceptedKeyTypes") == 0 || /* deprecated name */ | |
845 | strcasecmp(optarg, "HostbasedAcceptedAlgorithms") == 0) | |
d4d9e1d4 | 846 | cp = sshkey_alg_list(0, 0, 1, '\n'); |
357128ac | 847 | else if (strcmp(optarg, "sig") == 0) |
aa083aa9 | 848 | cp = sshkey_alg_list(0, 1, 1, '\n'); |
357128ac | 849 | else if (strcmp(optarg, "protocol-version") == 0) |
68d2dfc4 | 850 | cp = xstrdup("2"); |
7f8e66fe | 851 | else if (strcmp(optarg, "compression") == 0) { |
852 | cp = xstrdup(compression_alg_list(0)); | |
853 | len = strlen(cp); | |
854 | for (n = 0; n < len; n++) | |
855 | if (cp[n] == ',') | |
856 | cp[n] = '\n'; | |
857 | } else if (strcmp(optarg, "help") == 0) { | |
357128ac | 858 | cp = xstrdup( |
7f8e66fe | 859 | "cipher\ncipher-auth\ncompression\nkex\n" |
d4d9e1d4 | 860 | "key\nkey-cert\nkey-plain\nkey-sig\nmac\n" |
357128ac | 861 | "protocol-version\nsig"); |
68d2dfc4 | 862 | } |
ea11119e DM |
863 | if (cp == NULL) |
864 | fatal("Unsupported query \"%s\"", optarg); | |
865 | printf("%s\n", cp); | |
866 | free(cp); | |
867 | exit(0); | |
868 | break; | |
95def098 DM |
869 | case 'a': |
870 | options.forward_agent = 0; | |
871 | break; | |
b1715dc0 DM |
872 | case 'A': |
873 | options.forward_agent = 1; | |
874 | break; | |
95def098 | 875 | case 'k': |
e0113ccc | 876 | options.gss_deleg_creds = 0; |
95def098 | 877 | break; |
415bddc1 DT |
878 | case 'K': |
879 | options.gss_authentication = 1; | |
880 | options.gss_deleg_creds = 1; | |
881 | break; | |
95def098 | 882 | case 'i': |
e655ee04 | 883 | p = tilde_expand_filename(optarg, getuid()); |
4d28fa78 | 884 | if (stat(p, &st) == -1) |
f4614450 | 885 | fprintf(stderr, "Warning: Identity file %s " |
03239c18 | 886 | "not accessible: %s.\n", p, |
3eb48b62 | 887 | strerror(errno)); |
03239c18 | 888 | else |
889 | add_identity_file(&options, NULL, p, 1); | |
890 | free(p); | |
95def098 | 891 | break; |
c5b68001 | 892 | case 'I': |
7ea845e4 | 893 | #ifdef ENABLE_PKCS11 |
4f7cc2f8 | 894 | free(options.pkcs11_provider); |
7ea845e4 | 895 | options.pkcs11_provider = xstrdup(optarg); |
bcc1808b | 896 | #else |
7ea845e4 | 897 | fprintf(stderr, "no support for PKCS#11.\n"); |
bcc1808b | 898 | #endif |
c5b68001 | 899 | break; |
ed877ef6 | 900 | case 'J': |
cb8f5657 | 901 | if (options.jump_host != NULL) { |
902 | fatal("Only a single -J option is permitted " | |
903 | "(use commas to separate multiple " | |
904 | "jump hops)"); | |
905 | } | |
ed877ef6 | 906 | if (options.proxy_command != NULL) |
907 | fatal("Cannot specify -J with ProxyCommand"); | |
908 | if (parse_jump(optarg, &options, 1) == -1) | |
909 | fatal("Invalid -J argument"); | |
910 | options.proxy_command = xstrdup("none"); | |
911 | break; | |
95def098 | 912 | case 't': |
21771e22 DM |
913 | if (options.request_tty == REQUEST_TTY_YES) |
914 | options.request_tty = REQUEST_TTY_FORCE; | |
915 | else | |
916 | options.request_tty = REQUEST_TTY_YES; | |
95def098 | 917 | break; |
95def098 | 918 | case 'v': |
e98dfa32 | 919 | if (debug_flag == 0) { |
e4340be5 DM |
920 | debug_flag = 1; |
921 | options.log_level = SYSLOG_LEVEL_DEBUG1; | |
e98dfa32 | 922 | } else { |
ed877ef6 | 923 | if (options.log_level < SYSLOG_LEVEL_DEBUG3) { |
924 | debug_flag++; | |
e98dfa32 | 925 | options.log_level++; |
ed877ef6 | 926 | } |
e98dfa32 | 927 | } |
03d4d7e6 | 928 | break; |
95def098 | 929 | case 'V': |
0c889cd9 | 930 | fprintf(stderr, "%s, %s\n", |
4d94b031 | 931 | SSH_RELEASE, SSH_OPENSSL_VERSION); |
62360feb | 932 | exit(0); |
95def098 | 933 | break; |
d27b9471 | 934 | case 'w': |
7b58e800 DM |
935 | if (options.tun_open == -1) |
936 | options.tun_open = SSH_TUNMODE_DEFAULT; | |
d27b9471 | 937 | options.tun_local = a2tun(optarg, &options.tun_remote); |
7b58e800 | 938 | if (options.tun_local == SSH_TUNID_ERR) { |
d6173c0b DT |
939 | fprintf(stderr, |
940 | "Bad tun device '%s'\n", optarg); | |
e9a9b71c | 941 | exit(255); |
d27b9471 DM |
942 | } |
943 | break; | |
7ad8dd21 | 944 | case 'W': |
8543ff3f | 945 | if (options.stdio_forward_host != NULL) |
e1537f95 DM |
946 | fatal("stdio forward already specified"); |
947 | if (muxclient_command != 0) | |
948 | fatal("Cannot specify stdio forward with -O"); | |
7ad8dd21 | 949 | if (parse_forward(&fwd, optarg, 1, 0)) { |
94842bfe | 950 | options.stdio_forward_host = |
951 | fwd.listen_port == PORT_STREAMLOCAL ? | |
952 | fwd.listen_path : fwd.listen_host; | |
8543ff3f | 953 | options.stdio_forward_port = fwd.listen_port; |
a627d42e | 954 | free(fwd.connect_host); |
7ad8dd21 DT |
955 | } else { |
956 | fprintf(stderr, | |
957 | "Bad stdio forwarding specification '%s'\n", | |
958 | optarg); | |
959 | exit(255); | |
960 | } | |
21771e22 | 961 | options.request_tty = REQUEST_TTY_NO; |
eda8909d | 962 | options.session_type = SESSION_TYPE_NONE; |
7ad8dd21 | 963 | break; |
95def098 DM |
964 | case 'q': |
965 | options.log_level = SYSLOG_LEVEL_QUIET; | |
966 | break; | |
95def098 DM |
967 | case 'e': |
968 | if (optarg[0] == '^' && optarg[2] == 0 && | |
f4614450 DM |
969 | (u_char) optarg[1] >= 64 && |
970 | (u_char) optarg[1] < 128) | |
46c16220 | 971 | options.escape_char = (u_char) optarg[1] & 31; |
95def098 | 972 | else if (strlen(optarg) == 1) |
46c16220 | 973 | options.escape_char = (u_char) optarg[0]; |
95def098 | 974 | else if (strcmp(optarg, "none") == 0) |
2b1f71ba | 975 | options.escape_char = SSH_ESCAPECHAR_NONE; |
95def098 | 976 | else { |
f4614450 DM |
977 | fprintf(stderr, "Bad escape character '%s'.\n", |
978 | optarg); | |
e9a9b71c | 979 | exit(255); |
95def098 DM |
980 | } |
981 | break; | |
95def098 | 982 | case 'c': |
91a2135f | 983 | if (!ciphers_valid(*optarg == '+' || *optarg == '^' ? |
f9eca249 | 984 | optarg + 1 : optarg)) { |
f9eca249 | 985 | fprintf(stderr, "Unknown cipher type '%s'\n", |
986 | optarg); | |
987 | exit(255); | |
95def098 | 988 | } |
cdccebdf | 989 | free(options.ciphers); |
990 | options.ciphers = xstrdup(optarg); | |
95def098 | 991 | break; |
06b33aa0 | 992 | case 'm': |
4f7cc2f8 | 993 | if (mac_valid(optarg)) { |
994 | free(options.macs); | |
06b33aa0 | 995 | options.macs = xstrdup(optarg); |
4f7cc2f8 | 996 | } else { |
f4614450 DM |
997 | fprintf(stderr, "Unknown mac type '%s'\n", |
998 | optarg); | |
e9a9b71c | 999 | exit(255); |
06b33aa0 BL |
1000 | } |
1001 | break; | |
0e220dbf | 1002 | case 'M': |
d14b1e73 DM |
1003 | if (options.control_master == SSHCTL_MASTER_YES) |
1004 | options.control_master = SSHCTL_MASTER_ASK; | |
1005 | else | |
1006 | options.control_master = SSHCTL_MASTER_YES; | |
0e220dbf | 1007 | break; |
95def098 | 1008 | case 'p': |
887669ef | 1009 | if (options.port == -1) { |
1010 | options.port = a2port(optarg); | |
1011 | if (options.port <= 0) { | |
1012 | fprintf(stderr, "Bad port '%s'\n", | |
1013 | optarg); | |
1014 | exit(255); | |
1015 | } | |
146edb98 | 1016 | } |
95def098 | 1017 | break; |
95def098 | 1018 | case 'l': |
887669ef | 1019 | if (options.user == NULL) |
1020 | options.user = optarg; | |
95def098 | 1021 | break; |
1a174717 BL |
1022 | |
1023 | case 'L': | |
4bf648f7 | 1024 | if (parse_forward(&fwd, optarg, 0, 0)) |
f91ee4c3 DM |
1025 | add_local_forward(&options, &fwd); |
1026 | else { | |
f4614450 | 1027 | fprintf(stderr, |
f91ee4c3 | 1028 | "Bad local forwarding specification '%s'\n", |
f4614450 | 1029 | optarg); |
e9a9b71c | 1030 | exit(255); |
95def098 | 1031 | } |
f91ee4c3 DM |
1032 | break; |
1033 | ||
1034 | case 'R': | |
609d7a66 | 1035 | if (parse_forward(&fwd, optarg, 0, 1) || |
1036 | parse_forward(&fwd, optarg, 1, 1)) { | |
f91ee4c3 DM |
1037 | add_remote_forward(&options, &fwd); |
1038 | } else { | |
f4614450 | 1039 | fprintf(stderr, |
f91ee4c3 DM |
1040 | "Bad remote forwarding specification " |
1041 | "'%s'\n", optarg); | |
e9a9b71c | 1042 | exit(255); |
95def098 | 1043 | } |
95def098 | 1044 | break; |
3bb4f9da BL |
1045 | |
1046 | case 'D': | |
4bf648f7 | 1047 | if (parse_forward(&fwd, optarg, 1, 0)) { |
a699d952 | 1048 | add_local_forward(&options, &fwd); |
f91ee4c3 | 1049 | } else { |
a699d952 DM |
1050 | fprintf(stderr, |
1051 | "Bad dynamic forwarding specification " | |
1052 | "'%s'\n", optarg); | |
e9a9b71c | 1053 | exit(255); |
146edb98 | 1054 | } |
3bb4f9da BL |
1055 | break; |
1056 | ||
95def098 | 1057 | case 'C': |
7f8e66fe | 1058 | #ifdef WITH_ZLIB |
95def098 | 1059 | options.compression = 1; |
7f8e66fe | 1060 | #else |
1061 | error("Compression not supported, disabling."); | |
1062 | #endif | |
95def098 | 1063 | break; |
1383bd8e | 1064 | case 'N': |
eda8909d | 1065 | if (options.session_type != -1 && |
1066 | options.session_type != SESSION_TYPE_NONE) | |
1067 | fatal("Cannot specify -N with -s/SessionType"); | |
1068 | options.session_type = SESSION_TYPE_NONE; | |
21771e22 | 1069 | options.request_tty = REQUEST_TTY_NO; |
1383bd8e | 1070 | break; |
1383bd8e | 1071 | case 'T': |
21771e22 | 1072 | options.request_tty = REQUEST_TTY_NO; |
1383bd8e | 1073 | break; |
95def098 | 1074 | case 'o': |
9836cf8d | 1075 | line = xstrdup(optarg); |
957fbceb | 1076 | if (process_config_line(&options, pw, |
1077 | host ? host : "", host ? host : "", line, | |
1078 | "command-line", 0, NULL, SSHCONF_USERCONF) != 0) | |
e9a9b71c | 1079 | exit(255); |
a627d42e | 1080 | free(line); |
95def098 | 1081 | break; |
832562e9 | 1082 | case 's': |
eda8909d | 1083 | if (options.session_type != -1 && |
1084 | options.session_type != SESSION_TYPE_SUBSYSTEM) | |
1085 | fatal("Cannot specify -s with -N/SessionType"); | |
1086 | options.session_type = SESSION_TYPE_SUBSYSTEM; | |
832562e9 | 1087 | break; |
0e220dbf | 1088 | case 'S': |
d59ce088 | 1089 | free(options.control_path); |
0e220dbf | 1090 | options.control_path = xstrdup(optarg); |
0e220dbf | 1091 | break; |
e0f88041 BL |
1092 | case 'b': |
1093 | options.bind_address = optarg; | |
1094 | break; | |
ac2e3026 | 1095 | case 'B': |
1096 | options.bind_interface = optarg; | |
1097 | break; | |
14f31ab9 BL |
1098 | case 'F': |
1099 | config = optarg; | |
1100 | break; | |
95def098 DM |
1101 | default: |
1102 | usage(); | |
1103 | } | |
1104 | } | |
1105 | ||
643c2ad8 | 1106 | if (optind > 1 && strcmp(av[optind - 1], "--") == 0) |
1107 | opt_terminated = 1; | |
1108 | ||
f4614450 DM |
1109 | ac -= optind; |
1110 | av += optind; | |
1111 | ||
b8c884a0 | 1112 | if (ac > 0 && !host) { |
887669ef | 1113 | int tport; |
1114 | char *tuser; | |
1115 | switch (parse_ssh_uri(*av, &tuser, &host, &tport)) { | |
1116 | case -1: | |
1117 | usage(); | |
1118 | break; | |
1119 | case 0: | |
1120 | if (options.user == NULL) { | |
1121 | options.user = tuser; | |
1122 | tuser = NULL; | |
1123 | } | |
1124 | free(tuser); | |
1125 | if (options.port == -1 && tport != -1) | |
1126 | options.port = tport; | |
1127 | break; | |
1128 | default: | |
f4614450 | 1129 | p = xstrdup(*av); |
c276c120 | 1130 | cp = strrchr(p, '@'); |
887669ef | 1131 | if (cp != NULL) { |
1132 | if (cp == p) | |
1133 | usage(); | |
1134 | if (options.user == NULL) { | |
1135 | options.user = p; | |
1136 | p = NULL; | |
1137 | } | |
1138 | *cp++ = '\0'; | |
1139 | host = xstrdup(cp); | |
1140 | free(p); | |
1141 | } else | |
1142 | host = p; | |
1143 | break; | |
1144 | } | |
643c2ad8 | 1145 | if (ac > 1 && !opt_terminated) { |
b9fa6918 | 1146 | optind = optreset = 1; |
f4614450 DM |
1147 | goto again; |
1148 | } | |
b9fa6918 | 1149 | ac--, av++; |
f4614450 DM |
1150 | } |
1151 | ||
95def098 DM |
1152 | /* Check that we got a host name. */ |
1153 | if (!host) | |
d4a8b7e3 | 1154 | usage(); |
95def098 | 1155 | |
7ef3787c | 1156 | if (!valid_hostname(host)) |
1157 | fatal("hostname contains invalid characters"); | |
1158 | if (options.user != NULL && !valid_ruser(options.user)) | |
1159 | fatal("remote username contains invalid characters"); | |
41f36dd8 | 1160 | options.host_arg = xstrdup(host); |
0faf747e | 1161 | |
95def098 | 1162 | /* Initialize the command to execute on remote host. */ |
cecee2d6 | 1163 | if ((command = sshbuf_new()) == NULL) |
1164 | fatal("sshbuf_new failed"); | |
95def098 | 1165 | |
5428f646 DM |
1166 | /* |
1167 | * Save the command to execute on the remote host in a buffer. There | |
1168 | * is no limit on the length of the command, except by the maximum | |
1169 | * packet size. Also sets the tty flag if there is no command. | |
1170 | */ | |
f4614450 | 1171 | if (!ac) { |
95def098 | 1172 | /* No command specified - execute shell on a tty. */ |
eda8909d | 1173 | if (options.session_type == SESSION_TYPE_SUBSYSTEM) { |
f4614450 DM |
1174 | fprintf(stderr, |
1175 | "You must specify a subsystem to invoke.\n"); | |
832562e9 DM |
1176 | usage(); |
1177 | } | |
95def098 | 1178 | } else { |
f4614450 DM |
1179 | /* A command has been specified. Store it into the buffer. */ |
1180 | for (i = 0; i < ac; i++) { | |
cecee2d6 | 1181 | if ((r = sshbuf_putf(command, "%s%s", |
1182 | i ? " " : "", av[i])) != 0) | |
816036f1 | 1183 | fatal_fr(r, "buffer error"); |
95def098 | 1184 | } |
d4a8b7e3 | 1185 | } |
95def098 | 1186 | |
96faa0de | 1187 | ssh_signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ |
1188 | ||
8a432f5f BL |
1189 | /* |
1190 | * Initialize "log" output. Since we are the client all output | |
03d4d7e6 | 1191 | * goes to stderr unless otherwise specified by -y or -E. |
8a432f5f | 1192 | */ |
03d4d7e6 DM |
1193 | if (use_syslog && logfile != NULL) |
1194 | fatal("Can't specify both -y and -E"); | |
4f7cc2f8 | 1195 | if (logfile != NULL) |
03d4d7e6 | 1196 | log_redirect_stderr_to(logfile); |
72efd74d | 1197 | log_init(argv0, |
dbe0662e | 1198 | options.log_level == SYSLOG_LEVEL_NOT_SET ? |
68d3a2a0 | 1199 | SYSLOG_LEVEL_INFO : options.log_level, |
dbe0662e | 1200 | options.log_facility == SYSLOG_FACILITY_NOT_SET ? |
68d3a2a0 | 1201 | SYSLOG_FACILITY_USER : options.log_facility, |
1202 | !use_syslog); | |
95def098 | 1203 | |
03d4d7e6 | 1204 | if (debug_flag) |
4d94b031 | 1205 | logit("%s, %s", SSH_RELEASE, SSH_OPENSSL_VERSION); |
03d4d7e6 | 1206 | |
13f97b22 | 1207 | /* Parse the configuration files */ |
41f36dd8 | 1208 | process_config_files(options.host_arg, pw, 0, &want_final_pass); |
9e34e0c5 | 1209 | if (want_final_pass) |
1210 | debug("configuration requests final Match pass"); | |
13f97b22 DM |
1211 | |
1212 | /* Hostname canonicalisation needs a few options filled. */ | |
1213 | fill_default_options_for_canonicalization(&options); | |
1214 | ||
1215 | /* If the user has replaced the hostname then take it into use now */ | |
1216 | if (options.hostname != NULL) { | |
1217 | /* NB. Please keep in sync with readconf.c:match_cfg_line() */ | |
1218 | cp = percent_expand(options.hostname, | |
1219 | "h", host, (char *)NULL); | |
1220 | free(host); | |
1221 | host = cp; | |
957fbceb | 1222 | free(options.hostname); |
1223 | options.hostname = xstrdup(host); | |
13f97b22 DM |
1224 | } |
1225 | ||
fc21ea97 | 1226 | /* Don't lowercase addresses, they will be explicitly canonicalised */ |
1227 | if ((was_addr = is_addr(host)) == 0) | |
1228 | lowercase(host); | |
1229 | ||
1230 | /* | |
1231 | * Try to canonicalize if requested by configuration or the | |
1232 | * hostname is an address. | |
1233 | */ | |
1234 | if (options.canonicalize_hostname != SSH_CANONICALISE_NO || was_addr) | |
13f97b22 DM |
1235 | addrs = resolve_canonicalize(&host, options.port); |
1236 | ||
14f31ab9 | 1237 | /* |
08b57c67 DM |
1238 | * If CanonicalizePermittedCNAMEs have been specified but |
1239 | * other canonicalization did not happen (by not being requested | |
1240 | * or by failing with fallback) then the hostname may still be changed | |
dbe0662e | 1241 | * as a result of CNAME following. |
08b57c67 DM |
1242 | * |
1243 | * Try to resolve the bare hostname name using the system resolver's | |
1244 | * usual search rules and then apply the CNAME follow rules. | |
1245 | * | |
1246 | * Skip the lookup if a ProxyCommand is being used unless the user | |
1247 | * has specifically requested canonicalisation for this case via | |
1248 | * CanonicalizeHostname=always | |
14f31ab9 | 1249 | */ |
ed877ef6 | 1250 | direct = option_clear_or_none(options.proxy_command) && |
c8eb3941 | 1251 | option_clear_or_none(options.jump_host); |
a4bee193 | 1252 | if (addrs == NULL && config_has_permitted_cnames(&options) && (direct || |
ed877ef6 | 1253 | options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { |
19439e9a | 1254 | if ((addrs = resolve_host(host, options.port, |
383a33d1 | 1255 | direct, cname, sizeof(cname))) == NULL) { |
19439e9a | 1256 | /* Don't fatal proxied host names not in the DNS */ |
383a33d1 | 1257 | if (direct) |
19439e9a DM |
1258 | cleanup_exit(255); /* logged in resolve_host */ |
1259 | } else | |
ed877ef6 | 1260 | check_follow_cname(direct, &host, cname); |
13f97b22 | 1261 | } |
14f31ab9 | 1262 | |
13f97b22 | 1263 | /* |
957fbceb | 1264 | * If canonicalisation is enabled then re-parse the configuration |
1265 | * files as new stanzas may match. | |
13f97b22 | 1266 | */ |
9e34e0c5 | 1267 | if (options.canonicalize_hostname != 0 && !want_final_pass) { |
1268 | debug("hostname canonicalisation enabled, " | |
1269 | "will re-parse configuration"); | |
1270 | want_final_pass = 1; | |
1271 | } | |
1272 | ||
1273 | if (want_final_pass) { | |
1274 | debug("re-parsing configuration"); | |
957fbceb | 1275 | free(options.hostname); |
1276 | options.hostname = xstrdup(host); | |
41f36dd8 | 1277 | process_config_files(options.host_arg, pw, 1, NULL); |
957fbceb | 1278 | /* |
1279 | * Address resolution happens early with canonicalisation | |
1280 | * enabled and the port number may have changed since, so | |
1281 | * reset it in address list | |
1282 | */ | |
1283 | if (addrs != NULL && options.port > 0) | |
1284 | set_addrinfo_port(addrs, options.port); | |
14f31ab9 | 1285 | } |
95def098 DM |
1286 | |
1287 | /* Fill configuration defaults. */ | |
43026da0 | 1288 | if (fill_default_options(&options) != 0) |
1289 | cleanup_exit(255); | |
95def098 | 1290 | |
ced327b9 | 1291 | if (options.user == NULL) |
1292 | options.user = xstrdup(pw->pw_name); | |
1293 | ||
ed877ef6 | 1294 | /* |
1295 | * If ProxyJump option specified, then construct a ProxyCommand now. | |
1296 | */ | |
1297 | if (options.jump_host != NULL) { | |
1298 | char port_s[8]; | |
ced327b9 | 1299 | const char *jumpuser = options.jump_user, *sshbin = argv0; |
de1f3564 | 1300 | int port = options.port, jumpport = options.jump_port; |
1301 | ||
1302 | if (port <= 0) | |
1303 | port = default_ssh_port(); | |
1304 | if (jumpport <= 0) | |
1305 | jumpport = default_ssh_port(); | |
ced327b9 | 1306 | if (jumpuser == NULL) |
1307 | jumpuser = options.user; | |
1308 | if (strcmp(options.jump_host, host) == 0 && port == jumpport && | |
1309 | strcmp(options.user, jumpuser) == 0) | |
de1f3564 | 1310 | fatal("jumphost loop via %s", options.jump_host); |
d8748b91 | 1311 | |
1312 | /* | |
1313 | * Try to use SSH indicated by argv[0], but fall back to | |
1314 | * "ssh" if it appears unavailable. | |
1315 | */ | |
1316 | if (strchr(argv0, '/') != NULL && access(argv0, X_OK) != 0) | |
1317 | sshbin = "ssh"; | |
ed877ef6 | 1318 | |
1319 | /* Consistency check */ | |
1320 | if (options.proxy_command != NULL) | |
1321 | fatal("inconsistent options: ProxyCommand+ProxyJump"); | |
1322 | /* Never use FD passing for ProxyJump */ | |
1323 | options.proxy_use_fdpass = 0; | |
1324 | snprintf(port_s, sizeof(port_s), "%d", options.jump_port); | |
1325 | xasprintf(&options.proxy_command, | |
d8748b91 | 1326 | "%s%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s", |
1327 | sshbin, | |
ed877ef6 | 1328 | /* Optional "-l user" argument if jump_user set */ |
1329 | options.jump_user == NULL ? "" : " -l ", | |
1330 | options.jump_user == NULL ? "" : options.jump_user, | |
1331 | /* Optional "-p port" argument if jump_port set */ | |
1332 | options.jump_port <= 0 ? "" : " -p ", | |
1333 | options.jump_port <= 0 ? "" : port_s, | |
1334 | /* Optional additional jump hosts ",..." */ | |
1335 | options.jump_extra == NULL ? "" : " -J ", | |
1336 | options.jump_extra == NULL ? "" : options.jump_extra, | |
c45a752f | 1337 | /* Optional "-F" argument if -F specified */ |
ed877ef6 | 1338 | config == NULL ? "" : " -F ", |
1339 | config == NULL ? "" : config, | |
1340 | /* Optional "-v" arguments if -v set */ | |
1341 | debug_flag ? " -" : "", | |
1342 | debug_flag, "vvv", | |
1343 | /* Mandatory hostname */ | |
1344 | options.jump_host); | |
1345 | debug("Setting implicit ProxyCommand from ProxyJump: %s", | |
1346 | options.proxy_command); | |
1347 | } | |
1348 | ||
13f97b22 DM |
1349 | if (options.port == 0) |
1350 | options.port = default_ssh_port(); | |
dbee4119 | 1351 | channel_set_af(ssh, options.address_family); |
0a4f04b5 | 1352 | |
e9fc72ed DM |
1353 | /* Tidy and check options */ |
1354 | if (options.host_key_alias != NULL) | |
1355 | lowercase(options.host_key_alias); | |
1356 | if (options.proxy_command != NULL && | |
1357 | strcmp(options.proxy_command, "-") == 0 && | |
1358 | options.proxy_use_fdpass) | |
1359 | fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); | |
da22216b | 1360 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { |
1361 | if (options.control_persist && options.control_path != NULL) { | |
1362 | debug("UpdateHostKeys=ask is incompatible with " | |
1363 | "ControlPersist; disabling"); | |
1364 | options.update_hostkeys = 0; | |
1365 | } else if (sshbuf_len(command) != 0 || | |
1366 | options.remote_command != NULL || | |
1367 | options.request_tty == REQUEST_TTY_NO) { | |
1368 | debug("UpdateHostKeys=ask is incompatible with " | |
1369 | "remote command execution; disabling"); | |
1370 | options.update_hostkeys = 0; | |
156bef36 | 1371 | } else if (options.log_level < SYSLOG_LEVEL_INFO) { |
1372 | /* no point logging anything; user won't see it */ | |
1373 | options.update_hostkeys = 0; | |
da22216b | 1374 | } |
44732de0 | 1375 | } |
88b6fcde | 1376 | if (options.connection_attempts <= 0) |
1377 | fatal("Invalid number of ConnectionAttempts"); | |
e9fc72ed | 1378 | |
cecee2d6 | 1379 | if (sshbuf_len(command) != 0 && options.remote_command != NULL) |
1112b534 | 1380 | fatal("Cannot execute command-line and remote command."); |
1381 | ||
1382 | /* Cannot fork to background if no command. */ | |
a917e973 | 1383 | if (options.fork_after_authentication && sshbuf_len(command) == 0 && |
52c3b698 | 1384 | options.remote_command == NULL && |
1385 | options.session_type != SESSION_TYPE_NONE) | |
1112b534 | 1386 | fatal("Cannot fork into background without a command " |
1387 | "to execute."); | |
1388 | ||
95def098 | 1389 | /* reinit */ |
68d3a2a0 | 1390 | log_init(argv0, options.log_level, options.log_facility, !use_syslog); |
ea9e45c8 | 1391 | for (j = 0; j < options.num_log_verbose; j++) { |
1392 | if (strcasecmp(options.log_verbose[j], "none") == 0) | |
1393 | break; | |
793b583d | 1394 | log_verbose_add(options.log_verbose[j]); |
ea9e45c8 | 1395 | } |
95def098 | 1396 | |
fff9f095 DM |
1397 | if (options.request_tty == REQUEST_TTY_YES || |
1398 | options.request_tty == REQUEST_TTY_FORCE) | |
1399 | tty_flag = 1; | |
1400 | ||
1401 | /* Allocate a tty by default if no command specified. */ | |
cecee2d6 | 1402 | if (sshbuf_len(command) == 0 && options.remote_command == NULL) |
fff9f095 DM |
1403 | tty_flag = options.request_tty != REQUEST_TTY_NO; |
1404 | ||
1405 | /* Force no tty */ | |
8d057847 | 1406 | if (options.request_tty == REQUEST_TTY_NO || |
dc91ceea | 1407 | (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY) || |
1408 | options.session_type == SESSION_TYPE_NONE) | |
fff9f095 DM |
1409 | tty_flag = 0; |
1410 | /* Do not allocate a tty if stdin is not a tty. */ | |
e0c5088f | 1411 | if ((!isatty(fileno(stdin)) || options.stdin_null) && |
fff9f095 DM |
1412 | options.request_tty != REQUEST_TTY_FORCE) { |
1413 | if (tty_flag) | |
1414 | logit("Pseudo-terminal will not be allocated because " | |
1415 | "stdin is not a terminal."); | |
1416 | tty_flag = 0; | |
1417 | } | |
1418 | ||
b7548b12 | 1419 | /* Set up strings used to percent_expand() arguments */ |
d060bc7f | 1420 | cinfo = xcalloc(1, sizeof(*cinfo)); |
dfc85fa1 DM |
1421 | if (gethostname(thishost, sizeof(thishost)) == -1) |
1422 | fatal("gethostname: %s", strerror(errno)); | |
d060bc7f | 1423 | cinfo->thishost = xstrdup(thishost); |
1424 | thishost[strcspn(thishost, ".")] = '\0'; | |
1425 | cinfo->shorthost = xstrdup(thishost); | |
1426 | xasprintf(&cinfo->portstr, "%d", options.port); | |
1427 | xasprintf(&cinfo->uidstr, "%llu", | |
9c935dd9 | 1428 | (unsigned long long)pw->pw_uid); |
d060bc7f | 1429 | cinfo->keyalias = xstrdup(options.host_key_alias ? |
41f36dd8 | 1430 | options.host_key_alias : options.host_arg); |
41f36dd8 | 1431 | cinfo->host_arg = xstrdup(options.host_arg); |
d060bc7f | 1432 | cinfo->remhost = xstrdup(host); |
1433 | cinfo->remuser = xstrdup(options.user); | |
1434 | cinfo->homedir = xstrdup(pw->pw_dir); | |
1435 | cinfo->locuser = xstrdup(pw->pw_name); | |
98fc34df | 1436 | cinfo->jmphost = xstrdup(options.jump_host == NULL ? |
1437 | "" : options.jump_host); | |
f59a94e2 | 1438 | cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost, |
1439 | cinfo->remhost, cinfo->portstr, cinfo->remuser, cinfo->jmphost); | |
9c38643c | 1440 | |
b7548b12 | 1441 | /* |
1442 | * Expand tokens in arguments. NB. LocalCommand is expanded later, | |
1443 | * after port-forwarding is set up, so it may pick up any local | |
1444 | * tunnel interface name allocated. | |
1445 | */ | |
1112b534 | 1446 | if (options.remote_command != NULL) { |
1447 | debug3("expanding RemoteCommand: %s", options.remote_command); | |
1448 | cp = options.remote_command; | |
990687a0 | 1449 | options.remote_command = default_client_percent_expand(cp, |
d060bc7f | 1450 | cinfo); |
1112b534 | 1451 | debug3("expanded RemoteCommand: %s", options.remote_command); |
1452 | free(cp); | |
cecee2d6 | 1453 | if ((r = sshbuf_put(command, options.remote_command, |
1454 | strlen(options.remote_command))) != 0) | |
816036f1 | 1455 | fatal_fr(r, "buffer error"); |
1112b534 | 1456 | } |
1457 | ||
0e220dbf | 1458 | if (options.control_path != NULL) { |
e655ee04 | 1459 | cp = tilde_expand_filename(options.control_path, getuid()); |
a627d42e | 1460 | free(options.control_path); |
4a1b46e6 | 1461 | options.control_path = default_client_percent_dollar_expand(cp, |
d060bc7f | 1462 | cinfo); |
a627d42e | 1463 | free(cp); |
0e220dbf | 1464 | } |
9c38643c | 1465 | |
ed833da1 | 1466 | if (options.identity_agent != NULL) { |
1467 | p = tilde_expand_filename(options.identity_agent, getuid()); | |
d060bc7f | 1468 | cp = default_client_percent_dollar_expand(p, cinfo); |
ed833da1 | 1469 | free(p); |
1470 | free(options.identity_agent); | |
1471 | options.identity_agent = cp; | |
1472 | } | |
1473 | ||
f1a17de1 | 1474 | if (options.revoked_host_keys != NULL) { |
1475 | p = tilde_expand_filename(options.revoked_host_keys, getuid()); | |
1476 | cp = default_client_percent_dollar_expand(p, cinfo); | |
1477 | free(p); | |
1478 | free(options.revoked_host_keys); | |
1479 | options.revoked_host_keys = cp; | |
1480 | } | |
1481 | ||
ed833da1 | 1482 | if (options.forward_agent_sock_path != NULL) { |
1483 | p = tilde_expand_filename(options.forward_agent_sock_path, | |
1484 | getuid()); | |
d060bc7f | 1485 | cp = default_client_percent_dollar_expand(p, cinfo); |
ed833da1 | 1486 | free(p); |
1487 | free(options.forward_agent_sock_path); | |
1488 | options.forward_agent_sock_path = cp; | |
c3b16367 | 1489 | if (stat(options.forward_agent_sock_path, &st) != 0) { |
1490 | error("Cannot forward agent socket path \"%s\": %s", | |
1491 | options.forward_agent_sock_path, strerror(errno)); | |
1492 | if (options.exit_on_forward_failure) | |
1493 | cleanup_exit(255); | |
1494 | } | |
ed833da1 | 1495 | } |
1496 | ||
729b05f5 | 1497 | if (options.num_system_hostfiles > 0 && |
1498 | strcasecmp(options.system_hostfiles[0], "none") == 0) { | |
1499 | if (options.num_system_hostfiles > 1) | |
1500 | fatal("Invalid GlobalKnownHostsFiles: \"none\" " | |
1501 | "appears with other entries"); | |
1502 | free(options.system_hostfiles[0]); | |
1503 | options.system_hostfiles[0] = NULL; | |
1504 | options.num_system_hostfiles = 0; | |
1505 | } | |
1506 | ||
1507 | if (options.num_user_hostfiles > 0 && | |
1508 | strcasecmp(options.user_hostfiles[0], "none") == 0) { | |
1509 | if (options.num_user_hostfiles > 1) | |
1510 | fatal("Invalid UserKnownHostsFiles: \"none\" " | |
1511 | "appears with other entries"); | |
1512 | free(options.user_hostfiles[0]); | |
1513 | options.user_hostfiles[0] = NULL; | |
1514 | options.num_user_hostfiles = 0; | |
1515 | } | |
c4f23994 | 1516 | for (j = 0; j < options.num_user_hostfiles; j++) { |
729b05f5 | 1517 | if (options.user_hostfiles[j] == NULL) |
1518 | continue; | |
1519 | cp = tilde_expand_filename(options.user_hostfiles[j], getuid()); | |
1520 | p = default_client_percent_dollar_expand(cp, cinfo); | |
1521 | if (strcmp(options.user_hostfiles[j], p) != 0) | |
1522 | debug3("expanded UserKnownHostsFile '%s' -> " | |
1523 | "'%s'", options.user_hostfiles[j], p); | |
1524 | free(options.user_hostfiles[j]); | |
1525 | free(cp); | |
1526 | options.user_hostfiles[j] = p; | |
c4f23994 | 1527 | } |
1528 | ||
990687a0 | 1529 | for (i = 0; i < options.num_local_forwards; i++) { |
1530 | if (options.local_forwards[i].listen_path != NULL) { | |
1531 | cp = options.local_forwards[i].listen_path; | |
1532 | p = options.local_forwards[i].listen_path = | |
d060bc7f | 1533 | default_client_percent_expand(cp, cinfo); |
990687a0 | 1534 | if (strcmp(cp, p) != 0) |
1535 | debug3("expanded LocalForward listen path " | |
1536 | "'%s' -> '%s'", cp, p); | |
1537 | free(cp); | |
1538 | } | |
1539 | if (options.local_forwards[i].connect_path != NULL) { | |
1540 | cp = options.local_forwards[i].connect_path; | |
1541 | p = options.local_forwards[i].connect_path = | |
d060bc7f | 1542 | default_client_percent_expand(cp, cinfo); |
990687a0 | 1543 | if (strcmp(cp, p) != 0) |
1544 | debug3("expanded LocalForward connect path " | |
1545 | "'%s' -> '%s'", cp, p); | |
1546 | free(cp); | |
1547 | } | |
1548 | } | |
1549 | ||
1550 | for (i = 0; i < options.num_remote_forwards; i++) { | |
1551 | if (options.remote_forwards[i].listen_path != NULL) { | |
1552 | cp = options.remote_forwards[i].listen_path; | |
1553 | p = options.remote_forwards[i].listen_path = | |
d060bc7f | 1554 | default_client_percent_expand(cp, cinfo); |
990687a0 | 1555 | if (strcmp(cp, p) != 0) |
1556 | debug3("expanded RemoteForward listen path " | |
1557 | "'%s' -> '%s'", cp, p); | |
1558 | free(cp); | |
1559 | } | |
1560 | if (options.remote_forwards[i].connect_path != NULL) { | |
1561 | cp = options.remote_forwards[i].connect_path; | |
1562 | p = options.remote_forwards[i].connect_path = | |
d060bc7f | 1563 | default_client_percent_expand(cp, cinfo); |
990687a0 | 1564 | if (strcmp(cp, p) != 0) |
1565 | debug3("expanded RemoteForward connect path " | |
1566 | "'%s' -> '%s'", cp, p); | |
1567 | free(cp); | |
1568 | } | |
1569 | } | |
1570 | ||
957fbceb | 1571 | if (config_test) { |
1572 | dump_client_config(&options, host); | |
1573 | exit(0); | |
1574 | } | |
1575 | ||
884416bd | 1576 | /* Expand SecurityKeyProvider if it refers to an environment variable */ |
1577 | if (options.sk_provider != NULL && *options.sk_provider == '$' && | |
1578 | strlen(options.sk_provider) > 1) { | |
1579 | if ((cp = getenv(options.sk_provider + 1)) == NULL) { | |
a47f6a6c | 1580 | debug("Authenticator provider %s did not resolve; " |
884416bd | 1581 | "disabling", options.sk_provider); |
1582 | free(options.sk_provider); | |
1583 | options.sk_provider = NULL; | |
1584 | } else { | |
1585 | debug2("resolved SecurityKeyProvider %s => %s", | |
1586 | options.sk_provider, cp); | |
1587 | free(options.sk_provider); | |
1588 | options.sk_provider = xstrdup(cp); | |
1589 | } | |
1590 | } | |
1591 | ||
b1cbfa25 | 1592 | if (muxclient_command != 0 && options.control_path == NULL) |
0814d313 | 1593 | fatal("No ControlPath specified for \"-O\" command"); |
8d057847 | 1594 | if (options.control_path != NULL) { |
1595 | int sock; | |
1596 | if ((sock = muxclient(options.control_path)) >= 0) { | |
dbee4119 | 1597 | ssh_packet_set_connection(ssh, sock, sock); |
25b2ed66 | 1598 | ssh_packet_set_mux(ssh); |
8d057847 | 1599 | goto skip_connect; |
1600 | } | |
1601 | } | |
0e220dbf | 1602 | |
08b57c67 DM |
1603 | /* |
1604 | * If hostname canonicalisation was not enabled, then we may not | |
1605 | * have yet resolved the hostname. Do so now. | |
1606 | */ | |
1607 | if (addrs == NULL && options.proxy_command == NULL) { | |
a85768a9 | 1608 | debug2("resolving \"%s\" port %d", host, options.port); |
08b57c67 DM |
1609 | if ((addrs = resolve_host(host, options.port, 1, |
1610 | cname, sizeof(cname))) == NULL) | |
1611 | cleanup_exit(255); /* resolve_host logs the error */ | |
1612 | } | |
1613 | ||
819b44e8 | 1614 | if (options.connection_timeout >= INT_MAX/1000) |
1615 | timeout_ms = INT_MAX; | |
1616 | else | |
1617 | timeout_ms = options.connection_timeout * 1000; | |
67bd062b | 1618 | |
a752a6c0 | 1619 | /* Apply channels timeouts, if set */ |
1620 | channel_clear_timeouts(ssh); | |
1621 | for (j = 0; j < options.num_channel_timeouts; j++) { | |
1622 | debug3("applying channel timeout %s", | |
1623 | options.channel_timeouts[j]); | |
1624 | if (parse_pattern_interval(options.channel_timeouts[j], | |
1625 | &cp, &i) != 0) { | |
1626 | fatal_f("internal error: bad timeout %s", | |
1627 | options.channel_timeouts[j]); | |
1628 | } | |
1629 | channel_add_timeout(ssh, cp, i); | |
1630 | free(cp); | |
1631 | } | |
1632 | ||
fcec7f82 | 1633 | /* Open a connection to the remote host. */ |
41f36dd8 | 1634 | if (ssh_connect(ssh, host, options.host_arg, addrs, &hostaddr, |
1635 | options.port, options.connection_attempts, | |
95d41e90 | 1636 | &timeout_ms, options.tcp_keep_alive) != 0) |
57ed647e | 1637 | exit(255); |
0faf747e | 1638 | |
28631cea DM |
1639 | if (addrs != NULL) |
1640 | freeaddrinfo(addrs); | |
1641 | ||
25b2ed66 | 1642 | ssh_packet_set_timeout(ssh, options.server_alive_interval, |
0faf747e | 1643 | options.server_alive_count_max); |
95def098 | 1644 | |
67bd062b DM |
1645 | if (timeout_ms > 0) |
1646 | debug3("timeout: %d ms remain after connect", timeout_ms); | |
1647 | ||
5428f646 | 1648 | /* |
26efc2f5 | 1649 | * If we successfully made the connection and we have hostbased auth |
1650 | * enabled, load the public keys so we can later use the ssh-keysign | |
1651 | * helper to sign challenges. | |
5428f646 | 1652 | */ |
5eabda30 BL |
1653 | sensitive_data.nkeys = 0; |
1654 | sensitive_data.keys = NULL; | |
873d3e7d | 1655 | if (options.hostbased_authentication) { |
4403b62f | 1656 | int loaded = 0; |
1657 | ||
ac590760 | 1658 | sensitive_data.nkeys = 10; |
ddd63ab1 | 1659 | sensitive_data.keys = xcalloc(sensitive_data.nkeys, |
18376847 | 1660 | sizeof(*sensitive_data.keys)); |
5467fbcb | 1661 | |
1662 | /* XXX check errors? */ | |
ac590760 | 1663 | #define L_PUBKEY(p,o) do { \ |
1664 | if ((o) >= sensitive_data.nkeys) \ | |
816036f1 | 1665 | fatal_f("pubkey out of array bounds"); \ |
5467fbcb | 1666 | check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \ |
54b333d1 | 1667 | &(sensitive_data.keys[o]), p, "pubkey"); \ |
4403b62f | 1668 | if (sensitive_data.keys[o] != NULL) { \ |
11e8c430 | 1669 | debug2("hostbased key %d: %s key from \"%s\"", o, \ |
1670 | sshkey_ssh_name(sensitive_data.keys[o]), p); \ | |
4403b62f | 1671 | loaded++; \ |
1672 | } \ | |
ac590760 | 1673 | } while (0) |
1674 | #define L_CERT(p,o) do { \ | |
1675 | if ((o) >= sensitive_data.nkeys) \ | |
816036f1 | 1676 | fatal_f("cert out of array bounds"); \ |
54b333d1 | 1677 | check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \ |
1678 | &(sensitive_data.keys[o]), p, "cert"); \ | |
4403b62f | 1679 | if (sensitive_data.keys[o] != NULL) { \ |
11e8c430 | 1680 | debug2("hostbased key %d: %s cert from \"%s\"", o, \ |
1681 | sshkey_ssh_name(sensitive_data.keys[o]), p); \ | |
4403b62f | 1682 | loaded++; \ |
1683 | } \ | |
ac590760 | 1684 | } while (0) |
f9c4884c | 1685 | |
26efc2f5 | 1686 | if (options.hostbased_authentication == 1) { |
ac590760 | 1687 | L_CERT(_PATH_HOST_ECDSA_KEY_FILE, 0); |
1688 | L_CERT(_PATH_HOST_ED25519_KEY_FILE, 1); | |
1689 | L_CERT(_PATH_HOST_RSA_KEY_FILE, 2); | |
4e838120 | 1690 | #ifdef WITH_DSA |
ac590760 | 1691 | L_CERT(_PATH_HOST_DSA_KEY_FILE, 3); |
4e838120 | 1692 | #endif |
ac590760 | 1693 | L_PUBKEY(_PATH_HOST_ECDSA_KEY_FILE, 4); |
1694 | L_PUBKEY(_PATH_HOST_ED25519_KEY_FILE, 5); | |
1695 | L_PUBKEY(_PATH_HOST_RSA_KEY_FILE, 6); | |
4e838120 | 1696 | #ifdef WITH_DSA |
ac590760 | 1697 | L_PUBKEY(_PATH_HOST_DSA_KEY_FILE, 7); |
4e838120 | 1698 | #endif |
ac590760 | 1699 | L_CERT(_PATH_HOST_XMSS_KEY_FILE, 8); |
1700 | L_PUBKEY(_PATH_HOST_XMSS_KEY_FILE, 9); | |
4403b62f | 1701 | if (loaded == 0) |
1702 | debug("HostbasedAuthentication enabled but no " | |
1703 | "local public host keys could be loaded."); | |
1bad2568 | 1704 | } |
95def098 | 1705 | } |
95def098 | 1706 | |
266dfdfd | 1707 | /* load options.identity_files */ |
d060bc7f | 1708 | load_public_identity_files(cinfo); |
266dfdfd | 1709 | |
001aa554 | 1710 | /* optionally set the SSH_AUTHSOCKET_ENV_NAME variable */ |
1a75d14d | 1711 | if (options.identity_agent && |
1712 | strcmp(options.identity_agent, SSH_AUTHSOCKET_ENV_NAME) != 0) { | |
b02ad1ce | 1713 | if (strcmp(options.identity_agent, "none") == 0) { |
1714 | unsetenv(SSH_AUTHSOCKET_ENV_NAME); | |
1715 | } else { | |
ed833da1 | 1716 | cp = options.identity_agent; |
4a1b46e6 | 1717 | /* legacy (limited) format */ |
1718 | if (cp[0] == '$' && cp[1] != '{') { | |
5eff5b85 | 1719 | if (!valid_env_name(cp + 1)) { |
1720 | fatal("Invalid IdentityAgent " | |
1721 | "environment variable name %s", cp); | |
1722 | } | |
1723 | if ((p = getenv(cp + 1)) == NULL) | |
1724 | unsetenv(SSH_AUTHSOCKET_ENV_NAME); | |
1725 | else | |
1726 | setenv(SSH_AUTHSOCKET_ENV_NAME, p, 1); | |
1727 | } else { | |
1728 | /* identity_agent specifies a path directly */ | |
1729 | setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1); | |
1730 | } | |
b02ad1ce | 1731 | } |
1732 | } | |
1733 | ||
ed833da1 | 1734 | if (options.forward_agent && options.forward_agent_sock_path != NULL) { |
81624026 | 1735 | cp = options.forward_agent_sock_path; |
40be78f5 | 1736 | if (cp[0] == '$') { |
1737 | if (!valid_env_name(cp + 1)) { | |
1738 | fatal("Invalid ForwardAgent environment variable name %s", cp); | |
1739 | } | |
1740 | if ((p = getenv(cp + 1)) != NULL) | |
724eb900 | 1741 | forward_agent_sock_path = xstrdup(p); |
40be78f5 | 1742 | else |
1743 | options.forward_agent = 0; | |
1744 | free(cp); | |
1745 | } else { | |
1746 | forward_agent_sock_path = cp; | |
1747 | } | |
1748 | } | |
1749 | ||
266dfdfd | 1750 | /* Expand ~ in known host file names. */ |
295ee63a DM |
1751 | tilde_expand_paths(options.system_hostfiles, |
1752 | options.num_system_hostfiles); | |
1753 | tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles); | |
95def098 | 1754 | |
3bf2a6ac | 1755 | ssh_signal(SIGCHLD, main_sigchld_handler); |
07cd5897 | 1756 | |
d6173c0b | 1757 | /* Log into the remote system. Never returns if the login fails. */ |
0a843d9a | 1758 | ssh_login(ssh, &sensitive_data, host, (struct sockaddr *)&hostaddr, |
0f504f59 | 1759 | options.port, pw, timeout_ms, cinfo); |
5eabda30 BL |
1760 | |
1761 | /* We no longer need the private host keys. Clear them now. */ | |
1762 | if (sensitive_data.nkeys != 0) { | |
1763 | for (i = 0; i < sensitive_data.nkeys; i++) { | |
1764 | if (sensitive_data.keys[i] != NULL) { | |
1765 | /* Destroys contents safely */ | |
1766 | debug3("clear hostkey %d", i); | |
5467fbcb | 1767 | sshkey_free(sensitive_data.keys[i]); |
5eabda30 BL |
1768 | sensitive_data.keys[i] = NULL; |
1769 | } | |
1770 | } | |
a627d42e | 1771 | free(sensitive_data.keys); |
5eabda30 | 1772 | } |
a6c8a8d4 | 1773 | for (i = 0; i < options.num_identity_files; i++) { |
a627d42e DT |
1774 | free(options.identity_files[i]); |
1775 | options.identity_files[i] = NULL; | |
a6c8a8d4 | 1776 | if (options.identity_keys[i]) { |
5467fbcb | 1777 | sshkey_free(options.identity_keys[i]); |
a6c8a8d4 BL |
1778 | options.identity_keys[i] = NULL; |
1779 | } | |
1780 | } | |
4e44a79a | 1781 | for (i = 0; i < options.num_certificate_files; i++) { |
1782 | free(options.certificate_files[i]); | |
1783 | options.certificate_files[i] = NULL; | |
1784 | } | |
95def098 | 1785 | |
082804c1 | 1786 | #ifdef ENABLE_PKCS11 |
1787 | (void)pkcs11_del_provider(options.pkcs11_provider); | |
1788 | #endif | |
1789 | ||
8d057847 | 1790 | skip_connect: |
d060bc7f | 1791 | exit_status = ssh_session2(ssh, cinfo); |
1792 | ssh_conn_info_free(cinfo); | |
25b2ed66 | 1793 | ssh_packet_close(ssh); |
8c4e18a6 | 1794 | |
b1cbfa25 | 1795 | if (options.control_path != NULL && muxserver_sock != -1) |
0e220dbf DM |
1796 | unlink(options.control_path); |
1797 | ||
a41ccca6 DM |
1798 | /* Kill ProxyCommand if it is running. */ |
1799 | ssh_kill_proxy_command(); | |
8c4e18a6 | 1800 | |
1383bd8e DM |
1801 | return exit_status; |
1802 | } | |
1803 | ||
e11e1ea5 DM |
1804 | static void |
1805 | control_persist_detach(void) | |
1806 | { | |
1807 | pid_t pid; | |
1808 | ||
816036f1 | 1809 | debug_f("backgrounding master process"); |
e11e1ea5 | 1810 | |
b8bbff3b | 1811 | /* |
1812 | * master (current process) into the background, and make the | |
1813 | * foreground process a client of the backgrounded master. | |
1814 | */ | |
e11e1ea5 DM |
1815 | switch ((pid = fork())) { |
1816 | case -1: | |
816036f1 | 1817 | fatal_f("fork: %s", strerror(errno)); |
e11e1ea5 DM |
1818 | case 0: |
1819 | /* Child: master process continues mainloop */ | |
b8bbff3b | 1820 | break; |
1821 | default: | |
2d34205d | 1822 | /* |
1823 | * Parent: set up mux client to connect to backgrounded | |
1824 | * master. | |
1825 | */ | |
816036f1 | 1826 | debug2_f("background process is %ld", (long)pid); |
e0c5088f | 1827 | options.stdin_null = ostdin_null_flag; |
21771e22 | 1828 | options.request_tty = orequest_tty; |
e11e1ea5 | 1829 | tty_flag = otty_flag; |
2d34205d | 1830 | options.fork_after_authentication = ofork_after_authentication; |
43ec991a | 1831 | options.session_type = osession_type; |
b8bbff3b | 1832 | close(muxserver_sock); |
1833 | muxserver_sock = -1; | |
5929c52f | 1834 | options.control_master = SSHCTL_MASTER_NO; |
7174ba6f | 1835 | (void)muxclient(options.control_path); |
e11e1ea5 | 1836 | /* muxclient() doesn't return on success. */ |
b8bbff3b | 1837 | fatal("Failed to connect to new control master"); |
1838 | } | |
396d32f3 | 1839 | if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) |
816036f1 | 1840 | error_f("stdfd_devnull failed"); |
98e27dcf | 1841 | daemon(1, 1); |
ea2c1a4d | 1842 | setproctitle("%s [mux]", options.control_path); |
e11e1ea5 DM |
1843 | } |
1844 | ||
1845 | /* Do fork() after authentication. Used by "ssh -f" */ | |
1846 | static void | |
1847 | fork_postauth(void) | |
1848 | { | |
1849 | if (need_controlpersist_detach) | |
1850 | control_persist_detach(); | |
1851 | debug("forking to background"); | |
a917e973 | 1852 | options.fork_after_authentication = 0; |
4d28fa78 | 1853 | if (daemon(1, 1) == -1) |
e11e1ea5 | 1854 | fatal("daemon() failed: %.200s", strerror(errno)); |
396d32f3 | 1855 | if (stdfd_devnull(1, 1, !(log_is_on_stderr() && debug_flag)) == -1) |
816036f1 | 1856 | error_f("stdfd_devnull failed"); |
e11e1ea5 DM |
1857 | } |
1858 | ||
663e84bb | 1859 | static void |
1860 | forwarding_success(void) | |
1861 | { | |
7b4d8999 | 1862 | if (forward_confirms_pending == -1) |
1863 | return; | |
1864 | if (--forward_confirms_pending == 0) { | |
816036f1 | 1865 | debug_f("all expected forwarding replies received"); |
a917e973 | 1866 | if (options.fork_after_authentication) |
663e84bb | 1867 | fork_postauth(); |
7b4d8999 | 1868 | } else { |
816036f1 | 1869 | debug2_f("%d expected forwarding replies remaining", |
1870 | forward_confirms_pending); | |
663e84bb | 1871 | } |
1872 | } | |
1873 | ||
9f407c44 DT |
1874 | /* Callback for remote forward global requests */ |
1875 | static void | |
dbee4119 | 1876 | ssh_confirm_remote_forward(struct ssh *ssh, int type, u_int32_t seq, void *ctxt) |
9f407c44 | 1877 | { |
7acefbbc | 1878 | struct Forward *rfwd = (struct Forward *)ctxt; |
25b2ed66 | 1879 | u_int port; |
1880 | int r; | |
9f407c44 | 1881 | |
4bf648f7 | 1882 | /* XXX verbose() on failure? */ |
4b3ed647 | 1883 | debug("remote forward %s for: listen %s%s%d, connect %s:%d", |
9f407c44 | 1884 | type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", |
7acefbbc DM |
1885 | rfwd->listen_path ? rfwd->listen_path : |
1886 | rfwd->listen_host ? rfwd->listen_host : "", | |
1887 | (rfwd->listen_path || rfwd->listen_host) ? ":" : "", | |
1888 | rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : | |
1889 | rfwd->connect_host, rfwd->connect_port); | |
1890 | if (rfwd->listen_path == NULL && rfwd->listen_port == 0) { | |
68afb8c5 | 1891 | if (type == SSH2_MSG_REQUEST_SUCCESS) { |
25b2ed66 | 1892 | if ((r = sshpkt_get_u32(ssh, &port)) != 0) |
816036f1 | 1893 | fatal_fr(r, "parse packet"); |
25b2ed66 | 1894 | if (port > 65535) { |
1895 | error("Invalid allocated port %u for remote " | |
1896 | "forward to %s:%d", port, | |
1897 | rfwd->connect_host, rfwd->connect_port); | |
1898 | /* Ensure failure processing runs below */ | |
1899 | type = SSH2_MSG_REQUEST_FAILURE; | |
1900 | channel_update_permission(ssh, | |
1901 | rfwd->handle, -1); | |
1902 | } else { | |
1903 | rfwd->allocated_port = (int)port; | |
1904 | logit("Allocated port %u for remote " | |
1905 | "forward to %s:%d", | |
8c071701 | 1906 | rfwd->allocated_port, rfwd->connect_path ? |
1907 | rfwd->connect_path : rfwd->connect_host, | |
25b2ed66 | 1908 | rfwd->connect_port); |
1909 | channel_update_permission(ssh, | |
1910 | rfwd->handle, rfwd->allocated_port); | |
1911 | } | |
68afb8c5 | 1912 | } else { |
115063a6 | 1913 | channel_update_permission(ssh, rfwd->handle, -1); |
68afb8c5 | 1914 | } |
4bf648f7 | 1915 | } |
dbe0662e | 1916 | |
9f407c44 | 1917 | if (type == SSH2_MSG_REQUEST_FAILURE) { |
7acefbbc DM |
1918 | if (options.exit_on_forward_failure) { |
1919 | if (rfwd->listen_path != NULL) | |
1920 | fatal("Error: remote port forwarding failed " | |
1921 | "for listen path %s", rfwd->listen_path); | |
1922 | else | |
1923 | fatal("Error: remote port forwarding failed " | |
1924 | "for listen port %d", rfwd->listen_port); | |
1925 | } else { | |
1926 | if (rfwd->listen_path != NULL) | |
1927 | logit("Warning: remote port forwarding failed " | |
1928 | "for listen path %s", rfwd->listen_path); | |
1929 | else | |
1930 | logit("Warning: remote port forwarding failed " | |
1931 | "for listen port %d", rfwd->listen_port); | |
1932 | } | |
9f407c44 | 1933 | } |
663e84bb | 1934 | forwarding_success(); |
9f407c44 DT |
1935 | } |
1936 | ||
7ad8dd21 | 1937 | static void |
c6043815 | 1938 | client_cleanup_stdio_fwd(struct ssh *ssh, int id, int force, void *arg) |
7ad8dd21 DT |
1939 | { |
1940 | debug("stdio forwarding: done"); | |
1941 | cleanup_exit(0); | |
1942 | } | |
1943 | ||
357610d1 | 1944 | static void |
dbee4119 | 1945 | ssh_stdio_confirm(struct ssh *ssh, int id, int success, void *arg) |
357610d1 DM |
1946 | { |
1947 | if (!success) | |
1948 | fatal("stdio forwarding failed"); | |
1949 | } | |
1950 | ||
663e84bb | 1951 | static void |
1952 | ssh_tun_confirm(struct ssh *ssh, int id, int success, void *arg) | |
1953 | { | |
1954 | if (!success) { | |
1955 | error("Tunnel forwarding failed"); | |
1956 | if (options.exit_on_forward_failure) | |
1957 | cleanup_exit(255); | |
1958 | } | |
1959 | ||
816036f1 | 1960 | debug_f("tunnel forward established, id=%d", id); |
663e84bb | 1961 | forwarding_success(); |
1962 | } | |
1963 | ||
2d6665d9 | 1964 | static void |
dbee4119 | 1965 | ssh_init_stdio_forwarding(struct ssh *ssh) |
7ad8dd21 DT |
1966 | { |
1967 | Channel *c; | |
e1537f95 | 1968 | int in, out; |
7ad8dd21 | 1969 | |
8543ff3f | 1970 | if (options.stdio_forward_host == NULL) |
2d6665d9 | 1971 | return; |
e1537f95 | 1972 | |
816036f1 | 1973 | debug3_f("%s:%d", options.stdio_forward_host, |
8543ff3f | 1974 | options.stdio_forward_port); |
e1537f95 | 1975 | |
4d28fa78 | 1976 | if ((in = dup(STDIN_FILENO)) == -1 || |
1977 | (out = dup(STDOUT_FILENO)) == -1) | |
7be4ac81 | 1978 | fatal_f("dup() in/out failed"); |
dbee4119 | 1979 | if ((c = channel_connect_stdio_fwd(ssh, options.stdio_forward_host, |
7be4ac81 | 1980 | options.stdio_forward_port, in, out, |
1981 | CHANNEL_NONBLOCK_STDIO)) == NULL) | |
816036f1 | 1982 | fatal_f("channel_connect_stdio_fwd failed"); |
dbee4119 | 1983 | channel_register_cleanup(ssh, c->self, client_cleanup_stdio_fwd, 0); |
1984 | channel_register_open_confirm(ssh, c->self, ssh_stdio_confirm, NULL); | |
7ad8dd21 DT |
1985 | } |
1986 | ||
da0a9afc | 1987 | static void |
1988 | ssh_init_forward_permissions(struct ssh *ssh, const char *what, char **opens, | |
1989 | u_int num_opens) | |
1990 | { | |
1991 | u_int i; | |
1992 | int port; | |
45279abc | 1993 | char *addr, *arg, *oarg; |
da0a9afc | 1994 | int where = FORWARD_LOCAL; |
1995 | ||
1996 | channel_clear_permission(ssh, FORWARD_ADM, where); | |
1997 | if (num_opens == 0) | |
1998 | return; /* permit any */ | |
1999 | ||
2000 | /* handle keywords: "any" / "none" */ | |
2001 | if (num_opens == 1 && strcmp(opens[0], "any") == 0) | |
2002 | return; | |
2003 | if (num_opens == 1 && strcmp(opens[0], "none") == 0) { | |
2004 | channel_disable_admin(ssh, where); | |
2005 | return; | |
2006 | } | |
2007 | /* Otherwise treat it as a list of permitted host:port */ | |
2008 | for (i = 0; i < num_opens; i++) { | |
2009 | oarg = arg = xstrdup(opens[i]); | |
45279abc | 2010 | addr = hpdelim(&arg); |
2011 | if (addr == NULL) | |
da0a9afc | 2012 | fatal_f("missing host in %s", what); |
2013 | addr = cleanhostname(addr); | |
2014 | if (arg == NULL || ((port = permitopen_port(arg)) < 0)) | |
2015 | fatal_f("bad port number in %s", what); | |
2016 | /* Send it to channels layer */ | |
2017 | channel_add_permission(ssh, FORWARD_ADM, | |
2018 | where, addr, port); | |
2019 | free(oarg); | |
2020 | } | |
2021 | } | |
2022 | ||
bba81213 | 2023 | static void |
b7548b12 | 2024 | ssh_init_forwarding(struct ssh *ssh, char **ifname) |
0bc1bd81 | 2025 | { |
12057500 | 2026 | int success = 0; |
0bc1bd81 | 2027 | int i; |
12057500 | 2028 | |
da0a9afc | 2029 | ssh_init_forward_permissions(ssh, "permitremoteopen", |
2030 | options.permitted_remote_opens, | |
2031 | options.num_permitted_remote_opens); | |
2032 | ||
7b4d8999 | 2033 | if (options.exit_on_forward_failure) |
2034 | forward_confirms_pending = 0; /* track pending requests */ | |
0bc1bd81 DM |
2035 | /* Initiate local TCP/IP port forwardings. */ |
2036 | for (i = 0; i < options.num_local_forwards; i++) { | |
f91ee4c3 DM |
2037 | debug("Local connections to %.200s:%d forwarded to remote " |
2038 | "address %.200s:%d", | |
7acefbbc DM |
2039 | (options.local_forwards[i].listen_path != NULL) ? |
2040 | options.local_forwards[i].listen_path : | |
47eede77 | 2041 | (options.local_forwards[i].listen_host == NULL) ? |
7acefbbc | 2042 | (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : |
f91ee4c3 DM |
2043 | options.local_forwards[i].listen_host, |
2044 | options.local_forwards[i].listen_port, | |
7acefbbc DM |
2045 | (options.local_forwards[i].connect_path != NULL) ? |
2046 | options.local_forwards[i].connect_path : | |
f91ee4c3 DM |
2047 | options.local_forwards[i].connect_host, |
2048 | options.local_forwards[i].connect_port); | |
dbee4119 | 2049 | success += channel_setup_local_fwd_listener(ssh, |
7acefbbc | 2050 | &options.local_forwards[i], &options.fwd_opts); |
0bc1bd81 | 2051 | } |
e7d4b19f DT |
2052 | if (i > 0 && success != i && options.exit_on_forward_failure) |
2053 | fatal("Could not request local forwarding."); | |
12057500 KS |
2054 | if (i > 0 && success == 0) |
2055 | error("Could not request local forwarding."); | |
0bc1bd81 DM |
2056 | |
2057 | /* Initiate remote TCP/IP port forwardings. */ | |
2058 | for (i = 0; i < options.num_remote_forwards; i++) { | |
f91ee4c3 DM |
2059 | debug("Remote connections from %.200s:%d forwarded to " |
2060 | "local address %.200s:%d", | |
7acefbbc DM |
2061 | (options.remote_forwards[i].listen_path != NULL) ? |
2062 | options.remote_forwards[i].listen_path : | |
46d38de4 | 2063 | (options.remote_forwards[i].listen_host == NULL) ? |
aa3bb109 | 2064 | "LOCALHOST" : options.remote_forwards[i].listen_host, |
f91ee4c3 | 2065 | options.remote_forwards[i].listen_port, |
7acefbbc DM |
2066 | (options.remote_forwards[i].connect_path != NULL) ? |
2067 | options.remote_forwards[i].connect_path : | |
f91ee4c3 DM |
2068 | options.remote_forwards[i].connect_host, |
2069 | options.remote_forwards[i].connect_port); | |
663e84bb | 2070 | if ((options.remote_forwards[i].handle = |
dbee4119 | 2071 | channel_request_remote_forwarding(ssh, |
663e84bb | 2072 | &options.remote_forwards[i])) >= 0) { |
dbee4119 | 2073 | client_register_global_confirm( |
2074 | ssh_confirm_remote_forward, | |
68afb8c5 | 2075 | &options.remote_forwards[i]); |
663e84bb | 2076 | forward_confirms_pending++; |
2077 | } else if (options.exit_on_forward_failure) | |
2078 | fatal("Could not request remote forwarding."); | |
2079 | else | |
2080 | logit("Warning: Could not request remote forwarding."); | |
0bc1bd81 | 2081 | } |
b3ce9fec DM |
2082 | |
2083 | /* Initiate tunnel forwarding. */ | |
2084 | if (options.tun_open != SSH_TUNMODE_NO) { | |
b7548b12 | 2085 | if ((*ifname = client_request_tun_fwd(ssh, |
2086 | options.tun_open, options.tun_local, | |
663e84bb | 2087 | options.tun_remote, ssh_tun_confirm, NULL)) != NULL) |
2088 | forward_confirms_pending++; | |
2089 | else if (options.exit_on_forward_failure) | |
2090 | fatal("Could not request tunnel forwarding."); | |
2091 | else | |
2092 | error("Could not request tunnel forwarding."); | |
dbe0662e | 2093 | } |
7b4d8999 | 2094 | if (forward_confirms_pending > 0) { |
816036f1 | 2095 | debug_f("expecting replies for %d forwards", |
7b4d8999 | 2096 | forward_confirms_pending); |
2097 | } | |
0bc1bd81 DM |
2098 | } |
2099 | ||
bba81213 | 2100 | static void |
0bc1bd81 DM |
2101 | check_agent_present(void) |
2102 | { | |
141efe49 | 2103 | int r; |
2104 | ||
0bc1bd81 | 2105 | if (options.forward_agent) { |
788f212a | 2106 | /* Clear agent forwarding if we don't have an agent. */ |
141efe49 | 2107 | if ((r = ssh_get_authentication_socket(NULL)) != 0) { |
0bc1bd81 | 2108 | options.forward_agent = 0; |
141efe49 | 2109 | if (r != SSH_ERR_AGENT_NOT_PRESENT) |
816036f1 | 2110 | debug_r(r, "ssh_get_authentication_socket"); |
141efe49 | 2111 | } |
0bc1bd81 DM |
2112 | } |
2113 | } | |
2114 | ||
0e220dbf | 2115 | static void |
dbee4119 | 2116 | ssh_session2_setup(struct ssh *ssh, int id, int success, void *arg) |
0e220dbf | 2117 | { |
3756dcee | 2118 | extern char **environ; |
f64f8c00 | 2119 | const char *display, *term; |
25b2ed66 | 2120 | int r, interactive = tty_flag; |
ed4ce82d | 2121 | char *proto = NULL, *data = NULL; |
17e7ed0e | 2122 | |
d530f5f4 | 2123 | if (!success) |
1c2be7c2 | 2124 | return; /* No need for error message, channels code sends one */ |
d530f5f4 | 2125 | |
46d38de4 | 2126 | display = getenv("DISPLAY"); |
a58be33c | 2127 | if (display == NULL && options.forward_x11) |
2128 | debug("X11 forwarding requested but DISPLAY not set"); | |
dbee4119 | 2129 | if (options.forward_x11 && client_x11_get_proto(ssh, display, |
ed4ce82d | 2130 | options.xauth_location, options.forward_x11_trusted, |
2131 | options.forward_x11_timeout, &proto, &data) == 0) { | |
bd483e76 | 2132 | /* Request forwarding with authentication spoofing. */ |
d6173c0b DT |
2133 | debug("Requesting X11 forwarding with authentication " |
2134 | "spoofing."); | |
dbee4119 | 2135 | x11_request_forwarding_with_spoofing(ssh, id, display, proto, |
6d7b4377 | 2136 | data, 1); |
dbee4119 | 2137 | client_expect_confirm(ssh, id, "X11 forwarding", CONFIRM_WARN); |
6d7b4377 | 2138 | /* XXX exit_on_forward_failure */ |
bf555ba6 | 2139 | interactive = 1; |
bd483e76 DM |
2140 | } |
2141 | ||
0bc1bd81 DM |
2142 | check_agent_present(); |
2143 | if (options.forward_agent) { | |
2144 | debug("Requesting authentication agent forwarding."); | |
dbee4119 | 2145 | channel_request_start(ssh, id, "auth-agent-req@openssh.com", 0); |
25b2ed66 | 2146 | if ((r = sshpkt_send(ssh)) != 0) |
816036f1 | 2147 | fatal_fr(r, "send packet"); |
0bc1bd81 DM |
2148 | } |
2149 | ||
7b30501b | 2150 | /* Tell the packet module whether this is an interactive session. */ |
25b2ed66 | 2151 | ssh_packet_set_interactive(ssh, interactive, |
7b30501b DT |
2152 | options.ip_qos_interactive, options.ip_qos_bulk); |
2153 | ||
f64f8c00 | 2154 | if ((term = lookup_env_in_list("TERM", options.setenv, |
2155 | options.num_setenv)) == NULL || *term == '\0') | |
2156 | term = getenv("TERM"); | |
52c3b698 | 2157 | client_session2_setup(ssh, id, tty_flag, |
2158 | options.session_type == SESSION_TYPE_SUBSYSTEM, term, | |
cecee2d6 | 2159 | NULL, fileno(stdin), command, environ); |
1383bd8e DM |
2160 | } |
2161 | ||
f558cf65 | 2162 | /* open new channel for a session */ |
bba81213 | 2163 | static int |
dbee4119 | 2164 | ssh_session2_open(struct ssh *ssh) |
1383bd8e | 2165 | { |
99c73b37 BL |
2166 | Channel *c; |
2167 | int window, packetmax, in, out, err; | |
ad833b3e | 2168 | |
e0c5088f | 2169 | if (options.stdin_null) { |
31ca54aa | 2170 | in = open(_PATH_DEVNULL, O_RDONLY); |
caf6dd6d DM |
2171 | } else { |
2172 | in = dup(STDIN_FILENO); | |
2173 | } | |
ad833b3e DM |
2174 | out = dup(STDOUT_FILENO); |
2175 | err = dup(STDERR_FILENO); | |
1383bd8e | 2176 | |
4d28fa78 | 2177 | if (in == -1 || out == -1 || err == -1) |
caf6dd6d | 2178 | fatal("dup() in/out/err failed"); |
1383bd8e | 2179 | |
e4340be5 DM |
2180 | window = CHAN_SES_WINDOW_DEFAULT; |
2181 | packetmax = CHAN_SES_PACKET_DEFAULT; | |
19a59451 DM |
2182 | if (tty_flag) { |
2183 | window >>= 1; | |
2184 | packetmax >>= 1; | |
1383bd8e | 2185 | } |
dbee4119 | 2186 | c = channel_new(ssh, |
1383bd8e | 2187 | "session", SSH_CHANNEL_OPENING, in, out, err, |
e4340be5 | 2188 | window, packetmax, CHAN_EXTENDED_WRITE, |
7be4ac81 | 2189 | "client-session", CHANNEL_NONBLOCK_STDIO); |
1383bd8e | 2190 | |
816036f1 | 2191 | debug3_f("channel_new: %d", c->self); |
4c3f77dd | 2192 | |
dbee4119 | 2193 | channel_send_open(ssh, c->self); |
eda8909d | 2194 | if (options.session_type != SESSION_TYPE_NONE) |
dbee4119 | 2195 | channel_register_open_confirm(ssh, c->self, |
b84886ba | 2196 | ssh_session2_setup, NULL); |
95def098 | 2197 | |
99c73b37 | 2198 | return c->self; |
4c3f77dd BL |
2199 | } |
2200 | ||
bba81213 | 2201 | static int |
d060bc7f | 2202 | ssh_session2(struct ssh *ssh, const struct ssh_conn_info *cinfo) |
4c3f77dd | 2203 | { |
43254b32 | 2204 | int r, interactive, id = -1; |
b7548b12 | 2205 | char *cp, *tun_fwd_ifname = NULL; |
4c3f77dd BL |
2206 | |
2207 | /* XXX should be pre-session */ | |
2d6665d9 | 2208 | if (!options.control_persist) |
dbee4119 | 2209 | ssh_init_stdio_forwarding(ssh); |
b7548b12 | 2210 | |
2211 | ssh_init_forwarding(ssh, &tun_fwd_ifname); | |
2212 | ||
2213 | if (options.local_command != NULL) { | |
2214 | debug3("expanding LocalCommand: %s", options.local_command); | |
2215 | cp = options.local_command; | |
2216 | options.local_command = percent_expand(cp, | |
0f504f59 | 2217 | DEFAULT_CLIENT_PERCENT_EXPAND_ARGS(cinfo), |
b7548b12 | 2218 | "T", tun_fwd_ifname == NULL ? "NONE" : tun_fwd_ifname, |
2219 | (char *)NULL); | |
2220 | debug3("expanded LocalCommand: %s", options.local_command); | |
2221 | free(cp); | |
2222 | } | |
4c3f77dd | 2223 | |
e11e1ea5 | 2224 | /* Start listening for multiplex clients */ |
25b2ed66 | 2225 | if (!ssh_packet_get_mux(ssh)) |
dbee4119 | 2226 | muxserver_listen(ssh); |
e11e1ea5 | 2227 | |
b8bbff3b | 2228 | /* |
2d6665d9 DT |
2229 | * If we are in control persist mode and have a working mux listen |
2230 | * socket, then prepare to background ourselves and have a foreground | |
6d755706 | 2231 | * client attach as a control client. |
2d6665d9 | 2232 | * NB. we must save copies of the flags that we override for |
6d755706 | 2233 | * the backgrounding, since we defer attachment of the client until |
e11e1ea5 DM |
2234 | * after the connection is fully established (in particular, |
2235 | * async rfwd replies have been received for ExitOnForwardFailure). | |
2236 | */ | |
b8bbff3b | 2237 | if (options.control_persist && muxserver_sock != -1) { |
e0c5088f | 2238 | ostdin_null_flag = options.stdin_null; |
eda8909d | 2239 | osession_type = options.session_type; |
21771e22 | 2240 | orequest_tty = options.request_tty; |
e11e1ea5 | 2241 | otty_flag = tty_flag; |
2d34205d | 2242 | ofork_after_authentication = options.fork_after_authentication; |
e0c5088f | 2243 | options.stdin_null = 1; |
eda8909d | 2244 | options.session_type = SESSION_TYPE_NONE; |
b8bbff3b | 2245 | tty_flag = 0; |
2d34205d | 2246 | if ((osession_type != SESSION_TYPE_NONE || |
52c3b698 | 2247 | options.stdio_forward_host != NULL)) |
e11e1ea5 | 2248 | need_controlpersist_detach = 1; |
a917e973 | 2249 | options.fork_after_authentication = 1; |
b8bbff3b | 2250 | } |
2d6665d9 DT |
2251 | /* |
2252 | * ControlPersist mux listen socket setup failed, attempt the | |
2253 | * stdio forward setup that we skipped earlier. | |
2254 | */ | |
2255 | if (options.control_persist && muxserver_sock == -1) | |
dbee4119 | 2256 | ssh_init_stdio_forwarding(ssh); |
e11e1ea5 | 2257 | |
eda8909d | 2258 | if (options.session_type != SESSION_TYPE_NONE) |
dbee4119 | 2259 | id = ssh_session2_open(ssh); |
649fe025 | 2260 | else { |
43254b32 | 2261 | interactive = options.control_master == SSHCTL_MASTER_NO; |
2262 | /* ControlPersist may have clobbered ControlMaster, so check */ | |
2263 | if (need_controlpersist_detach) | |
2264 | interactive = otty_flag != 0; | |
2265 | ssh_packet_set_interactive(ssh, interactive, | |
649fe025 DM |
2266 | options.ip_qos_interactive, options.ip_qos_bulk); |
2267 | } | |
4c3f77dd | 2268 | |
8901fa9c | 2269 | /* If we don't expect to open a new session, then disallow it */ |
456e6f0e | 2270 | if (options.control_master == SSHCTL_MASTER_NO && |
4ca6a1fa | 2271 | (ssh->compat & SSH_NEW_OPENSSH)) { |
8901fa9c | 2272 | debug("Requesting no-more-sessions@openssh.com"); |
25b2ed66 | 2273 | if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || |
2274 | (r = sshpkt_put_cstring(ssh, | |
2275 | "no-more-sessions@openssh.com")) != 0 || | |
2276 | (r = sshpkt_put_u8(ssh, 0)) != 0 || | |
2277 | (r = sshpkt_send(ssh)) != 0) | |
816036f1 | 2278 | fatal_fr(r, "send packet"); |
8901fa9c DT |
2279 | } |
2280 | ||
d27b9471 DM |
2281 | /* Execute a local command */ |
2282 | if (options.local_command != NULL && | |
2283 | options.permit_local_command) | |
2284 | ssh_local_cmd(options.local_command); | |
2285 | ||
4d5456c7 | 2286 | /* |
2287 | * stdout is now owned by the session channel; clobber it here | |
2288 | * so future channel closes are propagated to the local fd. | |
2289 | * NB. this can only happen after LocalCommand has completed, | |
2290 | * as it may want to write to stdout. | |
2291 | */ | |
396d32f3 | 2292 | if (!need_controlpersist_detach && stdfd_devnull(0, 1, 0) == -1) |
816036f1 | 2293 | error_f("stdfd_devnull failed"); |
4d5456c7 | 2294 | |
1f25ab43 DM |
2295 | /* |
2296 | * If requested and we are not interested in replies to remote | |
2297 | * forwarding requests, then let ssh continue in the background. | |
2298 | */ | |
a917e973 | 2299 | if (options.fork_after_authentication) { |
e11e1ea5 DM |
2300 | if (options.exit_on_forward_failure && |
2301 | options.num_remote_forwards > 0) { | |
2302 | debug("deferring postauth fork until remote forward " | |
2303 | "confirmation received"); | |
2304 | } else | |
2305 | fork_postauth(); | |
9a2a6098 | 2306 | } |
4c3f77dd | 2307 | |
dbee4119 | 2308 | return client_loop(ssh, tty_flag, tty_flag ? |
2b1f71ba | 2309 | options.escape_char : SSH_ESCAPECHAR_NONE, id); |
d4a8b7e3 | 2310 | } |
0bc1bd81 | 2311 | |
4e44a79a | 2312 | /* Loads all IdentityFile and CertificateFile keys */ |
bba81213 | 2313 | static void |
d060bc7f | 2314 | load_public_identity_files(const struct ssh_conn_info *cinfo) |
266dfdfd | 2315 | { |
b7548b12 | 2316 | char *filename, *cp; |
54d90ace | 2317 | struct sshkey *public; |
4e44a79a | 2318 | int i; |
2319 | u_int n_ids, n_certs; | |
0a80ca19 | 2320 | char *identity_files[SSH_MAX_IDENTITY_FILES]; |
54d90ace | 2321 | struct sshkey *identity_keys[SSH_MAX_IDENTITY_FILES]; |
3eb7f103 | 2322 | int identity_file_userprovided[SSH_MAX_IDENTITY_FILES]; |
4e44a79a | 2323 | char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; |
2324 | struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; | |
3eb7f103 | 2325 | int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; |
7ea845e4 | 2326 | #ifdef ENABLE_PKCS11 |
89a8d452 | 2327 | struct sshkey **keys = NULL; |
2328 | char **comments = NULL; | |
7ea845e4 | 2329 | int nkeys; |
0a80ca19 | 2330 | #endif /* PKCS11 */ |
0936a5bb | 2331 | |
4e44a79a | 2332 | n_ids = n_certs = 0; |
1d2c4564 DM |
2333 | memset(identity_files, 0, sizeof(identity_files)); |
2334 | memset(identity_keys, 0, sizeof(identity_keys)); | |
3eb7f103 | 2335 | memset(identity_file_userprovided, 0, |
2336 | sizeof(identity_file_userprovided)); | |
4e44a79a | 2337 | memset(certificate_files, 0, sizeof(certificate_files)); |
2338 | memset(certificates, 0, sizeof(certificates)); | |
3eb7f103 | 2339 | memset(certificate_file_userprovided, 0, |
2340 | sizeof(certificate_file_userprovided)); | |
0a80ca19 DM |
2341 | |
2342 | #ifdef ENABLE_PKCS11 | |
7ea845e4 | 2343 | if (options.pkcs11_provider != NULL && |
0936a5bb | 2344 | options.num_identity_files < SSH_MAX_IDENTITY_FILES && |
7ea845e4 DM |
2345 | (pkcs11_init(!options.batch_mode) == 0) && |
2346 | (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, | |
89a8d452 | 2347 | &keys, &comments)) > 0) { |
7ea845e4 | 2348 | for (i = 0; i < nkeys; i++) { |
0a80ca19 | 2349 | if (n_ids >= SSH_MAX_IDENTITY_FILES) { |
5467fbcb | 2350 | sshkey_free(keys[i]); |
89a8d452 | 2351 | free(comments[i]); |
0a80ca19 DM |
2352 | continue; |
2353 | } | |
2354 | identity_keys[n_ids] = keys[i]; | |
89a8d452 | 2355 | identity_files[n_ids] = comments[i]; /* transferred */ |
0a80ca19 | 2356 | n_ids++; |
0936a5bb | 2357 | } |
a627d42e | 2358 | free(keys); |
89a8d452 | 2359 | free(comments); |
c5b68001 | 2360 | } |
7ea845e4 | 2361 | #endif /* ENABLE_PKCS11 */ |
0a80ca19 | 2362 | for (i = 0; i < options.num_identity_files; i++) { |
15fd19c4 DT |
2363 | if (n_ids >= SSH_MAX_IDENTITY_FILES || |
2364 | strcasecmp(options.identity_files[i], "none") == 0) { | |
a627d42e | 2365 | free(options.identity_files[i]); |
4e44a79a | 2366 | options.identity_files[i] = NULL; |
0a80ca19 DM |
2367 | continue; |
2368 | } | |
e655ee04 | 2369 | cp = tilde_expand_filename(options.identity_files[i], getuid()); |
d060bc7f | 2370 | filename = default_client_percent_dollar_expand(cp, cinfo); |
a627d42e | 2371 | free(cp); |
5467fbcb | 2372 | check_load(sshkey_load_public(filename, &public, NULL), |
54b333d1 | 2373 | &public, filename, "pubkey"); |
711b04a5 BL |
2374 | debug("identity file %s type %d", filename, |
2375 | public ? public->type : -1); | |
a627d42e | 2376 | free(options.identity_files[i]); |
0a80ca19 DM |
2377 | identity_files[n_ids] = filename; |
2378 | identity_keys[n_ids] = public; | |
3eb7f103 | 2379 | identity_file_userprovided[n_ids] = |
2380 | options.identity_file_userprovided[i]; | |
0a80ca19 DM |
2381 | if (++n_ids >= SSH_MAX_IDENTITY_FILES) |
2382 | continue; | |
2383 | ||
4e44a79a | 2384 | /* |
2385 | * If no certificates have been explicitly listed then try | |
2386 | * to add the default certificate variant too. | |
2387 | */ | |
2388 | if (options.num_certificate_files != 0) | |
2389 | continue; | |
0a80ca19 | 2390 | xasprintf(&cp, "%s-cert", filename); |
5467fbcb | 2391 | check_load(sshkey_load_public(cp, &public, NULL), |
54b333d1 | 2392 | &public, filename, "pubkey"); |
0a80ca19 DM |
2393 | debug("identity file %s type %d", cp, |
2394 | public ? public->type : -1); | |
2395 | if (public == NULL) { | |
a627d42e | 2396 | free(cp); |
0a80ca19 DM |
2397 | continue; |
2398 | } | |
5467fbcb | 2399 | if (!sshkey_is_cert(public)) { |
816036f1 | 2400 | debug_f("key %s type %s is not a certificate", |
2401 | cp, sshkey_type(public)); | |
5467fbcb | 2402 | sshkey_free(public); |
a627d42e | 2403 | free(cp); |
0a80ca19 DM |
2404 | continue; |
2405 | } | |
b4867e07 | 2406 | /* NB. leave filename pointing to private key */ |
2407 | identity_files[n_ids] = xstrdup(filename); | |
0a80ca19 | 2408 | identity_keys[n_ids] = public; |
3eb7f103 | 2409 | identity_file_userprovided[n_ids] = |
2410 | options.identity_file_userprovided[i]; | |
0a80ca19 | 2411 | n_ids++; |
711b04a5 | 2412 | } |
4e44a79a | 2413 | |
2414 | if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES) | |
816036f1 | 2415 | fatal_f("too many certificates"); |
4e44a79a | 2416 | for (i = 0; i < options.num_certificate_files; i++) { |
2417 | cp = tilde_expand_filename(options.certificate_files[i], | |
e655ee04 | 2418 | getuid()); |
d060bc7f | 2419 | filename = default_client_percent_dollar_expand(cp, cinfo); |
4e44a79a | 2420 | free(cp); |
2421 | ||
5467fbcb | 2422 | check_load(sshkey_load_public(filename, &public, NULL), |
54b333d1 | 2423 | &public, filename, "certificate"); |
4e44a79a | 2424 | debug("certificate file %s type %d", filename, |
2425 | public ? public->type : -1); | |
2426 | free(options.certificate_files[i]); | |
2427 | options.certificate_files[i] = NULL; | |
2428 | if (public == NULL) { | |
2429 | free(filename); | |
2430 | continue; | |
2431 | } | |
5467fbcb | 2432 | if (!sshkey_is_cert(public)) { |
816036f1 | 2433 | debug_f("key %s type %s is not a certificate", |
2434 | filename, sshkey_type(public)); | |
5467fbcb | 2435 | sshkey_free(public); |
4e44a79a | 2436 | free(filename); |
2437 | continue; | |
2438 | } | |
2439 | certificate_files[n_certs] = filename; | |
2440 | certificates[n_certs] = public; | |
3eb7f103 | 2441 | certificate_file_userprovided[n_certs] = |
2442 | options.certificate_file_userprovided[i]; | |
4e44a79a | 2443 | ++n_certs; |
2444 | } | |
2445 | ||
0a80ca19 DM |
2446 | options.num_identity_files = n_ids; |
2447 | memcpy(options.identity_files, identity_files, sizeof(identity_files)); | |
2448 | memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); | |
3eb7f103 | 2449 | memcpy(options.identity_file_userprovided, |
2450 | identity_file_userprovided, sizeof(identity_file_userprovided)); | |
0a80ca19 | 2451 | |
4e44a79a | 2452 | options.num_certificate_files = n_certs; |
2453 | memcpy(options.certificate_files, | |
2454 | certificate_files, sizeof(certificate_files)); | |
2455 | memcpy(options.certificates, certificates, sizeof(certificates)); | |
3eb7f103 | 2456 | memcpy(options.certificate_file_userprovided, |
2457 | certificate_file_userprovided, | |
2458 | sizeof(certificate_file_userprovided)); | |
266dfdfd | 2459 | } |
857b02e3 DM |
2460 | |
2461 | static void | |
2462 | main_sigchld_handler(int sig) | |
2463 | { | |
2464 | int save_errno = errno; | |
2465 | pid_t pid; | |
2466 | int status; | |
2467 | ||
2468 | while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || | |
4d28fa78 | 2469 | (pid == -1 && errno == EINTR)) |
857b02e3 | 2470 | ; |
857b02e3 DM |
2471 | errno = save_errno; |
2472 | } |