jcs's openbsd hax
openbsd
1/* $OpenBSD: readconf.c,v 1.410 2026/02/14 00:18:34 jsg Exp $ */
2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved
6 * Functions for reading the configuration files.
7 *
8 * As far as I am concerned, the code I have written for this software
9 * can be used freely for any purpose. Any derived versions of this
10 * software must be clearly marked as such, and if the derived work is
11 * incompatible with the protocol description in the RFC file, it must be
12 * called by a name other than "ssh" or "Secure Shell".
13 */
14
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <sys/socket.h>
18#include <sys/wait.h>
19#include <sys/un.h>
20
21#include <net/if.h>
22#include <netinet/in.h>
23#include <netinet/ip.h>
24
25#include <ctype.h>
26#include <errno.h>
27#include <glob.h>
28#include <ifaddrs.h>
29#include <limits.h>
30#include <netdb.h>
31#include <paths.h>
32#include <pwd.h>
33#include <signal.h>
34#include <stdio.h>
35#include <string.h>
36#include <stdarg.h>
37#include <unistd.h>
38#include <util.h>
39#include <vis.h>
40
41#include "xmalloc.h"
42#include "ssh.h"
43#include "cipher.h"
44#include "pathnames.h"
45#include "log.h"
46#include "sshkey.h"
47#include "misc.h"
48#include "readconf.h"
49#include "match.h"
50#include "kex.h"
51#include "mac.h"
52#include "myproposal.h"
53#include "digest.h"
54#include "version.h"
55
56/* Format of the configuration file:
57
58 # Configuration data is parsed as follows:
59 # 1. command line options
60 # 2. user-specific file
61 # 3. system-wide file
62 # Any configuration value is only changed the first time it is set.
63 # Thus, host-specific definitions should be at the beginning of the
64 # configuration file, and defaults at the end.
65
66 # Host-specific declarations. These may override anything above. A single
67 # host may match multiple declarations; these are processed in the order
68 # that they are given in.
69
70 Host *.ngs.fi ngs.fi
71 User foo
72
73 Host fake.com
74 Hostname another.host.name.real.org
75 User blaah
76 Port 34289
77 ForwardX11 no
78 ForwardAgent no
79
80 Host books.com
81 RemoteForward 9999 shadows.cs.hut.fi:9999
82 Ciphers 3des-cbc
83
84 Host fascist.blob.com
85 Port 23123
86 User tylonen
87 PasswordAuthentication no
88
89 Host puukko.hut.fi
90 User t35124p
91 ProxyCommand ssh-proxy %h %p
92
93 Host *.fr
94 PublicKeyAuthentication no
95
96 Host *.su
97 Ciphers aes128-ctr
98 PasswordAuthentication no
99
100 Host vpn.fake.com
101 Tunnel yes
102 TunnelDevice 3
103
104 # Defaults for various options
105 Host *
106 ForwardAgent no
107 ForwardX11 no
108 PasswordAuthentication yes
109 StrictHostKeyChecking yes
110 TcpKeepAlive no
111 IdentityFile ~/.ssh/identity
112 Port 22
113 EscapeChar ~
114
115*/
116
117static int read_config_file_depth(const char *filename, struct passwd *pw,
118 const char *host, const char *original_host, const char *remote_command,
119 Options *options, int flags, int *activep, int *want_final_pass, int depth);
120static int process_config_line_depth(Options *options, struct passwd *pw,
121 const char *host, const char *original_host, const char *remote_command,
122 char *line, const char *filename, int linenum, int *activep, int flags,
123 int *want_final_pass, int depth);
124
125/* Keyword tokens. */
126
127typedef enum {
128 oBadOption,
129 oHost, oMatch, oInclude, oTag,
130 oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
131 oGatewayPorts, oExitOnForwardFailure,
132 oPasswordAuthentication,
133 oXAuthLocation,
134 oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward,
135 oPermitRemoteOpen,
136 oCertificateFile, oAddKeysToAgent, oIdentityAgent,
137 oUser, oEscapeChar, oProxyCommand,
138 oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
139 oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
140 oTCPKeepAlive, oNumberOfPasswordPrompts,
141 oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs,
142 oPubkeyAuthentication,
143 oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
144 oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
145 oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
146 oClearAllForwardings, oNoHostAuthenticationForLocalhost,
147 oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
148 oAddressFamily, oGssAuthentication, oGssDelegateCreds,
149 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
150 oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
151 oHashKnownHosts,
152 oTunnel, oTunnelDevice,
153 oLocalCommand, oPermitLocalCommand, oRemoteCommand,
154 oVisualHostKey,
155 oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
156 oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass,
157 oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
158 oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
159 oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
160 oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
161 oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
162 oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
163 oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
164 oVersionAddendum, oRefuseConnection, oWarnWeakCrypto,
165 oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
166} OpCodes;
167
168/* Textual representations of the tokens. */
169
170static struct {
171 const char *name;
172 OpCodes opcode;
173} keywords[] = {
174 /* Deprecated options */
175 { "protocol", oIgnore }, /* NB. silently ignored */
176 { "cipher", oDeprecated },
177 { "fallbacktorsh", oDeprecated },
178 { "globalknownhostsfile2", oDeprecated },
179 { "rhostsauthentication", oDeprecated },
180 { "userknownhostsfile2", oDeprecated },
181 { "useroaming", oDeprecated },
182 { "usersh", oDeprecated },
183 { "useprivilegedport", oDeprecated },
184
185 /* Unsupported options */
186 { "afstokenpassing", oUnsupported },
187 { "kerberosauthentication", oUnsupported },
188 { "kerberostgtpassing", oUnsupported },
189 { "rsaauthentication", oUnsupported },
190 { "rhostsrsaauthentication", oUnsupported },
191 { "compressionlevel", oUnsupported },
192
193 /* Sometimes-unsupported options */
194#if defined(GSSAPI)
195 { "gssapiauthentication", oGssAuthentication },
196 { "gssapidelegatecredentials", oGssDelegateCreds },
197# else
198 { "gssapiauthentication", oUnsupported },
199 { "gssapidelegatecredentials", oUnsupported },
200#endif
201#ifdef ENABLE_PKCS11
202 { "pkcs11provider", oPKCS11Provider },
203 { "smartcarddevice", oPKCS11Provider },
204# else
205 { "smartcarddevice", oUnsupported },
206 { "pkcs11provider", oUnsupported },
207#endif
208
209 { "forwardagent", oForwardAgent },
210 { "forwardx11", oForwardX11 },
211 { "forwardx11trusted", oForwardX11Trusted },
212 { "forwardx11timeout", oForwardX11Timeout },
213 { "exitonforwardfailure", oExitOnForwardFailure },
214 { "xauthlocation", oXAuthLocation },
215 { "gatewayports", oGatewayPorts },
216 { "passwordauthentication", oPasswordAuthentication },
217 { "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
218 { "kbdinteractivedevices", oKbdInteractiveDevices },
219 { "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */
220 { "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */
221 { "tisauthentication", oKbdInteractiveAuthentication }, /* alias */
222 { "pubkeyauthentication", oPubkeyAuthentication },
223 { "dsaauthentication", oPubkeyAuthentication }, /* alias */
224 { "hostbasedauthentication", oHostbasedAuthentication },
225 { "identityfile", oIdentityFile },
226 { "identityfile2", oIdentityFile }, /* obsolete */
227 { "identitiesonly", oIdentitiesOnly },
228 { "certificatefile", oCertificateFile },
229 { "addkeystoagent", oAddKeysToAgent },
230 { "identityagent", oIdentityAgent },
231 { "hostname", oHostname },
232 { "hostkeyalias", oHostKeyAlias },
233 { "proxycommand", oProxyCommand },
234 { "port", oPort },
235 { "ciphers", oCiphers },
236 { "macs", oMacs },
237 { "remoteforward", oRemoteForward },
238 { "localforward", oLocalForward },
239 { "permitremoteopen", oPermitRemoteOpen },
240 { "user", oUser },
241 { "host", oHost },
242 { "match", oMatch },
243 { "tag", oTag },
244 { "escapechar", oEscapeChar },
245 { "globalknownhostsfile", oGlobalKnownHostsFile },
246 { "userknownhostsfile", oUserKnownHostsFile },
247 { "connectionattempts", oConnectionAttempts },
248 { "batchmode", oBatchMode },
249 { "checkhostip", oCheckHostIP },
250 { "stricthostkeychecking", oStrictHostKeyChecking },
251 { "compression", oCompression },
252 { "tcpkeepalive", oTCPKeepAlive },
253 { "keepalive", oTCPKeepAlive }, /* obsolete */
254 { "numberofpasswordprompts", oNumberOfPasswordPrompts },
255 { "syslogfacility", oLogFacility },
256 { "loglevel", oLogLevel },
257 { "logverbose", oLogVerbose },
258 { "dynamicforward", oDynamicForward },
259 { "preferredauthentications", oPreferredAuthentications },
260 { "hostkeyalgorithms", oHostKeyAlgorithms },
261 { "casignaturealgorithms", oCASignatureAlgorithms },
262 { "bindaddress", oBindAddress },
263 { "bindinterface", oBindInterface },
264 { "clearallforwardings", oClearAllForwardings },
265 { "enablesshkeysign", oEnableSSHKeysign },
266 { "verifyhostkeydns", oVerifyHostKeyDNS },
267 { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
268 { "rekeylimit", oRekeyLimit },
269 { "connecttimeout", oConnectTimeout },
270 { "addressfamily", oAddressFamily },
271 { "serveraliveinterval", oServerAliveInterval },
272 { "serveralivecountmax", oServerAliveCountMax },
273 { "sendenv", oSendEnv },
274 { "setenv", oSetEnv },
275 { "controlpath", oControlPath },
276 { "controlmaster", oControlMaster },
277 { "controlpersist", oControlPersist },
278 { "hashknownhosts", oHashKnownHosts },
279 { "include", oInclude },
280 { "tunnel", oTunnel },
281 { "tunneldevice", oTunnelDevice },
282 { "localcommand", oLocalCommand },
283 { "permitlocalcommand", oPermitLocalCommand },
284 { "remotecommand", oRemoteCommand },
285 { "visualhostkey", oVisualHostKey },
286 { "kexalgorithms", oKexAlgorithms },
287 { "ipqos", oIPQoS },
288 { "requesttty", oRequestTTY },
289 { "sessiontype", oSessionType },
290 { "stdinnull", oStdinNull },
291 { "forkafterauthentication", oForkAfterAuthentication },
292 { "proxyusefdpass", oProxyUseFdpass },
293 { "canonicaldomains", oCanonicalDomains },
294 { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
295 { "canonicalizehostname", oCanonicalizeHostname },
296 { "canonicalizemaxdots", oCanonicalizeMaxDots },
297 { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
298 { "streamlocalbindmask", oStreamLocalBindMask },
299 { "streamlocalbindunlink", oStreamLocalBindUnlink },
300 { "revokedhostkeys", oRevokedHostKeys },
301 { "fingerprinthash", oFingerprintHash },
302 { "updatehostkeys", oUpdateHostkeys },
303 { "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms },
304 { "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */
305 { "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
306 { "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
307 { "ignoreunknown", oIgnoreUnknown },
308 { "proxyjump", oProxyJump },
309 { "securitykeyprovider", oSecurityKeyProvider },
310 { "knownhostscommand", oKnownHostsCommand },
311 { "requiredrsasize", oRequiredRSASize },
312 { "enableescapecommandline", oEnableEscapeCommandline },
313 { "obscurekeystroketiming", oObscureKeystrokeTiming },
314 { "channeltimeout", oChannelTimeout },
315 { "versionaddendum", oVersionAddendum },
316 { "refuseconnection", oRefuseConnection },
317 { "warnweakcrypto", oWarnWeakCrypto },
318
319 { NULL, oBadOption }
320};
321
322static const char *lookup_opcode_name(OpCodes code);
323
324const char *
325kex_default_pk_alg(void)
326{
327 static char *pkalgs;
328
329 if (pkalgs == NULL) {
330 char *all_key;
331
332 all_key = sshkey_alg_list(0, 0, 1, ',');
333 pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
334 free(all_key);
335 }
336 return pkalgs;
337}
338
339char *
340ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
341 const char *user, const char *jumphost)
342{
343 struct ssh_digest_ctx *md;
344 u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
345
346 if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
347 ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
348 ssh_digest_update(md, host, strlen(host)) < 0 ||
349 ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
350 ssh_digest_update(md, user, strlen(user)) < 0 ||
351 ssh_digest_update(md, jumphost, strlen(jumphost)) < 0 ||
352 ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
353 fatal_f("mux digest failed");
354 ssh_digest_free(md);
355 return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
356}
357
358/*
359 * Adds a local TCP/IP port forward to options. Never returns if there is an
360 * error.
361 */
362
363void
364add_local_forward(Options *options, const struct Forward *newfwd)
365{
366 struct Forward *fwd;
367 int i;
368
369 /* Don't add duplicates */
370 for (i = 0; i < options->num_local_forwards; i++) {
371 if (forward_equals(newfwd, options->local_forwards + i))
372 return;
373 }
374 options->local_forwards = xreallocarray(options->local_forwards,
375 options->num_local_forwards + 1,
376 sizeof(*options->local_forwards));
377 fwd = &options->local_forwards[options->num_local_forwards++];
378
379 fwd->listen_host = newfwd->listen_host;
380 fwd->listen_port = newfwd->listen_port;
381 fwd->listen_path = newfwd->listen_path;
382 fwd->connect_host = newfwd->connect_host;
383 fwd->connect_port = newfwd->connect_port;
384 fwd->connect_path = newfwd->connect_path;
385}
386
387/*
388 * Adds a remote TCP/IP port forward to options. Never returns if there is
389 * an error.
390 */
391
392void
393add_remote_forward(Options *options, const struct Forward *newfwd)
394{
395 struct Forward *fwd;
396 int i;
397
398 /* Don't add duplicates */
399 for (i = 0; i < options->num_remote_forwards; i++) {
400 if (forward_equals(newfwd, options->remote_forwards + i))
401 return;
402 }
403 options->remote_forwards = xreallocarray(options->remote_forwards,
404 options->num_remote_forwards + 1,
405 sizeof(*options->remote_forwards));
406 fwd = &options->remote_forwards[options->num_remote_forwards++];
407
408 fwd->listen_host = newfwd->listen_host;
409 fwd->listen_port = newfwd->listen_port;
410 fwd->listen_path = newfwd->listen_path;
411 fwd->connect_host = newfwd->connect_host;
412 fwd->connect_port = newfwd->connect_port;
413 fwd->connect_path = newfwd->connect_path;
414 fwd->handle = newfwd->handle;
415 fwd->allocated_port = 0;
416}
417
418static void
419clear_forwardings(Options *options)
420{
421 int i;
422
423 for (i = 0; i < options->num_local_forwards; i++) {
424 free(options->local_forwards[i].listen_host);
425 free(options->local_forwards[i].listen_path);
426 free(options->local_forwards[i].connect_host);
427 free(options->local_forwards[i].connect_path);
428 }
429 if (options->num_local_forwards > 0) {
430 free(options->local_forwards);
431 options->local_forwards = NULL;
432 }
433 options->num_local_forwards = 0;
434 for (i = 0; i < options->num_remote_forwards; i++) {
435 free(options->remote_forwards[i].listen_host);
436 free(options->remote_forwards[i].listen_path);
437 free(options->remote_forwards[i].connect_host);
438 free(options->remote_forwards[i].connect_path);
439 }
440 if (options->num_remote_forwards > 0) {
441 free(options->remote_forwards);
442 options->remote_forwards = NULL;
443 }
444 options->num_remote_forwards = 0;
445 options->tun_open = SSH_TUNMODE_NO;
446}
447
448void
449add_certificate_file(Options *options, const char *path, int userprovided)
450{
451 int i;
452
453 if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
454 fatal("Too many certificate files specified (max %d)",
455 SSH_MAX_CERTIFICATE_FILES);
456
457 /* Avoid registering duplicates */
458 for (i = 0; i < options->num_certificate_files; i++) {
459 if (options->certificate_file_userprovided[i] == userprovided &&
460 strcmp(options->certificate_files[i], path) == 0) {
461 debug2_f("ignoring duplicate key %s", path);
462 return;
463 }
464 }
465
466 options->certificate_file_userprovided[options->num_certificate_files] =
467 userprovided;
468 options->certificate_files[options->num_certificate_files++] =
469 xstrdup(path);
470}
471
472void
473add_identity_file(Options *options, const char *dir, const char *filename,
474 int userprovided)
475{
476 char *path;
477 int i;
478
479 if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
480 fatal("Too many identity files specified (max %d)",
481 SSH_MAX_IDENTITY_FILES);
482
483 if (dir == NULL) /* no dir, filename is absolute */
484 path = xstrdup(filename);
485 else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX)
486 fatal("Identity file path %s too long", path);
487
488 /* Avoid registering duplicates */
489 for (i = 0; i < options->num_identity_files; i++) {
490 if (options->identity_file_userprovided[i] == userprovided &&
491 strcmp(options->identity_files[i], path) == 0) {
492 debug2_f("ignoring duplicate key %s", path);
493 free(path);
494 return;
495 }
496 }
497
498 options->identity_file_userprovided[options->num_identity_files] =
499 userprovided;
500 options->identity_files[options->num_identity_files++] = path;
501}
502
503int
504default_ssh_port(void)
505{
506 static int port;
507 struct servent *sp;
508
509 if (port == 0) {
510 sp = getservbyname(SSH_SERVICE_NAME, "tcp");
511 port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
512 }
513 return port;
514}
515
516/*
517 * Execute a command in a shell.
518 * Return its exit status or -1 on abnormal exit.
519 */
520static int
521execute_in_shell(const char *cmd)
522{
523 char *shell;
524 pid_t pid;
525 int status;
526
527 if ((shell = getenv("SHELL")) == NULL)
528 shell = _PATH_BSHELL;
529
530 if (access(shell, X_OK) == -1) {
531 fatal("Shell \"%s\" is not executable: %s",
532 shell, strerror(errno));
533 }
534
535 debug("Executing command: '%.500s'", cmd);
536
537 /* Fork and execute the command. */
538 if ((pid = fork()) == 0) {
539 char *argv[4];
540
541 if (stdfd_devnull(1, 1, 0) == -1)
542 fatal_f("stdfd_devnull failed");
543 closefrom(STDERR_FILENO + 1);
544
545 argv[0] = shell;
546 argv[1] = "-c";
547 argv[2] = xstrdup(cmd);
548 argv[3] = NULL;
549
550 execv(argv[0], argv);
551 error("Unable to execute '%.100s': %s", cmd, strerror(errno));
552 /* Die with signal to make this error apparent to parent. */
553 ssh_signal(SIGTERM, SIG_DFL);
554 kill(getpid(), SIGTERM);
555 _exit(1);
556 }
557 /* Parent. */
558 if (pid == -1)
559 fatal_f("fork: %.100s", strerror(errno));
560
561 while (waitpid(pid, &status, 0) == -1) {
562 if (errno != EINTR && errno != EAGAIN)
563 fatal_f("waitpid: %s", strerror(errno));
564 }
565 if (!WIFEXITED(status)) {
566 error("command '%.100s' exited abnormally", cmd);
567 return -1;
568 }
569 debug3("command returned status %d", WEXITSTATUS(status));
570 return WEXITSTATUS(status);
571}
572
573/*
574 * Check whether a local network interface address appears in CIDR pattern-
575 * list 'addrlist'. Returns 1 if matched or 0 otherwise.
576 */
577static int
578check_match_ifaddrs(const char *addrlist)
579{
580 struct ifaddrs *ifa, *ifaddrs = NULL;
581 int r, found = 0;
582 char addr[NI_MAXHOST];
583 socklen_t salen;
584
585 if (getifaddrs(&ifaddrs) != 0) {
586 error("match localnetwork: getifaddrs failed: %s",
587 strerror(errno));
588 return 0;
589 }
590 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
591 if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
592 (ifa->ifa_flags & IFF_UP) == 0)
593 continue;
594 switch (ifa->ifa_addr->sa_family) {
595 case AF_INET:
596 salen = sizeof(struct sockaddr_in);
597 break;
598 case AF_INET6:
599 salen = sizeof(struct sockaddr_in6);
600 break;
601 case AF_LINK:
602 /* ignore */
603 continue;
604 default:
605 debug2_f("interface %s: unsupported address family %d",
606 ifa->ifa_name, ifa->ifa_addr->sa_family);
607 continue;
608 }
609 if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr),
610 NULL, 0, NI_NUMERICHOST)) != 0) {
611 debug2_f("interface %s getnameinfo failed: %s",
612 ifa->ifa_name, gai_strerror(r));
613 continue;
614 }
615 debug3_f("interface %s addr %s", ifa->ifa_name, addr);
616 if (addr_match_cidr_list(addr, addrlist) == 1) {
617 debug3_f("matched interface %s: address %s in %s",
618 ifa->ifa_name, addr, addrlist);
619 found = 1;
620 break;
621 }
622 }
623 freeifaddrs(ifaddrs);
624 return found;
625}
626
627/*
628 * Expand a "match exec" command or an Include path, caller must free returned
629 * value.
630 */
631static char *
632expand_match_exec_or_include_path(const char *path, Options *options,
633 struct passwd *pw, const char *host_arg, const char *original_host,
634 int final_pass, int is_include_path)
635{
636 char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
637 char uidstr[32], *conn_hash_hex, *keyalias, *jmphost, *ruser;
638 char *host, *ret;
639 int port;
640
641 port = options->port <= 0 ? default_ssh_port() : options->port;
642 ruser = options->user == NULL ? pw->pw_name : options->user;
643 if (final_pass) {
644 host = xstrdup(options->hostname);
645 } else if (options->hostname != NULL) {
646 /* NB. Please keep in sync with ssh.c:main() */
647 host = percent_expand(options->hostname,
648 "h", host_arg, (char *)NULL);
649 } else {
650 host = xstrdup(host_arg);
651 }
652 if (gethostname(thishost, sizeof(thishost)) == -1)
653 fatal("gethostname: %s", strerror(errno));
654 jmphost = option_clear_or_none(options->jump_host) ?
655 "" : options->jump_host;
656 strlcpy(shorthost, thishost, sizeof(shorthost));
657 shorthost[strcspn(thishost, ".")] = '\0';
658 snprintf(portstr, sizeof(portstr), "%d", port);
659 snprintf(uidstr, sizeof(uidstr), "%llu",
660 (unsigned long long)pw->pw_uid);
661 conn_hash_hex = ssh_connection_hash(thishost, host,
662 portstr, ruser, jmphost);
663 keyalias = options->host_key_alias ? options->host_key_alias : host;
664
665 ret = (is_include_path ? percent_dollar_expand : percent_expand)(path,
666 "C", conn_hash_hex,
667 "L", shorthost,
668 "d", pw->pw_dir,
669 "h", host,
670 "k", keyalias,
671 "l", thishost,
672 "n", original_host,
673 "p", portstr,
674 "r", ruser,
675 "u", pw->pw_name,
676 "i", uidstr,
677 "j", jmphost,
678 (char *)NULL);
679 free(host);
680 free(conn_hash_hex);
681 return ret;
682}
683
684/*
685 * Parse and execute a Match directive.
686 */
687static int
688match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp,
689 struct passwd *pw, const char *host_arg, const char *original_host,
690 const char *remote_command, int final_pass, int *want_final_pass,
691 const char *filename, int linenum)
692{
693 char *arg, *oattrib = NULL, *attrib = NULL, *cmd, *host, *criteria;
694 const char *ruser;
695 int r, this_result, result = 1, attributes = 0, negate;
696
697 /*
698 * Configuration is likely to be incomplete at this point so we
699 * must be prepared to use default values.
700 */
701 ruser = options->user == NULL ? pw->pw_name : options->user;
702 if (final_pass) {
703 host = xstrdup(options->hostname);
704 } else if (options->hostname != NULL) {
705 /* NB. Please keep in sync with ssh.c:main() */
706 host = percent_expand(options->hostname,
707 "h", host_arg, (char *)NULL);
708 } else {
709 host = xstrdup(host_arg);
710 }
711
712 debug2("checking match for '%s' host %s originally %s",
713 full_line, host, original_host);
714 while ((attrib = argv_next(acp, avp)) != NULL) {
715 /* Terminate on comment */
716 if (*attrib == '#') {
717 argv_consume(acp);
718 break;
719 }
720 attrib = oattrib = xstrdup(attrib);
721 arg = criteria = NULL;
722 this_result = 1;
723 if ((negate = (attrib[0] == '!')))
724 attrib++;
725 /* Criterion "all" has no argument and must appear alone */
726 if (strcasecmp(attrib, "all") == 0) {
727 if (attributes > 1 ||
728 ((arg = argv_next(acp, avp)) != NULL &&
729 *arg != '\0' && *arg != '#')) {
730 error("%.200s line %d: '%s' cannot be combined "
731 "with other Match attributes",
732 filename, linenum, oattrib);
733 result = -1;
734 goto out;
735 }
736 if (arg != NULL && *arg == '#')
737 argv_consume(acp); /* consume remaining args */
738 if (result)
739 result = negate ? 0 : 1;
740 goto out;
741 }
742 attributes++;
743 /* criteria "final" and "canonical" have no argument */
744 if (strcasecmp(attrib, "canonical") == 0 ||
745 strcasecmp(attrib, "final") == 0) {
746 /*
747 * If the config requests "Match final" without
748 * negation then remember this so we can perform a
749 * second pass later.
750 */
751 if (strcasecmp(attrib, "final") == 0 &&
752 want_final_pass != NULL)
753 *want_final_pass |= !negate;
754 r = !!final_pass; /* force bitmask member to boolean */
755 if (r == (negate ? 1 : 0))
756 this_result = result = 0;
757 debug3("%.200s line %d: %smatched '%s'",
758 filename, linenum,
759 this_result ? "" : "not ", oattrib);
760 goto next;
761 }
762
763 /* Keep this list in sync with below */
764 if (strprefix(attrib, "host=", 1) != NULL ||
765 strprefix(attrib, "originalhost=", 1) != NULL ||
766 strprefix(attrib, "user=", 1) != NULL ||
767 strprefix(attrib, "localuser=", 1) != NULL ||
768 strprefix(attrib, "localnetwork=", 1) != NULL ||
769 strprefix(attrib, "version=", 1) != NULL ||
770 strprefix(attrib, "tagged=", 1) != NULL ||
771 strprefix(attrib, "command=", 1) != NULL ||
772 strprefix(attrib, "exec=", 1) != NULL) {
773 arg = strchr(attrib, '=');
774 *(arg++) = '\0';
775 } else if ((arg = argv_next(acp, avp)) == NULL) {
776 error("%.200s line %d: missing argument for Match '%s'",
777 filename, linenum, oattrib);
778 result = -1;
779 goto out;
780 }
781
782 /*
783 * All other criteria require an argument, though it may
784 * be the empty string for the "tagged" and "command"
785 * options.
786 */
787 if (*arg == '\0' &&
788 strcasecmp(attrib, "tagged") != 0 &&
789 strcasecmp(attrib, "command") != 0)
790 arg = NULL;
791 if (arg == NULL || *arg == '#') {
792 error("Missing Match criteria for %s", attrib);
793 result = -1;
794 goto out;
795 }
796 if (strcasecmp(attrib, "host") == 0) {
797 criteria = xstrdup(host);
798 r = match_hostname(host, arg) == 1;
799 if (r == (negate ? 1 : 0))
800 this_result = result = 0;
801 } else if (strcasecmp(attrib, "originalhost") == 0) {
802 criteria = xstrdup(original_host);
803 r = match_hostname(original_host, arg) == 1;
804 if (r == (negate ? 1 : 0))
805 this_result = result = 0;
806 } else if (strcasecmp(attrib, "user") == 0) {
807 criteria = xstrdup(ruser);
808 r = match_pattern_list(ruser, arg, 0) == 1;
809 if (r == (negate ? 1 : 0))
810 this_result = result = 0;
811 } else if (strcasecmp(attrib, "localuser") == 0) {
812 criteria = xstrdup(pw->pw_name);
813 r = match_pattern_list(pw->pw_name, arg, 0) == 1;
814 if (r == (negate ? 1 : 0))
815 this_result = result = 0;
816 } else if (strcasecmp(attrib, "localnetwork") == 0) {
817 if (addr_match_cidr_list(NULL, arg) == -1) {
818 /* Error already printed */
819 result = -1;
820 goto out;
821 }
822 r = check_match_ifaddrs(arg) == 1;
823 if (r == (negate ? 1 : 0))
824 this_result = result = 0;
825 } else if (strcasecmp(attrib, "version") == 0) {
826 criteria = xstrdup(SSH_RELEASE);
827 r = match_pattern_list(SSH_RELEASE, arg, 0) == 1;
828 if (r == (negate ? 1 : 0))
829 this_result = result = 0;
830 } else if (strcasecmp(attrib, "tagged") == 0) {
831 criteria = xstrdup(options->tag == NULL ? "" :
832 options->tag);
833 /* Special case: empty criteria matches empty arg */
834 r = (*criteria == '\0') ? *arg == '\0' :
835 match_pattern_list(criteria, arg, 0) == 1;
836 if (r == (negate ? 1 : 0))
837 this_result = result = 0;
838 } else if (strcasecmp(attrib, "command") == 0) {
839 criteria = xstrdup(remote_command == NULL ?
840 "" : remote_command);
841 /* Special case: empty criteria matches empty arg */
842 r = (*criteria == '\0') ? *arg == '\0' :
843 match_pattern_list(criteria, arg, 0) == 1;
844 if (r == (negate ? 1 : 0))
845 this_result = result = 0;
846 } else if (strcasecmp(attrib, "sessiontype") == 0) {
847 if (options->session_type == SESSION_TYPE_SUBSYSTEM)
848 criteria = xstrdup("subsystem");
849 else if (options->session_type == SESSION_TYPE_NONE)
850 criteria = xstrdup("none");
851 else if (remote_command != NULL &&
852 *remote_command != '\0')
853 criteria = xstrdup("exec");
854 else
855 criteria = xstrdup("shell");
856 r = match_pattern_list(criteria, arg, 0) == 1;
857 if (r == (negate ? 1 : 0))
858 this_result = result = 0;
859 } else if (strcasecmp(attrib, "exec") == 0) {
860 if ((cmd = expand_match_exec_or_include_path(arg,
861 options, pw, host_arg, original_host,
862 final_pass, 0)) == NULL) {
863 fatal("%.200s line %d: failed to expand match "
864 "exec '%.100s'", filename, linenum, arg);
865 }
866 if (result != 1) {
867 /* skip execution if prior predicate failed */
868 debug3("%.200s line %d: skipped exec "
869 "\"%.100s\"", filename, linenum, cmd);
870 free(cmd);
871 goto next;
872 }
873 r = execute_in_shell(cmd);
874 if (r == -1) {
875 fatal("%.200s line %d: match exec "
876 "'%.100s' error", filename,
877 linenum, cmd);
878 }
879 criteria = xstrdup(cmd);
880 free(cmd);
881 /* Force exit status to boolean */
882 r = r == 0;
883 if (r == (negate ? 1 : 0))
884 this_result = result = 0;
885 } else {
886 error("Unsupported Match attribute %s", attrib);
887 result = -1;
888 goto out;
889 }
890 debug3("%.200s line %d: %smatched '%s%s%.100s%s' ",
891 filename, linenum, this_result ? "": "not ", oattrib,
892 criteria == NULL ? "" : " \"",
893 criteria == NULL ? "" : criteria,
894 criteria == NULL ? "" : "\"");
895 next:
896 free(criteria);
897 free(oattrib);
898 oattrib = attrib = NULL;
899 }
900 if (attributes == 0) {
901 error("One or more attributes required for Match");
902 result = -1;
903 goto out;
904 }
905 out:
906 if (result != -1)
907 debug2("match %sfound", result ? "" : "not ");
908 free(oattrib);
909 free(host);
910 return result;
911}
912
913/* Remove environment variable by pattern */
914static void
915rm_env(Options *options, const char *arg, const char *filename, int linenum)
916{
917 u_int i, j, onum_send_env = options->num_send_env;
918
919 /* Remove an environment variable */
920 for (i = 0; i < options->num_send_env; ) {
921 if (!match_pattern(options->send_env[i], arg + 1)) {
922 i++;
923 continue;
924 }
925 debug3("%s line %d: removing environment %s",
926 filename, linenum, options->send_env[i]);
927 free(options->send_env[i]);
928 options->send_env[i] = NULL;
929 for (j = i; j < options->num_send_env - 1; j++) {
930 options->send_env[j] = options->send_env[j + 1];
931 options->send_env[j + 1] = NULL;
932 }
933 options->num_send_env--;
934 /* NB. don't increment i */
935 }
936 if (onum_send_env != options->num_send_env) {
937 options->send_env = xrecallocarray(options->send_env,
938 onum_send_env, options->num_send_env,
939 sizeof(*options->send_env));
940 }
941}
942
943/*
944 * Returns the number of the token pointed to by cp or oBadOption.
945 */
946static OpCodes
947parse_token(const char *cp, const char *filename, int linenum,
948 const char *ignored_unknown)
949{
950 int i;
951
952 for (i = 0; keywords[i].name; i++)
953 if (strcmp(cp, keywords[i].name) == 0)
954 return keywords[i].opcode;
955 if (ignored_unknown != NULL &&
956 match_pattern_list(cp, ignored_unknown, 1) == 1)
957 return oIgnoredUnknownOption;
958 error("%s: line %d: Bad configuration option: %s",
959 filename, linenum, cp);
960 return oBadOption;
961}
962
963static void
964free_canon_cnames(struct allowed_cname *cnames, u_int n)
965{
966 u_int i;
967
968 if (cnames == NULL || n == 0)
969 return;
970 for (i = 0; i < n; i++) {
971 free(cnames[i].source_list);
972 free(cnames[i].target_list);
973 }
974 free(cnames);
975}
976
977/* Multistate option parsing */
978struct multistate {
979 char *key;
980 int value;
981};
982static const struct multistate multistate_flag[] = {
983 { "true", 1 },
984 { "false", 0 },
985 { "yes", 1 },
986 { "no", 0 },
987 { NULL, -1 }
988};
989static const struct multistate multistate_yesnoask[] = {
990 { "true", 1 },
991 { "false", 0 },
992 { "yes", 1 },
993 { "no", 0 },
994 { "ask", 2 },
995 { NULL, -1 }
996};
997static const struct multistate multistate_strict_hostkey[] = {
998 { "true", SSH_STRICT_HOSTKEY_YES },
999 { "false", SSH_STRICT_HOSTKEY_OFF },
1000 { "yes", SSH_STRICT_HOSTKEY_YES },
1001 { "no", SSH_STRICT_HOSTKEY_OFF },
1002 { "ask", SSH_STRICT_HOSTKEY_ASK },
1003 { "off", SSH_STRICT_HOSTKEY_OFF },
1004 { "accept-new", SSH_STRICT_HOSTKEY_NEW },
1005 { NULL, -1 }
1006};
1007static const struct multistate multistate_yesnoaskconfirm[] = {
1008 { "true", 1 },
1009 { "false", 0 },
1010 { "yes", 1 },
1011 { "no", 0 },
1012 { "ask", 2 },
1013 { "confirm", 3 },
1014 { NULL, -1 }
1015};
1016static const struct multistate multistate_addressfamily[] = {
1017 { "inet", AF_INET },
1018 { "inet6", AF_INET6 },
1019 { "any", AF_UNSPEC },
1020 { NULL, -1 }
1021};
1022static const struct multistate multistate_controlmaster[] = {
1023 { "true", SSHCTL_MASTER_YES },
1024 { "yes", SSHCTL_MASTER_YES },
1025 { "false", SSHCTL_MASTER_NO },
1026 { "no", SSHCTL_MASTER_NO },
1027 { "auto", SSHCTL_MASTER_AUTO },
1028 { "ask", SSHCTL_MASTER_ASK },
1029 { "autoask", SSHCTL_MASTER_AUTO_ASK },
1030 { NULL, -1 }
1031};
1032static const struct multistate multistate_tunnel[] = {
1033 { "ethernet", SSH_TUNMODE_ETHERNET },
1034 { "point-to-point", SSH_TUNMODE_POINTOPOINT },
1035 { "true", SSH_TUNMODE_DEFAULT },
1036 { "yes", SSH_TUNMODE_DEFAULT },
1037 { "false", SSH_TUNMODE_NO },
1038 { "no", SSH_TUNMODE_NO },
1039 { NULL, -1 }
1040};
1041static const struct multistate multistate_requesttty[] = {
1042 { "true", REQUEST_TTY_YES },
1043 { "yes", REQUEST_TTY_YES },
1044 { "false", REQUEST_TTY_NO },
1045 { "no", REQUEST_TTY_NO },
1046 { "force", REQUEST_TTY_FORCE },
1047 { "auto", REQUEST_TTY_AUTO },
1048 { NULL, -1 }
1049};
1050static const struct multistate multistate_sessiontype[] = {
1051 { "none", SESSION_TYPE_NONE },
1052 { "subsystem", SESSION_TYPE_SUBSYSTEM },
1053 { "default", SESSION_TYPE_DEFAULT },
1054 { NULL, -1 }
1055};
1056static const struct multistate multistate_canonicalizehostname[] = {
1057 { "true", SSH_CANONICALISE_YES },
1058 { "false", SSH_CANONICALISE_NO },
1059 { "yes", SSH_CANONICALISE_YES },
1060 { "no", SSH_CANONICALISE_NO },
1061 { "always", SSH_CANONICALISE_ALWAYS },
1062 { NULL, -1 }
1063};
1064static const struct multistate multistate_pubkey_auth[] = {
1065 { "true", SSH_PUBKEY_AUTH_ALL },
1066 { "false", SSH_PUBKEY_AUTH_NO },
1067 { "yes", SSH_PUBKEY_AUTH_ALL },
1068 { "no", SSH_PUBKEY_AUTH_NO },
1069 { "unbound", SSH_PUBKEY_AUTH_UNBOUND },
1070 { "host-bound", SSH_PUBKEY_AUTH_HBOUND },
1071 { NULL, -1 }
1072};
1073static const struct multistate multistate_compression[] = {
1074#ifdef WITH_ZLIB
1075 { "yes", COMP_DELAYED },
1076#endif
1077 { "no", COMP_NONE },
1078 { NULL, -1 }
1079};
1080/* XXX this will need to be replaced with a bitmask if we add more flags */
1081static const struct multistate multistate_warnweakcrypto[] = {
1082 { "true", 1 },
1083 { "false", 0 },
1084 { "yes", 1 },
1085 { "no", 0 },
1086 { "no-pq-kex", 0 },
1087 { NULL, -1 }
1088};
1089
1090static int
1091parse_multistate_value(const char *arg, const char *filename, int linenum,
1092 const struct multistate *multistate_ptr)
1093{
1094 int i;
1095
1096 if (!arg || *arg == '\0') {
1097 error("%s line %d: missing argument.", filename, linenum);
1098 return -1;
1099 }
1100 for (i = 0; multistate_ptr[i].key != NULL; i++) {
1101 if (strcasecmp(arg, multistate_ptr[i].key) == 0)
1102 return multistate_ptr[i].value;
1103 }
1104 return -1;
1105}
1106
1107/*
1108 * Processes a single option line as used in the configuration files. This
1109 * only sets those values that have not already been set.
1110 */
1111int
1112process_config_line(Options *options, struct passwd *pw, const char *host,
1113 const char *original_host, const char *remote_command, char *line,
1114 const char *filename, int linenum, int *activep, int flags)
1115{
1116 return process_config_line_depth(options, pw, host, original_host,
1117 remote_command, line, filename, linenum, activep, flags, NULL, 0);
1118}
1119
1120#define WHITESPACE " \t\r\n"
1121static int
1122process_config_line_depth(Options *options, struct passwd *pw, const char *host,
1123 const char *original_host, const char *remote_command, char *line,
1124 const char *filename, int linenum, int *activep, int flags,
1125 int *want_final_pass, int depth)
1126{
1127 char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
1128 char **cpptr, ***cppptr, fwdarg[256];
1129 u_int i, *uintptr, max_entries = 0;
1130 int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
1131 int remotefwd, dynamicfwd, ca_only = 0, found = 0;
1132 LogLevel *log_level_ptr;
1133 SyslogFacility *log_facility_ptr;
1134 long long val64;
1135 size_t len;
1136 struct Forward fwd;
1137 const struct multistate *multistate_ptr;
1138 glob_t gl;
1139 const char *errstr;
1140 char **oav = NULL, **av;
1141 int oac = 0, ac;
1142 int ret = -1;
1143 struct allowed_cname *cnames = NULL;
1144 u_int ncnames = 0;
1145 char **strs = NULL; /* string array arguments; freed implicitly */
1146 u_int nstrs = 0;
1147
1148 if (activep == NULL) { /* We are processing a command line directive */
1149 cmdline = 1;
1150 activep = &cmdline;
1151 }
1152
1153 /* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1154 if ((len = strlen(line)) == 0)
1155 return 0;
1156 for (len--; len > 0; len--) {
1157 if (strchr(WHITESPACE "\f", line[len]) == NULL)
1158 break;
1159 line[len] = '\0';
1160 }
1161
1162 str = line;
1163 /* Get the keyword. (Each line is supposed to begin with a keyword). */
1164 if ((keyword = strdelim(&str)) == NULL)
1165 return 0;
1166 /* Ignore leading whitespace. */
1167 if (*keyword == '\0')
1168 keyword = strdelim(&str);
1169 if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
1170 return 0;
1171 /* Match lowercase keyword */
1172 lowercase(keyword);
1173
1174 /* Prepare to parse remainder of line */
1175 if (str != NULL)
1176 str += strspn(str, WHITESPACE);
1177 if (str == NULL || *str == '\0') {
1178 error("%s line %d: no argument after keyword \"%s\"",
1179 filename, linenum, keyword);
1180 return -1;
1181 }
1182 opcode = parse_token(keyword, filename, linenum,
1183 options->ignored_unknown);
1184 if (argv_split(str, &oac, &oav, 1) != 0) {
1185 error("%s line %d: invalid quotes", filename, linenum);
1186 return -1;
1187 }
1188 ac = oac;
1189 av = oav;
1190
1191 switch (opcode) {
1192 case oBadOption:
1193 /* don't panic, but count bad options */
1194 goto out;
1195 case oIgnore:
1196 argv_consume(&ac);
1197 break;
1198 case oIgnoredUnknownOption:
1199 debug("%s line %d: Ignored unknown option \"%s\"",
1200 filename, linenum, keyword);
1201 argv_consume(&ac);
1202 break;
1203 case oConnectTimeout:
1204 intptr = &options->connection_timeout;
1205parse_time:
1206 arg = argv_next(&ac, &av);
1207 if (!arg || *arg == '\0') {
1208 error("%s line %d: missing time value.",
1209 filename, linenum);
1210 goto out;
1211 }
1212 if (strcmp(arg, "none") == 0)
1213 value = -1;
1214 else if ((value = convtime(arg)) == -1) {
1215 error("%s line %d: invalid time value.",
1216 filename, linenum);
1217 goto out;
1218 }
1219 if (*activep && *intptr == -1)
1220 *intptr = value;
1221 break;
1222
1223 case oForwardAgent:
1224 intptr = &options->forward_agent;
1225
1226 arg = argv_next(&ac, &av);
1227 if (!arg || *arg == '\0') {
1228 error("%s line %d: missing argument.",
1229 filename, linenum);
1230 goto out;
1231 }
1232
1233 value = -1;
1234 multistate_ptr = multistate_flag;
1235 for (i = 0; multistate_ptr[i].key != NULL; i++) {
1236 if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
1237 value = multistate_ptr[i].value;
1238 break;
1239 }
1240 }
1241 if (value != -1) {
1242 if (*activep && *intptr == -1)
1243 *intptr = value;
1244 break;
1245 }
1246 /* ForwardAgent wasn't 'yes' or 'no', assume a path */
1247 if (*activep && *intptr == -1)
1248 *intptr = 1;
1249
1250 charptr = &options->forward_agent_sock_path;
1251 goto parse_agent_path;
1252
1253 case oForwardX11:
1254 intptr = &options->forward_x11;
1255 parse_flag:
1256 multistate_ptr = multistate_flag;
1257 parse_multistate:
1258 arg = argv_next(&ac, &av);
1259 if ((value = parse_multistate_value(arg, filename, linenum,
1260 multistate_ptr)) == -1) {
1261 error("%s line %d: unsupported option \"%s\".",
1262 filename, linenum, arg);
1263 goto out;
1264 }
1265 if (*activep && *intptr == -1)
1266 *intptr = value;
1267 break;
1268
1269 case oForwardX11Trusted:
1270 intptr = &options->forward_x11_trusted;
1271 goto parse_flag;
1272
1273 case oForwardX11Timeout:
1274 intptr = &options->forward_x11_timeout;
1275 goto parse_time;
1276
1277 case oGatewayPorts:
1278 intptr = &options->fwd_opts.gateway_ports;
1279 goto parse_flag;
1280
1281 case oExitOnForwardFailure:
1282 intptr = &options->exit_on_forward_failure;
1283 goto parse_flag;
1284
1285 case oPasswordAuthentication:
1286 intptr = &options->password_authentication;
1287 goto parse_flag;
1288
1289 case oKbdInteractiveAuthentication:
1290 intptr = &options->kbd_interactive_authentication;
1291 goto parse_flag;
1292
1293 case oKbdInteractiveDevices:
1294 charptr = &options->kbd_interactive_devices;
1295 goto parse_string;
1296
1297 case oPubkeyAuthentication:
1298 multistate_ptr = multistate_pubkey_auth;
1299 intptr = &options->pubkey_authentication;
1300 goto parse_multistate;
1301
1302 case oHostbasedAuthentication:
1303 intptr = &options->hostbased_authentication;
1304 goto parse_flag;
1305
1306 case oGssAuthentication:
1307 intptr = &options->gss_authentication;
1308 goto parse_flag;
1309
1310 case oGssDelegateCreds:
1311 intptr = &options->gss_deleg_creds;
1312 goto parse_flag;
1313
1314 case oBatchMode:
1315 intptr = &options->batch_mode;
1316 goto parse_flag;
1317
1318 case oCheckHostIP:
1319 intptr = &options->check_host_ip;
1320 goto parse_flag;
1321
1322 case oVerifyHostKeyDNS:
1323 intptr = &options->verify_host_key_dns;
1324 multistate_ptr = multistate_yesnoask;
1325 goto parse_multistate;
1326
1327 case oStrictHostKeyChecking:
1328 intptr = &options->strict_host_key_checking;
1329 multistate_ptr = multistate_strict_hostkey;
1330 goto parse_multistate;
1331
1332 case oCompression:
1333 intptr = &options->compression;
1334 multistate_ptr = multistate_compression;
1335 goto parse_multistate;
1336
1337 case oTCPKeepAlive:
1338 intptr = &options->tcp_keep_alive;
1339 goto parse_flag;
1340
1341 case oNoHostAuthenticationForLocalhost:
1342 intptr = &options->no_host_authentication_for_localhost;
1343 goto parse_flag;
1344
1345 case oNumberOfPasswordPrompts:
1346 intptr = &options->number_of_password_prompts;
1347 goto parse_int;
1348
1349 case oRekeyLimit:
1350 arg = argv_next(&ac, &av);
1351 if (!arg || *arg == '\0') {
1352 error("%.200s line %d: Missing argument.", filename,
1353 linenum);
1354 goto out;
1355 }
1356 if (strcmp(arg, "default") == 0) {
1357 val64 = 0;
1358 } else {
1359 if (scan_scaled(arg, &val64) == -1) {
1360 error("%.200s line %d: Bad number '%s': %s",
1361 filename, linenum, arg, strerror(errno));
1362 goto out;
1363 }
1364 if (val64 != 0 && val64 < 16) {
1365 error("%.200s line %d: RekeyLimit too small",
1366 filename, linenum);
1367 goto out;
1368 }
1369 }
1370 if (*activep && options->rekey_limit == -1)
1371 options->rekey_limit = val64;
1372 if (ac != 0) { /* optional rekey interval present */
1373 if (strcmp(av[0], "none") == 0) {
1374 (void)argv_next(&ac, &av); /* discard */
1375 break;
1376 }
1377 intptr = &options->rekey_interval;
1378 goto parse_time;
1379 }
1380 break;
1381
1382 case oIdentityFile:
1383 arg = argv_next(&ac, &av);
1384 if (!arg || *arg == '\0') {
1385 error("%.200s line %d: Missing argument.",
1386 filename, linenum);
1387 goto out;
1388 }
1389 if (*activep) {
1390 intptr = &options->num_identity_files;
1391 if (*intptr >= SSH_MAX_IDENTITY_FILES) {
1392 error("%.200s line %d: Too many identity files "
1393 "specified (max %d).", filename, linenum,
1394 SSH_MAX_IDENTITY_FILES);
1395 goto out;
1396 }
1397 add_identity_file(options, NULL,
1398 arg, flags & SSHCONF_USERCONF);
1399 }
1400 break;
1401
1402 case oCertificateFile:
1403 arg = argv_next(&ac, &av);
1404 if (!arg || *arg == '\0') {
1405 error("%.200s line %d: Missing argument.",
1406 filename, linenum);
1407 goto out;
1408 }
1409 if (*activep) {
1410 intptr = &options->num_certificate_files;
1411 if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
1412 error("%.200s line %d: Too many certificate "
1413 "files specified (max %d).",
1414 filename, linenum,
1415 SSH_MAX_CERTIFICATE_FILES);
1416 goto out;
1417 }
1418 add_certificate_file(options, arg,
1419 flags & SSHCONF_USERCONF);
1420 }
1421 break;
1422
1423 case oXAuthLocation:
1424 charptr=&options->xauth_location;
1425 goto parse_string;
1426
1427 case oUser:
1428 charptr = &options->user;
1429parse_string:
1430 arg = argv_next(&ac, &av);
1431 if (!arg || *arg == '\0') {
1432 error("%.200s line %d: Missing argument.",
1433 filename, linenum);
1434 goto out;
1435 }
1436 if (*activep && *charptr == NULL)
1437 *charptr = xstrdup(arg);
1438 break;
1439
1440 case oGlobalKnownHostsFile:
1441 cpptr = (char **)&options->system_hostfiles;
1442 uintptr = &options->num_system_hostfiles;
1443 max_entries = SSH_MAX_HOSTS_FILES;
1444parse_char_array:
1445 i = 0;
1446 value = *uintptr == 0; /* was array empty when we started? */
1447 while ((arg = argv_next(&ac, &av)) != NULL) {
1448 if (*arg == '\0') {
1449 error("%s line %d: keyword %s empty argument",
1450 filename, linenum, keyword);
1451 goto out;
1452 }
1453 /* Allow "none" only in first position */
1454 if (strcasecmp(arg, "none") == 0) {
1455 if (i > 0 || ac > 0) {
1456 error("%s line %d: keyword %s \"none\" "
1457 "argument must appear alone.",
1458 filename, linenum, keyword);
1459 goto out;
1460 }
1461 }
1462 i++;
1463 if (*activep && value) {
1464 if ((*uintptr) >= max_entries) {
1465 error("%s line %d: too many %s "
1466 "entries.", filename, linenum,
1467 keyword);
1468 goto out;
1469 }
1470 cpptr[(*uintptr)++] = xstrdup(arg);
1471 }
1472 }
1473 break;
1474
1475 case oUserKnownHostsFile:
1476 cpptr = (char **)&options->user_hostfiles;
1477 uintptr = &options->num_user_hostfiles;
1478 max_entries = SSH_MAX_HOSTS_FILES;
1479 goto parse_char_array;
1480
1481 case oHostname:
1482 charptr = &options->hostname;
1483 goto parse_string;
1484
1485 case oTag:
1486 charptr = &options->tag;
1487 goto parse_string;
1488
1489 case oHostKeyAlias:
1490 charptr = &options->host_key_alias;
1491 goto parse_string;
1492
1493 case oPreferredAuthentications:
1494 charptr = &options->preferred_authentications;
1495 goto parse_string;
1496
1497 case oBindAddress:
1498 charptr = &options->bind_address;
1499 goto parse_string;
1500
1501 case oBindInterface:
1502 charptr = &options->bind_interface;
1503 goto parse_string;
1504
1505 case oPKCS11Provider:
1506 charptr = &options->pkcs11_provider;
1507 goto parse_string;
1508
1509 case oSecurityKeyProvider:
1510 charptr = &options->sk_provider;
1511 goto parse_string;
1512
1513 case oKnownHostsCommand:
1514 charptr = &options->known_hosts_command;
1515 goto parse_command;
1516
1517 case oProxyCommand:
1518 charptr = &options->proxy_command;
1519 /* Ignore ProxyCommand if ProxyJump already specified */
1520 if (options->jump_host != NULL)
1521 charptr = &options->jump_host; /* Skip below */
1522parse_command:
1523 if (str == NULL) {
1524 error("%.200s line %d: Missing argument.",
1525 filename, linenum);
1526 goto out;
1527 }
1528 len = strspn(str, WHITESPACE "=");
1529 if (*activep && *charptr == NULL)
1530 *charptr = xstrdup(str + len);
1531 argv_consume(&ac);
1532 break;
1533
1534 case oProxyJump:
1535 if (str == NULL) {
1536 error("%.200s line %d: Missing argument.",
1537 filename, linenum);
1538 goto out;
1539 }
1540 len = strspn(str, WHITESPACE "=");
1541 /* XXX use argv? */
1542 if (parse_jump(str + len, options, *activep) == -1) {
1543 error("%.200s line %d: Invalid ProxyJump \"%s\"",
1544 filename, linenum, str + len);
1545 goto out;
1546 }
1547 argv_consume(&ac);
1548 break;
1549
1550 case oPort:
1551 arg = argv_next(&ac, &av);
1552 if (!arg || *arg == '\0') {
1553 error("%.200s line %d: Missing argument.",
1554 filename, linenum);
1555 goto out;
1556 }
1557 value = a2port(arg);
1558 if (value <= 0) {
1559 error("%.200s line %d: Bad port '%s'.",
1560 filename, linenum, arg);
1561 goto out;
1562 }
1563 if (*activep && options->port == -1)
1564 options->port = value;
1565 break;
1566
1567 case oConnectionAttempts:
1568 intptr = &options->connection_attempts;
1569parse_int:
1570 arg = argv_next(&ac, &av);
1571 if ((errstr = atoi_err(arg, &value)) != NULL) {
1572 error("%s line %d: integer value %s.",
1573 filename, linenum, errstr);
1574 goto out;
1575 }
1576 if (*activep && *intptr == -1)
1577 *intptr = value;
1578 break;
1579
1580 case oCiphers:
1581 arg = argv_next(&ac, &av);
1582 if (!arg || *arg == '\0') {
1583 error("%.200s line %d: Missing argument.",
1584 filename, linenum);
1585 goto out;
1586 }
1587 if (*arg != '-' &&
1588 !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
1589 error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
1590 filename, linenum, arg ? arg : "<NONE>");
1591 goto out;
1592 }
1593 if (*activep && options->ciphers == NULL)
1594 options->ciphers = xstrdup(arg);
1595 break;
1596
1597 case oMacs:
1598 arg = argv_next(&ac, &av);
1599 if (!arg || *arg == '\0') {
1600 error("%.200s line %d: Missing argument.",
1601 filename, linenum);
1602 goto out;
1603 }
1604 if (*arg != '-' &&
1605 !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
1606 error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
1607 filename, linenum, arg ? arg : "<NONE>");
1608 goto out;
1609 }
1610 if (*activep && options->macs == NULL)
1611 options->macs = xstrdup(arg);
1612 break;
1613
1614 case oKexAlgorithms:
1615 arg = argv_next(&ac, &av);
1616 if (!arg || *arg == '\0') {
1617 error("%.200s line %d: Missing argument.",
1618 filename, linenum);
1619 goto out;
1620 }
1621 if (*arg != '-' &&
1622 !kex_names_valid(*arg == '+' || *arg == '^' ?
1623 arg + 1 : arg)) {
1624 error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
1625 filename, linenum, arg ? arg : "<NONE>");
1626 goto out;
1627 }
1628 if (*activep && options->kex_algorithms == NULL)
1629 options->kex_algorithms = xstrdup(arg);
1630 break;
1631
1632 case oHostKeyAlgorithms:
1633 charptr = &options->hostkeyalgorithms;
1634 ca_only = 0;
1635parse_pubkey_algos:
1636 arg = argv_next(&ac, &av);
1637 if (!arg || *arg == '\0') {
1638 error("%.200s line %d: Missing argument.",
1639 filename, linenum);
1640 goto out;
1641 }
1642 if (*arg != '-' &&
1643 !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
1644 arg + 1 : arg, 1, ca_only)) {
1645 error("%s line %d: Bad key types '%s'.",
1646 filename, linenum, arg ? arg : "<NONE>");
1647 goto out;
1648 }
1649 if (*activep && *charptr == NULL)
1650 *charptr = xstrdup(arg);
1651 break;
1652
1653 case oCASignatureAlgorithms:
1654 charptr = &options->ca_sign_algorithms;
1655 ca_only = 1;
1656 goto parse_pubkey_algos;
1657
1658 case oLogLevel:
1659 log_level_ptr = &options->log_level;
1660 arg = argv_next(&ac, &av);
1661 value = log_level_number(arg);
1662 if (value == SYSLOG_LEVEL_NOT_SET) {
1663 error("%.200s line %d: unsupported log level '%s'",
1664 filename, linenum, arg ? arg : "<NONE>");
1665 goto out;
1666 }
1667 if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
1668 *log_level_ptr = (LogLevel) value;
1669 break;
1670
1671 case oLogFacility:
1672 log_facility_ptr = &options->log_facility;
1673 arg = argv_next(&ac, &av);
1674 value = log_facility_number(arg);
1675 if (value == SYSLOG_FACILITY_NOT_SET) {
1676 error("%.200s line %d: unsupported log facility '%s'",
1677 filename, linenum, arg ? arg : "<NONE>");
1678 goto out;
1679 }
1680 if (*log_facility_ptr == -1)
1681 *log_facility_ptr = (SyslogFacility) value;
1682 break;
1683
1684 case oLogVerbose:
1685 cppptr = &options->log_verbose;
1686 uintptr = &options->num_log_verbose;
1687 i = 0;
1688 while ((arg = argv_next(&ac, &av)) != NULL) {
1689 if (*arg == '\0') {
1690 error("%s line %d: keyword %s empty argument",
1691 filename, linenum, keyword);
1692 goto out;
1693 }
1694 /* Allow "none" only in first position */
1695 if (strcasecmp(arg, "none") == 0) {
1696 if (i > 0 || ac > 0) {
1697 error("%s line %d: keyword %s \"none\" "
1698 "argument must appear alone.",
1699 filename, linenum, keyword);
1700 goto out;
1701 }
1702 }
1703 i++;
1704 if (*activep && *uintptr == 0) {
1705 *cppptr = xrecallocarray(*cppptr, *uintptr,
1706 *uintptr + 1, sizeof(**cppptr));
1707 (*cppptr)[(*uintptr)++] = xstrdup(arg);
1708 }
1709 }
1710 break;
1711
1712 case oLocalForward:
1713 case oRemoteForward:
1714 case oDynamicForward:
1715 arg = argv_next(&ac, &av);
1716 if (!arg || *arg == '\0') {
1717 error("%.200s line %d: Missing argument.",
1718 filename, linenum);
1719 goto out;
1720 }
1721
1722 remotefwd = (opcode == oRemoteForward);
1723 dynamicfwd = (opcode == oDynamicForward);
1724
1725 if (!dynamicfwd) {
1726 arg2 = argv_next(&ac, &av);
1727 if (arg2 == NULL || *arg2 == '\0') {
1728 if (remotefwd)
1729 dynamicfwd = 1;
1730 else {
1731 error("%.200s line %d: Missing target "
1732 "argument.", filename, linenum);
1733 goto out;
1734 }
1735 } else {
1736 /* construct a string for parse_forward */
1737 snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
1738 arg2);
1739 }
1740 }
1741 if (dynamicfwd)
1742 strlcpy(fwdarg, arg, sizeof(fwdarg));
1743
1744 if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
1745 error("%.200s line %d: Bad forwarding specification.",
1746 filename, linenum);
1747 goto out;
1748 }
1749
1750 if (*activep) {
1751 if (remotefwd) {
1752 add_remote_forward(options, &fwd);
1753 } else {
1754 add_local_forward(options, &fwd);
1755 }
1756 }
1757 break;
1758
1759 case oPermitRemoteOpen:
1760 uintptr = &options->num_permitted_remote_opens;
1761 cppptr = &options->permitted_remote_opens;
1762 found = *uintptr == 0;
1763 while ((arg = argv_next(&ac, &av)) != NULL) {
1764 arg2 = xstrdup(arg);
1765 /* Allow any/none only in first position */
1766 if (strcasecmp(arg, "none") == 0 ||
1767 strcasecmp(arg, "any") == 0) {
1768 if (nstrs > 0 || ac > 0) {
1769 error("%s line %d: keyword %s \"%s\" "
1770 "argument must appear alone.",
1771 filename, linenum, keyword, arg);
1772 free(arg2);
1773 goto out;
1774 }
1775 } else {
1776 p = hpdelim(&arg);
1777 if (p == NULL) {
1778 fatal("%s line %d: missing host in %s",
1779 filename, linenum,
1780 lookup_opcode_name(opcode));
1781 }
1782 p = cleanhostname(p);
1783 /*
1784 * don't want to use permitopen_port to avoid
1785 * dependency on channels.[ch] here.
1786 */
1787 if (arg == NULL || (strcmp(arg, "*") != 0 &&
1788 a2port(arg) <= 0)) {
1789 fatal("%s line %d: bad port number "
1790 "in %s", filename, linenum,
1791 lookup_opcode_name(opcode));
1792 }
1793 }
1794 opt_array_append(filename, linenum,
1795 lookup_opcode_name(opcode),
1796 &strs, &nstrs, arg2);
1797 free(arg2);
1798 }
1799 if (nstrs == 0)
1800 fatal("%s line %d: missing %s specification",
1801 filename, linenum, lookup_opcode_name(opcode));
1802 if (found && *activep) {
1803 *cppptr = strs;
1804 *uintptr = nstrs;
1805 strs = NULL; /* transferred */
1806 nstrs = 0;
1807 }
1808 break;
1809
1810 case oClearAllForwardings:
1811 intptr = &options->clear_forwardings;
1812 goto parse_flag;
1813
1814 case oHost:
1815 if (cmdline) {
1816 error("Host directive not supported as a command-line "
1817 "option");
1818 goto out;
1819 }
1820 *activep = 0;
1821 arg2 = NULL;
1822 while ((arg = argv_next(&ac, &av)) != NULL) {
1823 if (*arg == '\0') {
1824 error("%s line %d: keyword %s empty argument",
1825 filename, linenum, keyword);
1826 goto out;
1827 }
1828 if ((flags & SSHCONF_NEVERMATCH) != 0) {
1829 argv_consume(&ac);
1830 break;
1831 }
1832 negated = *arg == '!';
1833 if (negated)
1834 arg++;
1835 if (match_pattern(host, arg)) {
1836 if (negated) {
1837 debug("%.200s line %d: Skipping Host "
1838 "block because of negated match "
1839 "for %.100s", filename, linenum,
1840 arg);
1841 *activep = 0;
1842 argv_consume(&ac);
1843 break;
1844 }
1845 if (!*activep)
1846 arg2 = arg; /* logged below */
1847 *activep = 1;
1848 }
1849 }
1850 if (*activep)
1851 debug("%.200s line %d: Applying options for %.100s",
1852 filename, linenum, arg2);
1853 break;
1854
1855 case oMatch:
1856 if (cmdline) {
1857 error("Host directive not supported as a command-line "
1858 "option");
1859 goto out;
1860 }
1861 value = match_cfg_line(options, str, &ac, &av, pw, host,
1862 original_host, remote_command, flags & SSHCONF_FINAL,
1863 want_final_pass, filename, linenum);
1864 if (value < 0) {
1865 error("%.200s line %d: Bad Match condition", filename,
1866 linenum);
1867 goto out;
1868 }
1869 *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
1870 break;
1871
1872 case oEscapeChar:
1873 intptr = &options->escape_char;
1874 arg = argv_next(&ac, &av);
1875 if (!arg || *arg == '\0') {
1876 error("%.200s line %d: Missing argument.",
1877 filename, linenum);
1878 goto out;
1879 }
1880 if (strcmp(arg, "none") == 0)
1881 value = SSH_ESCAPECHAR_NONE;
1882 else if (arg[1] == '\0')
1883 value = (u_char) arg[0];
1884 else if (arg[0] == '^' && arg[2] == 0 &&
1885 (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
1886 value = (u_char) arg[1] & 31;
1887 else {
1888 error("%.200s line %d: Bad escape character.",
1889 filename, linenum);
1890 goto out;
1891 }
1892 if (*activep && *intptr == -1)
1893 *intptr = value;
1894 break;
1895
1896 case oAddressFamily:
1897 intptr = &options->address_family;
1898 multistate_ptr = multistate_addressfamily;
1899 goto parse_multistate;
1900
1901 case oEnableSSHKeysign:
1902 intptr = &options->enable_ssh_keysign;
1903 goto parse_flag;
1904
1905 case oIdentitiesOnly:
1906 intptr = &options->identities_only;
1907 goto parse_flag;
1908
1909 case oServerAliveInterval:
1910 intptr = &options->server_alive_interval;
1911 goto parse_time;
1912
1913 case oServerAliveCountMax:
1914 intptr = &options->server_alive_count_max;
1915 goto parse_int;
1916
1917 case oSendEnv:
1918 /* XXX appends to list; doesn't respect first-match-wins */
1919 while ((arg = argv_next(&ac, &av)) != NULL) {
1920 if (*arg == '\0' || strchr(arg, '=') != NULL) {
1921 error("%s line %d: Invalid environment name.",
1922 filename, linenum);
1923 goto out;
1924 }
1925 found = 1;
1926 if (!*activep)
1927 continue;
1928 if (*arg == '-') {
1929 /* Removing an env var */
1930 rm_env(options, arg, filename, linenum);
1931 continue;
1932 }
1933 opt_array_append(filename, linenum,
1934 lookup_opcode_name(opcode),
1935 &options->send_env, &options->num_send_env, arg);
1936 }
1937 if (!found) {
1938 fatal("%s line %d: no %s specified",
1939 filename, linenum, keyword);
1940 }
1941 break;
1942
1943 case oSetEnv:
1944 found = options->num_setenv == 0;
1945 while ((arg = argv_next(&ac, &av)) != NULL) {
1946 if (strchr(arg, '=') == NULL) {
1947 error("%s line %d: Invalid SetEnv.",
1948 filename, linenum);
1949 goto out;
1950 }
1951 if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
1952 debug2("%s line %d: ignoring duplicate env "
1953 "name \"%.64s\"", filename, linenum, arg);
1954 continue;
1955 }
1956 opt_array_append(filename, linenum,
1957 lookup_opcode_name(opcode),
1958 &strs, &nstrs, arg);
1959 }
1960 if (nstrs == 0) {
1961 fatal("%s line %d: no %s specified",
1962 filename, linenum, keyword);
1963 }
1964 if (found && *activep) {
1965 options->setenv = strs;
1966 options->num_setenv = nstrs;
1967 strs = NULL; /* transferred */
1968 nstrs = 0;
1969 }
1970 break;
1971
1972 case oControlPath:
1973 charptr = &options->control_path;
1974 goto parse_string;
1975
1976 case oControlMaster:
1977 intptr = &options->control_master;
1978 multistate_ptr = multistate_controlmaster;
1979 goto parse_multistate;
1980
1981 case oControlPersist:
1982 /* no/false/yes/true, or a time spec */
1983 intptr = &options->control_persist;
1984 arg = argv_next(&ac, &av);
1985 if (!arg || *arg == '\0') {
1986 error("%.200s line %d: Missing ControlPersist"
1987 " argument.", filename, linenum);
1988 goto out;
1989 }
1990 value = 0;
1991 value2 = 0; /* timeout */
1992 if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
1993 value = 0;
1994 else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
1995 value = 1;
1996 else if ((value2 = convtime(arg)) >= 0)
1997 value = 1;
1998 else {
1999 error("%.200s line %d: Bad ControlPersist argument.",
2000 filename, linenum);
2001 goto out;
2002 }
2003 if (*activep && *intptr == -1) {
2004 *intptr = value;
2005 options->control_persist_timeout = value2;
2006 }
2007 break;
2008
2009 case oHashKnownHosts:
2010 intptr = &options->hash_known_hosts;
2011 goto parse_flag;
2012
2013 case oTunnel:
2014 intptr = &options->tun_open;
2015 multistate_ptr = multistate_tunnel;
2016 goto parse_multistate;
2017
2018 case oTunnelDevice:
2019 arg = argv_next(&ac, &av);
2020 if (!arg || *arg == '\0') {
2021 error("%.200s line %d: Missing argument.",
2022 filename, linenum);
2023 goto out;
2024 }
2025 value = a2tun(arg, &value2);
2026 if (value == SSH_TUNID_ERR) {
2027 error("%.200s line %d: Bad tun device.",
2028 filename, linenum);
2029 goto out;
2030 }
2031 if (*activep && options->tun_local == -1) {
2032 options->tun_local = value;
2033 options->tun_remote = value2;
2034 }
2035 break;
2036
2037 case oLocalCommand:
2038 charptr = &options->local_command;
2039 goto parse_command;
2040
2041 case oPermitLocalCommand:
2042 intptr = &options->permit_local_command;
2043 goto parse_flag;
2044
2045 case oRemoteCommand:
2046 charptr = &options->remote_command;
2047 goto parse_command;
2048
2049 case oVisualHostKey:
2050 intptr = &options->visual_host_key;
2051 goto parse_flag;
2052
2053 case oInclude:
2054 if (cmdline) {
2055 error("Include directive not supported as a "
2056 "command-line option");
2057 goto out;
2058 }
2059 value = 0;
2060 while ((arg = argv_next(&ac, &av)) != NULL) {
2061 if (*arg == '\0') {
2062 error("%s line %d: keyword %s empty argument",
2063 filename, linenum, keyword);
2064 goto out;
2065 }
2066 /* Expand %tokens and environment variables */
2067 if ((p = expand_match_exec_or_include_path(arg,
2068 options, pw, host, original_host,
2069 flags & SSHCONF_FINAL, 1)) == NULL) {
2070 error("%.200s line %d: Unable to expand user "
2071 "config file '%.100s'",
2072 filename, linenum, arg);
2073 continue;
2074 }
2075 /*
2076 * Ensure all paths are anchored. User configuration
2077 * files may begin with '~/' but system configurations
2078 * must not. If the path is relative, then treat it
2079 * as living in ~/.ssh for user configurations or
2080 * /etc/ssh for system ones.
2081 */
2082 if (*p == '~' && (flags & SSHCONF_USERCONF) == 0) {
2083 error("%.200s line %d: bad include path %s.",
2084 filename, linenum, p);
2085 goto out;
2086 }
2087 if (!path_absolute(p) && *p != '~') {
2088 xasprintf(&arg2, "%s/%s",
2089 (flags & SSHCONF_USERCONF) ?
2090 "~/" _PATH_SSH_USER_DIR : SSHDIR, p);
2091 } else {
2092 arg2 = xstrdup(p);
2093 }
2094 free(p);
2095 memset(&gl, 0, sizeof(gl));
2096 r = glob(arg2, GLOB_TILDE, NULL, &gl);
2097 if (r == GLOB_NOMATCH) {
2098 debug("%.200s line %d: include %s matched no "
2099 "files",filename, linenum, arg2);
2100 free(arg2);
2101 continue;
2102 } else if (r != 0) {
2103 error("%.200s line %d: glob failed for %s.",
2104 filename, linenum, arg2);
2105 goto out;
2106 }
2107 free(arg2);
2108 oactive = *activep;
2109 for (i = 0; i < gl.gl_pathc; i++) {
2110 debug3("%.200s line %d: Including file %s "
2111 "depth %d%s", filename, linenum,
2112 gl.gl_pathv[i], depth,
2113 oactive ? "" : " (parse only)");
2114 r = read_config_file_depth(gl.gl_pathv[i],
2115 pw, host, original_host, remote_command,
2116 options, flags | SSHCONF_CHECKPERM |
2117 (oactive ? 0 : SSHCONF_NEVERMATCH),
2118 activep, want_final_pass, depth + 1);
2119 if (r != 1 && errno != ENOENT) {
2120 error("%.200s line %d: Can't open user "
2121 "config file %.100s: %.100s",
2122 filename, linenum, gl.gl_pathv[i],
2123 strerror(errno));
2124 globfree(&gl);
2125 goto out;
2126 }
2127 /*
2128 * don't let Match in includes clobber the
2129 * containing file's Match state.
2130 */
2131 *activep = oactive;
2132 if (r != 1)
2133 value = -1;
2134 }
2135 globfree(&gl);
2136 }
2137 if (value != 0)
2138 ret = value;
2139 break;
2140
2141 case oIPQoS:
2142 arg = argv_next(&ac, &av);
2143 if ((value = parse_ipqos(arg)) == -1) {
2144 error("%s line %d: Bad IPQoS value: %s",
2145 filename, linenum, arg);
2146 goto out;
2147 }
2148 if (value == INT_MIN) {
2149 debug("%s line %d: Deprecated IPQoS value \"%s\" "
2150 "ignored - using system default instead. Consider"
2151 " using DSCP values.", filename, linenum, arg);
2152 value = INT_MAX;
2153 }
2154 arg = argv_next(&ac, &av);
2155 if (arg == NULL)
2156 value2 = value;
2157 else if ((value2 = parse_ipqos(arg)) == -1) {
2158 error("%s line %d: Bad IPQoS value: %s",
2159 filename, linenum, arg);
2160 goto out;
2161 }
2162 if (value2 == INT_MIN) {
2163 debug("%s line %d: Deprecated IPQoS value \"%s\" "
2164 "ignored - using system default instead. Consider"
2165 " using DSCP values.", filename, linenum, arg);
2166 value2 = INT_MAX;
2167 }
2168 if (*activep && options->ip_qos_interactive == -1) {
2169 options->ip_qos_interactive = value;
2170 options->ip_qos_bulk = value2;
2171 }
2172 break;
2173
2174 case oRequestTTY:
2175 intptr = &options->request_tty;
2176 multistate_ptr = multistate_requesttty;
2177 goto parse_multistate;
2178
2179 case oSessionType:
2180 intptr = &options->session_type;
2181 multistate_ptr = multistate_sessiontype;
2182 goto parse_multistate;
2183
2184 case oStdinNull:
2185 intptr = &options->stdin_null;
2186 goto parse_flag;
2187
2188 case oForkAfterAuthentication:
2189 intptr = &options->fork_after_authentication;
2190 goto parse_flag;
2191
2192 case oIgnoreUnknown:
2193 charptr = &options->ignored_unknown;
2194 goto parse_string;
2195
2196 case oProxyUseFdpass:
2197 intptr = &options->proxy_use_fdpass;
2198 goto parse_flag;
2199
2200 case oCanonicalDomains:
2201 found = options->num_canonical_domains == 0;
2202 while ((arg = argv_next(&ac, &av)) != NULL) {
2203 /* Allow "none" only in first position */
2204 if (strcasecmp(arg, "none") == 0) {
2205 if (nstrs > 0 || ac > 0) {
2206 error("%s line %d: keyword %s \"none\" "
2207 "argument must appear alone.",
2208 filename, linenum, keyword);
2209 goto out;
2210 }
2211 }
2212 if (!valid_domain(arg, 1, &errstr)) {
2213 error("%s line %d: %s", filename, linenum,
2214 errstr);
2215 goto out;
2216 }
2217 opt_array_append(filename, linenum, keyword,
2218 &strs, &nstrs, arg);
2219 }
2220 if (nstrs == 0) {
2221 fatal("%s line %d: no %s specified",
2222 filename, linenum, keyword);
2223 }
2224 if (found && *activep) {
2225 options->canonical_domains = strs;
2226 options->num_canonical_domains = nstrs;
2227 strs = NULL; /* transferred */
2228 nstrs = 0;
2229 }
2230 break;
2231
2232 case oCanonicalizePermittedCNAMEs:
2233 found = options->num_permitted_cnames == 0;
2234 while ((arg = argv_next(&ac, &av)) != NULL) {
2235 /*
2236 * Either 'none' (only in first position), '*' for
2237 * everything or 'list:list'
2238 */
2239 if (strcasecmp(arg, "none") == 0) {
2240 if (ncnames > 0 || ac > 0) {
2241 error("%s line %d: keyword %s \"none\" "
2242 "argument must appear alone.",
2243 filename, linenum, keyword);
2244 goto out;
2245 }
2246 arg2 = "";
2247 } else if (strcmp(arg, "*") == 0) {
2248 arg2 = arg;
2249 } else {
2250 lowercase(arg);
2251 if ((arg2 = strchr(arg, ':')) == NULL ||
2252 arg2[1] == '\0') {
2253 error("%s line %d: "
2254 "Invalid permitted CNAME \"%s\"",
2255 filename, linenum, arg);
2256 goto out;
2257 }
2258 *arg2 = '\0';
2259 arg2++;
2260 }
2261 cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
2262 sizeof(*cnames));
2263 cnames[ncnames].source_list = xstrdup(arg);
2264 cnames[ncnames].target_list = xstrdup(arg2);
2265 ncnames++;
2266 }
2267 if (ncnames == 0) {
2268 fatal("%s line %d: no %s specified",
2269 filename, linenum, keyword);
2270 }
2271 if (found && *activep) {
2272 options->permitted_cnames = cnames;
2273 options->num_permitted_cnames = ncnames;
2274 cnames = NULL; /* transferred */
2275 ncnames = 0;
2276 }
2277 /* un-transferred cnames is cleaned up before exit */
2278 break;
2279
2280 case oCanonicalizeHostname:
2281 intptr = &options->canonicalize_hostname;
2282 multistate_ptr = multistate_canonicalizehostname;
2283 goto parse_multistate;
2284
2285 case oCanonicalizeMaxDots:
2286 intptr = &options->canonicalize_max_dots;
2287 goto parse_int;
2288
2289 case oCanonicalizeFallbackLocal:
2290 intptr = &options->canonicalize_fallback_local;
2291 goto parse_flag;
2292
2293 case oStreamLocalBindMask:
2294 arg = argv_next(&ac, &av);
2295 if (!arg || *arg == '\0') {
2296 error("%.200s line %d: Missing StreamLocalBindMask "
2297 "argument.", filename, linenum);
2298 goto out;
2299 }
2300 /* Parse mode in octal format */
2301 value = strtol(arg, &endofnumber, 8);
2302 if (arg == endofnumber || value < 0 || value > 0777) {
2303 error("%.200s line %d: Bad mask.", filename, linenum);
2304 goto out;
2305 }
2306 options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
2307 break;
2308
2309 case oStreamLocalBindUnlink:
2310 intptr = &options->fwd_opts.streamlocal_bind_unlink;
2311 goto parse_flag;
2312
2313 case oRevokedHostKeys:
2314 uintptr = &options->num_revoked_host_keys;
2315 cppptr = &options->revoked_host_keys;
2316 found = *uintptr == 0;
2317 while ((arg = argv_next(&ac, &av)) != NULL) {
2318 if (*arg == '\0') {
2319 error("%s line %d: keyword %s empty argument",
2320 filename, linenum, keyword);
2321 goto out;
2322 }
2323 /* Allow "none" only in first position */
2324 if (strcasecmp(arg, "none") == 0) {
2325 if (nstrs > 0 || ac > 0) {
2326 error("%s line %d: keyword %s \"none\" "
2327 "argument must appear alone.",
2328 filename, linenum, keyword);
2329 goto out;
2330 }
2331 }
2332 opt_array_append(filename, linenum, keyword,
2333 &strs, &nstrs, arg);
2334 }
2335 if (nstrs == 0) {
2336 fatal("%s line %d: no %s specified",
2337 filename, linenum, keyword);
2338 }
2339 if (found && *activep) {
2340 *cppptr = strs;
2341 *uintptr = nstrs;
2342 strs = NULL; /* transferred */
2343 nstrs = 0;
2344 }
2345 break;
2346
2347 case oFingerprintHash:
2348 intptr = &options->fingerprint_hash;
2349 arg = argv_next(&ac, &av);
2350 if (!arg || *arg == '\0') {
2351 error("%.200s line %d: Missing argument.",
2352 filename, linenum);
2353 goto out;
2354 }
2355 if ((value = ssh_digest_alg_by_name(arg)) == -1) {
2356 error("%.200s line %d: Invalid hash algorithm \"%s\".",
2357 filename, linenum, arg);
2358 goto out;
2359 }
2360 if (*activep && *intptr == -1)
2361 *intptr = value;
2362 break;
2363
2364 case oUpdateHostkeys:
2365 intptr = &options->update_hostkeys;
2366 multistate_ptr = multistate_yesnoask;
2367 goto parse_multistate;
2368
2369 case oHostbasedAcceptedAlgorithms:
2370 charptr = &options->hostbased_accepted_algos;
2371 ca_only = 0;
2372 goto parse_pubkey_algos;
2373
2374 case oPubkeyAcceptedAlgorithms:
2375 charptr = &options->pubkey_accepted_algos;
2376 ca_only = 0;
2377 goto parse_pubkey_algos;
2378
2379 case oAddKeysToAgent:
2380 arg = argv_next(&ac, &av);
2381 arg2 = argv_next(&ac, &av);
2382 value = parse_multistate_value(arg, filename, linenum,
2383 multistate_yesnoaskconfirm);
2384 value2 = 0; /* unlimited lifespan by default */
2385 if (value == 3 && arg2 != NULL) {
2386 /* allow "AddKeysToAgent confirm 5m" */
2387 if ((value2 = convtime(arg2)) == -1) {
2388 error("%s line %d: invalid time value.",
2389 filename, linenum);
2390 goto out;
2391 }
2392 } else if (value == -1 && arg2 == NULL) {
2393 if ((value2 = convtime(arg)) == -1) {
2394 error("%s line %d: unsupported option",
2395 filename, linenum);
2396 goto out;
2397 }
2398 value = 1; /* yes */
2399 } else if (value == -1 || arg2 != NULL) {
2400 error("%s line %d: unsupported option",
2401 filename, linenum);
2402 goto out;
2403 }
2404 if (*activep && options->add_keys_to_agent == -1) {
2405 options->add_keys_to_agent = value;
2406 options->add_keys_to_agent_lifespan = value2;
2407 }
2408 break;
2409
2410 case oIdentityAgent:
2411 charptr = &options->identity_agent;
2412 arg = argv_next(&ac, &av);
2413 if (!arg || *arg == '\0') {
2414 error("%.200s line %d: Missing argument.",
2415 filename, linenum);
2416 goto out;
2417 }
2418 parse_agent_path:
2419 /* Extra validation if the string represents an env var. */
2420 if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
2421 error("%.200s line %d: Invalid environment expansion "
2422 "%s.", filename, linenum, arg);
2423 goto out;
2424 }
2425 free(arg2);
2426 /* check for legacy environment format */
2427 if (arg[0] == '$' && arg[1] != '{' &&
2428 !valid_env_name(arg + 1)) {
2429 error("%.200s line %d: Invalid environment name %s.",
2430 filename, linenum, arg);
2431 goto out;
2432 }
2433 if (*activep && *charptr == NULL)
2434 *charptr = xstrdup(arg);
2435 break;
2436
2437 case oEnableEscapeCommandline:
2438 intptr = &options->enable_escape_commandline;
2439 goto parse_flag;
2440
2441 case oRequiredRSASize:
2442 intptr = &options->required_rsa_size;
2443 goto parse_int;
2444
2445 case oWarnWeakCrypto:
2446 intptr = &options->warn_weak_crypto;
2447 multistate_ptr = multistate_warnweakcrypto;
2448 goto parse_multistate;
2449
2450 case oObscureKeystrokeTiming:
2451 value = -1;
2452 while ((arg = argv_next(&ac, &av)) != NULL) {
2453 if (value != -1) {
2454 error("%s line %d: invalid arguments",
2455 filename, linenum);
2456 goto out;
2457 }
2458 if (strcmp(arg, "yes") == 0 ||
2459 strcmp(arg, "true") == 0)
2460 value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2461 else if (strcmp(arg, "no") == 0 ||
2462 strcmp(arg, "false") == 0)
2463 value = 0;
2464 else if (strncmp(arg, "interval:", 9) == 0) {
2465 if ((errstr = atoi_err(arg + 9,
2466 &value)) != NULL) {
2467 error("%s line %d: integer value %s.",
2468 filename, linenum, errstr);
2469 goto out;
2470 }
2471 if (value <= 0 || value > 1000) {
2472 error("%s line %d: value out of range.",
2473 filename, linenum);
2474 goto out;
2475 }
2476 } else {
2477 error("%s line %d: unsupported argument \"%s\"",
2478 filename, linenum, arg);
2479 goto out;
2480 }
2481 }
2482 if (value == -1) {
2483 error("%s line %d: missing argument",
2484 filename, linenum);
2485 goto out;
2486 }
2487 intptr = &options->obscure_keystroke_timing_interval;
2488 if (*activep && *intptr == -1)
2489 *intptr = value;
2490 break;
2491
2492 case oChannelTimeout:
2493 found = options->num_channel_timeouts == 0;
2494 while ((arg = argv_next(&ac, &av)) != NULL) {
2495 /* Allow "none" only in first position */
2496 if (strcasecmp(arg, "none") == 0) {
2497 if (nstrs > 0 || ac > 0) {
2498 error("%s line %d: keyword %s \"none\" "
2499 "argument must appear alone.",
2500 filename, linenum, keyword);
2501 goto out;
2502 }
2503 } else if (parse_pattern_interval(arg,
2504 NULL, NULL) != 0) {
2505 fatal("%s line %d: invalid channel timeout %s",
2506 filename, linenum, arg);
2507 }
2508 opt_array_append(filename, linenum, keyword,
2509 &strs, &nstrs, arg);
2510 }
2511 if (nstrs == 0) {
2512 fatal("%s line %d: no %s specified",
2513 filename, linenum, keyword);
2514 }
2515 if (found && *activep) {
2516 options->channel_timeouts = strs;
2517 options->num_channel_timeouts = nstrs;
2518 strs = NULL; /* transferred */
2519 nstrs = 0;
2520 }
2521 break;
2522
2523 case oVersionAddendum:
2524 if (str == NULL || *str == '\0')
2525 fatal("%s line %d: %s missing argument.",
2526 filename, linenum, keyword);
2527 len = strspn(str, WHITESPACE);
2528 if (strchr(str + len, '\r') != NULL) {
2529 fatal("%.200s line %d: Invalid %s argument",
2530 filename, linenum, keyword);
2531 }
2532 if ((arg = strchr(line, '#')) != NULL) {
2533 *arg = '\0';
2534 rtrim(line);
2535 }
2536 if (*activep && options->version_addendum == NULL) {
2537 if (strcasecmp(str + len, "none") == 0)
2538 options->version_addendum = xstrdup("");
2539 else
2540 options->version_addendum = xstrdup(str + len);
2541 }
2542 argv_consume(&ac);
2543 break;
2544
2545 case oRefuseConnection:
2546 arg = argv_next(&ac, &av);
2547 if (!arg || *arg == '\0') {
2548 error("%.200s line %d: Missing argument.",
2549 filename, linenum);
2550 goto out;
2551 }
2552 if (*activep) {
2553 fatal("%.200s line %d: RefuseConnection: %s",
2554 filename, linenum, arg);
2555 }
2556 break;
2557
2558 case oDeprecated:
2559 debug("%s line %d: Deprecated option \"%s\"",
2560 filename, linenum, keyword);
2561 argv_consume(&ac);
2562 break;
2563
2564 case oUnsupported:
2565 error("%s line %d: Unsupported option \"%s\"",
2566 filename, linenum, keyword);
2567 argv_consume(&ac);
2568 break;
2569
2570 default:
2571 error("%s line %d: Unimplemented opcode %d",
2572 filename, linenum, opcode);
2573 goto out;
2574 }
2575
2576 /* Check that there is no garbage at end of line. */
2577 if (ac > 0) {
2578 error("%.200s line %d: keyword %s extra arguments "
2579 "at end of line", filename, linenum, keyword);
2580 goto out;
2581 }
2582
2583 /* success */
2584 ret = 0;
2585 out:
2586 free_canon_cnames(cnames, ncnames);
2587 opt_array_free2(strs, NULL, nstrs);
2588 argv_free(oav, oac);
2589 return ret;
2590}
2591
2592/*
2593 * Reads the config file and modifies the options accordingly. Options
2594 * should already be initialized before this call. This never returns if
2595 * there is an error. If the file does not exist, this returns 0.
2596 */
2597int
2598read_config_file(const char *filename, struct passwd *pw, const char *host,
2599 const char *original_host, const char *remote_command, Options *options, int flags,
2600 int *want_final_pass)
2601{
2602 int active = 1;
2603
2604 return read_config_file_depth(filename, pw, host, original_host,
2605 remote_command, options, flags, &active, want_final_pass, 0);
2606}
2607
2608#define READCONF_MAX_DEPTH 16
2609static int
2610read_config_file_depth(const char *filename, struct passwd *pw,
2611 const char *host, const char *original_host, const char *remote_command,
2612 Options *options, int flags, int *activep, int *want_final_pass, int depth)
2613{
2614 FILE *f;
2615 char *line = NULL;
2616 size_t linesize = 0;
2617 int linenum;
2618 int bad_options = 0;
2619
2620 if (depth < 0 || depth > READCONF_MAX_DEPTH)
2621 fatal("Too many recursive configuration includes");
2622
2623 if ((f = fopen(filename, "r")) == NULL)
2624 return 0;
2625
2626 if (flags & SSHCONF_CHECKPERM) {
2627 struct stat sb;
2628
2629 if (fstat(fileno(f), &sb) == -1)
2630 fatal("fstat %s: %s", filename, strerror(errno));
2631 if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
2632 (sb.st_mode & 022) != 0))
2633 fatal("Bad owner or permissions on %s", filename);
2634 }
2635
2636 debug("Reading configuration data %.200s", filename);
2637
2638 /*
2639 * Mark that we are now processing the options. This flag is turned
2640 * on/off by Host specifications.
2641 */
2642 linenum = 0;
2643 while (getline(&line, &linesize, f) != -1) {
2644 /* Update line number counter. */
2645 linenum++;
2646 /*
2647 * Trim out comments and strip whitespace.
2648 * NB - preserve newlines, they are needed to reproduce
2649 * line numbers later for error messages.
2650 */
2651 if (process_config_line_depth(options, pw, host, original_host,
2652 remote_command, line, filename, linenum, activep, flags,
2653 want_final_pass, depth) != 0)
2654 bad_options++;
2655 }
2656 free(line);
2657 fclose(f);
2658 if (bad_options > 0)
2659 fatal("%s: terminating, %d bad configuration options",
2660 filename, bad_options);
2661 return 1;
2662}
2663
2664/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
2665int
2666option_clear_or_none(const char *o)
2667{
2668 return o == NULL || strcasecmp(o, "none") == 0;
2669}
2670
2671/*
2672 * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
2673 * Allowed to be called on non-final configuration.
2674 */
2675int
2676config_has_permitted_cnames(Options *options)
2677{
2678 if (options->num_permitted_cnames == 1 &&
2679 strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 &&
2680 strcmp(options->permitted_cnames[0].target_list, "") == 0)
2681 return 0;
2682 return options->num_permitted_cnames > 0;
2683}
2684
2685/*
2686 * Initializes options to special values that indicate that they have not yet
2687 * been set. Read_config_file will only set options with this value. Options
2688 * are processed in the following order: command line, user config file,
2689 * system config file. Last, fill_default_options is called.
2690 */
2691
2692void
2693initialize_options(Options * options)
2694{
2695 memset(options, 'X', sizeof(*options));
2696 options->host_arg = NULL;
2697 options->forward_agent = -1;
2698 options->forward_agent_sock_path = NULL;
2699 options->forward_x11 = -1;
2700 options->forward_x11_trusted = -1;
2701 options->forward_x11_timeout = -1;
2702 options->stdio_forward_host = NULL;
2703 options->stdio_forward_port = 0;
2704 options->clear_forwardings = -1;
2705 options->exit_on_forward_failure = -1;
2706 options->xauth_location = NULL;
2707 options->fwd_opts.gateway_ports = -1;
2708 options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
2709 options->fwd_opts.streamlocal_bind_unlink = -1;
2710 options->pubkey_authentication = -1;
2711 options->gss_authentication = -1;
2712 options->gss_deleg_creds = -1;
2713 options->password_authentication = -1;
2714 options->kbd_interactive_authentication = -1;
2715 options->kbd_interactive_devices = NULL;
2716 options->hostbased_authentication = -1;
2717 options->batch_mode = -1;
2718 options->check_host_ip = -1;
2719 options->strict_host_key_checking = -1;
2720 options->compression = -1;
2721 options->tcp_keep_alive = -1;
2722 options->port = -1;
2723 options->address_family = -1;
2724 options->connection_attempts = -1;
2725 options->connection_timeout = -1;
2726 options->number_of_password_prompts = -1;
2727 options->ciphers = NULL;
2728 options->macs = NULL;
2729 options->kex_algorithms = NULL;
2730 options->hostkeyalgorithms = NULL;
2731 options->ca_sign_algorithms = NULL;
2732 options->num_identity_files = 0;
2733 memset(options->identity_keys, 0, sizeof(options->identity_keys));
2734 options->num_certificate_files = 0;
2735 memset(options->certificates, 0, sizeof(options->certificates));
2736 options->hostname = NULL;
2737 options->host_key_alias = NULL;
2738 options->proxy_command = NULL;
2739 options->jump_user = NULL;
2740 options->jump_host = NULL;
2741 options->jump_port = -1;
2742 options->jump_extra = NULL;
2743 options->user = NULL;
2744 options->escape_char = -1;
2745 options->num_system_hostfiles = 0;
2746 options->num_user_hostfiles = 0;
2747 options->local_forwards = NULL;
2748 options->num_local_forwards = 0;
2749 options->remote_forwards = NULL;
2750 options->num_remote_forwards = 0;
2751 options->permitted_remote_opens = NULL;
2752 options->num_permitted_remote_opens = 0;
2753 options->log_facility = SYSLOG_FACILITY_NOT_SET;
2754 options->log_level = SYSLOG_LEVEL_NOT_SET;
2755 options->num_log_verbose = 0;
2756 options->log_verbose = NULL;
2757 options->preferred_authentications = NULL;
2758 options->bind_address = NULL;
2759 options->bind_interface = NULL;
2760 options->pkcs11_provider = NULL;
2761 options->sk_provider = NULL;
2762 options->enable_ssh_keysign = - 1;
2763 options->no_host_authentication_for_localhost = - 1;
2764 options->identities_only = - 1;
2765 options->rekey_limit = - 1;
2766 options->rekey_interval = -1;
2767 options->verify_host_key_dns = -1;
2768 options->server_alive_interval = -1;
2769 options->server_alive_count_max = -1;
2770 options->send_env = NULL;
2771 options->num_send_env = 0;
2772 options->setenv = NULL;
2773 options->num_setenv = 0;
2774 options->control_path = NULL;
2775 options->control_master = -1;
2776 options->control_persist = -1;
2777 options->control_persist_timeout = 0;
2778 options->hash_known_hosts = -1;
2779 options->tun_open = -1;
2780 options->tun_local = -1;
2781 options->tun_remote = -1;
2782 options->local_command = NULL;
2783 options->permit_local_command = -1;
2784 options->remote_command = NULL;
2785 options->add_keys_to_agent = -1;
2786 options->add_keys_to_agent_lifespan = -1;
2787 options->identity_agent = NULL;
2788 options->visual_host_key = -1;
2789 options->ip_qos_interactive = -1;
2790 options->ip_qos_bulk = -1;
2791 options->request_tty = -1;
2792 options->session_type = -1;
2793 options->stdin_null = -1;
2794 options->fork_after_authentication = -1;
2795 options->proxy_use_fdpass = -1;
2796 options->ignored_unknown = NULL;
2797 options->num_canonical_domains = 0;
2798 options->num_permitted_cnames = 0;
2799 options->canonicalize_max_dots = -1;
2800 options->canonicalize_fallback_local = -1;
2801 options->canonicalize_hostname = -1;
2802 options->revoked_host_keys = NULL;
2803 options->num_revoked_host_keys = 0;
2804 options->fingerprint_hash = -1;
2805 options->update_hostkeys = -1;
2806 options->hostbased_accepted_algos = NULL;
2807 options->pubkey_accepted_algos = NULL;
2808 options->known_hosts_command = NULL;
2809 options->required_rsa_size = -1;
2810 options->warn_weak_crypto = -1;
2811 options->enable_escape_commandline = -1;
2812 options->obscure_keystroke_timing_interval = -1;
2813 options->tag = NULL;
2814 options->channel_timeouts = NULL;
2815 options->num_channel_timeouts = 0;
2816 options->version_addendum = NULL;
2817}
2818
2819/*
2820 * A petite version of fill_default_options() that just fills the options
2821 * needed for hostname canonicalization to proceed.
2822 */
2823void
2824fill_default_options_for_canonicalization(Options *options)
2825{
2826 if (options->canonicalize_max_dots == -1)
2827 options->canonicalize_max_dots = 1;
2828 if (options->canonicalize_fallback_local == -1)
2829 options->canonicalize_fallback_local = 1;
2830 if (options->canonicalize_hostname == -1)
2831 options->canonicalize_hostname = SSH_CANONICALISE_NO;
2832}
2833
2834/*
2835 * Called after processing other sources of option data, this fills those
2836 * options for which no value has been specified with their default values.
2837 */
2838int
2839fill_default_options(Options * options)
2840{
2841 char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig;
2842 char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig;
2843 int ret = 0, r;
2844
2845 if (options->forward_agent == -1)
2846 options->forward_agent = 0;
2847 if (options->forward_x11 == -1)
2848 options->forward_x11 = 0;
2849 if (options->forward_x11_trusted == -1)
2850 options->forward_x11_trusted = 0;
2851 if (options->forward_x11_timeout == -1)
2852 options->forward_x11_timeout = 1200;
2853 /*
2854 * stdio forwarding (-W) changes the default for these but we defer
2855 * setting the values so they can be overridden.
2856 */
2857 if (options->exit_on_forward_failure == -1)
2858 options->exit_on_forward_failure =
2859 options->stdio_forward_host != NULL ? 1 : 0;
2860 if (options->clear_forwardings == -1)
2861 options->clear_forwardings =
2862 options->stdio_forward_host != NULL ? 1 : 0;
2863 if (options->clear_forwardings == 1)
2864 clear_forwardings(options);
2865
2866 if (options->xauth_location == NULL)
2867 options->xauth_location = xstrdup(_PATH_XAUTH);
2868 if (options->fwd_opts.gateway_ports == -1)
2869 options->fwd_opts.gateway_ports = 0;
2870 if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
2871 options->fwd_opts.streamlocal_bind_mask = 0177;
2872 if (options->fwd_opts.streamlocal_bind_unlink == -1)
2873 options->fwd_opts.streamlocal_bind_unlink = 0;
2874 if (options->pubkey_authentication == -1)
2875 options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
2876 if (options->gss_authentication == -1)
2877 options->gss_authentication = 0;
2878 if (options->gss_deleg_creds == -1)
2879 options->gss_deleg_creds = 0;
2880 if (options->password_authentication == -1)
2881 options->password_authentication = 1;
2882 if (options->kbd_interactive_authentication == -1)
2883 options->kbd_interactive_authentication = 1;
2884 if (options->hostbased_authentication == -1)
2885 options->hostbased_authentication = 0;
2886 if (options->batch_mode == -1)
2887 options->batch_mode = 0;
2888 if (options->check_host_ip == -1)
2889 options->check_host_ip = 0;
2890 if (options->strict_host_key_checking == -1)
2891 options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK;
2892 if (options->compression == -1)
2893 options->compression = 0;
2894 if (options->tcp_keep_alive == -1)
2895 options->tcp_keep_alive = 1;
2896 if (options->port == -1)
2897 options->port = 0; /* Filled in ssh_connect. */
2898 if (options->address_family == -1)
2899 options->address_family = AF_UNSPEC;
2900 if (options->connection_attempts == -1)
2901 options->connection_attempts = 1;
2902 if (options->number_of_password_prompts == -1)
2903 options->number_of_password_prompts = 3;
2904 /* options->hostkeyalgorithms, default set in myproposals.h */
2905 if (options->add_keys_to_agent == -1) {
2906 options->add_keys_to_agent = 0;
2907 options->add_keys_to_agent_lifespan = 0;
2908 }
2909 if (options->num_identity_files == 0) {
2910 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0);
2911 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
2912 add_identity_file(options, "~/",
2913 _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
2914 add_identity_file(options, "~/",
2915 _PATH_SSH_CLIENT_ID_ED25519, 0);
2916 add_identity_file(options, "~/",
2917 _PATH_SSH_CLIENT_ID_ED25519_SK, 0);
2918 }
2919 if (options->escape_char == -1)
2920 options->escape_char = '~';
2921 if (options->num_system_hostfiles == 0) {
2922 options->system_hostfiles[options->num_system_hostfiles++] =
2923 xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
2924 options->system_hostfiles[options->num_system_hostfiles++] =
2925 xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
2926 }
2927 if (options->update_hostkeys == -1) {
2928 if (options->verify_host_key_dns <= 0 &&
2929 (options->num_user_hostfiles == 0 ||
2930 (options->num_user_hostfiles == 1 && strcmp(options->
2931 user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0)))
2932 options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES;
2933 else
2934 options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO;
2935 }
2936 if (options->num_user_hostfiles == 0) {
2937 options->user_hostfiles[options->num_user_hostfiles++] =
2938 xstrdup(_PATH_SSH_USER_HOSTFILE);
2939 options->user_hostfiles[options->num_user_hostfiles++] =
2940 xstrdup(_PATH_SSH_USER_HOSTFILE2);
2941 }
2942 if (options->log_level == SYSLOG_LEVEL_NOT_SET)
2943 options->log_level = SYSLOG_LEVEL_INFO;
2944 if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
2945 options->log_facility = SYSLOG_FACILITY_USER;
2946 if (options->no_host_authentication_for_localhost == - 1)
2947 options->no_host_authentication_for_localhost = 0;
2948 if (options->identities_only == -1)
2949 options->identities_only = 0;
2950 if (options->enable_ssh_keysign == -1)
2951 options->enable_ssh_keysign = 0;
2952 if (options->rekey_limit == -1)
2953 options->rekey_limit = 0;
2954 if (options->rekey_interval == -1)
2955 options->rekey_interval = 0;
2956 if (options->verify_host_key_dns == -1)
2957 options->verify_host_key_dns = 0;
2958 if (options->server_alive_interval == -1)
2959 options->server_alive_interval = 0;
2960 if (options->server_alive_count_max == -1)
2961 options->server_alive_count_max = 3;
2962 if (options->control_master == -1)
2963 options->control_master = 0;
2964 if (options->control_persist == -1) {
2965 options->control_persist = 0;
2966 options->control_persist_timeout = 0;
2967 }
2968 if (options->hash_known_hosts == -1)
2969 options->hash_known_hosts = 0;
2970 if (options->tun_open == -1)
2971 options->tun_open = SSH_TUNMODE_NO;
2972 if (options->tun_local == -1)
2973 options->tun_local = SSH_TUNID_ANY;
2974 if (options->tun_remote == -1)
2975 options->tun_remote = SSH_TUNID_ANY;
2976 if (options->permit_local_command == -1)
2977 options->permit_local_command = 0;
2978 if (options->visual_host_key == -1)
2979 options->visual_host_key = 0;
2980 if (options->ip_qos_interactive == -1)
2981 options->ip_qos_interactive = IPTOS_DSCP_EF;
2982 if (options->ip_qos_bulk == -1)
2983 options->ip_qos_bulk = IPTOS_DSCP_CS0;
2984 if (options->request_tty == -1)
2985 options->request_tty = REQUEST_TTY_AUTO;
2986 if (options->session_type == -1)
2987 options->session_type = SESSION_TYPE_DEFAULT;
2988 if (options->stdin_null == -1)
2989 options->stdin_null = 0;
2990 if (options->fork_after_authentication == -1)
2991 options->fork_after_authentication = 0;
2992 if (options->proxy_use_fdpass == -1)
2993 options->proxy_use_fdpass = 0;
2994 if (options->canonicalize_max_dots == -1)
2995 options->canonicalize_max_dots = 1;
2996 if (options->canonicalize_fallback_local == -1)
2997 options->canonicalize_fallback_local = 1;
2998 if (options->canonicalize_hostname == -1)
2999 options->canonicalize_hostname = SSH_CANONICALISE_NO;
3000 if (options->fingerprint_hash == -1)
3001 options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
3002 if (options->sk_provider == NULL)
3003 options->sk_provider = xstrdup("internal");
3004 if (options->required_rsa_size == -1)
3005 options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
3006 if (options->warn_weak_crypto == -1)
3007 options->warn_weak_crypto = 1;
3008 if (options->enable_escape_commandline == -1)
3009 options->enable_escape_commandline = 0;
3010 if (options->obscure_keystroke_timing_interval == -1) {
3011 options->obscure_keystroke_timing_interval =
3012 SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
3013 }
3014
3015 /* Expand KEX name lists */
3016 all_cipher = cipher_alg_list(',', 0);
3017 all_mac = mac_alg_list(',');
3018 all_kex = kex_alg_list(',');
3019 all_key = sshkey_alg_list(0, 0, 1, ',');
3020 all_sig = sshkey_alg_list(0, 1, 1, ',');
3021 /* remove unsupported algos from default lists */
3022 def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher);
3023 def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac);
3024 def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex);
3025 def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
3026 def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig);
3027#define ASSEMBLE(what, defaults, all) \
3028 do { \
3029 if ((r = kex_assemble_names(&options->what, \
3030 defaults, all)) != 0) { \
3031 error_fr(r, "%s", #what); \
3032 goto fail; \
3033 } \
3034 } while (0)
3035 options->kex_algorithms_set = options->kex_algorithms != NULL;
3036 ASSEMBLE(ciphers, def_cipher, all_cipher);
3037 ASSEMBLE(macs, def_mac, all_mac);
3038 ASSEMBLE(kex_algorithms, def_kex, all_kex);
3039 ASSEMBLE(hostbased_accepted_algos, def_key, all_key);
3040 ASSEMBLE(pubkey_accepted_algos, def_key, all_key);
3041 ASSEMBLE(ca_sign_algorithms, def_sig, all_sig);
3042#undef ASSEMBLE
3043
3044#define CLEAR_ON_NONE(v) \
3045 do { \
3046 if (option_clear_or_none(v)) { \
3047 free(v); \
3048 v = NULL; \
3049 } \
3050 } while(0)
3051#define CLEAR_ON_NONE_ARRAY(v, nv, none) \
3052 do { \
3053 if (options->nv == 1 && \
3054 strcasecmp(options->v[0], none) == 0) { \
3055 free(options->v[0]); \
3056 free(options->v); \
3057 options->v = NULL; \
3058 options->nv = 0; \
3059 } \
3060 } while (0)
3061 CLEAR_ON_NONE(options->local_command);
3062 CLEAR_ON_NONE(options->remote_command);
3063 CLEAR_ON_NONE(options->proxy_command);
3064 CLEAR_ON_NONE(options->control_path);
3065 CLEAR_ON_NONE(options->pkcs11_provider);
3066 CLEAR_ON_NONE(options->sk_provider);
3067 CLEAR_ON_NONE(options->known_hosts_command);
3068 CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
3069 CLEAR_ON_NONE_ARRAY(revoked_host_keys, num_revoked_host_keys, "none");
3070#undef CLEAR_ON_NONE
3071#undef CLEAR_ON_NONE_ARRAY
3072 if (options->jump_host != NULL &&
3073 strcmp(options->jump_host, "none") == 0 &&
3074 options->jump_port == 0 && options->jump_user == NULL) {
3075 free(options->jump_host);
3076 options->jump_host = NULL;
3077 }
3078 if (options->num_permitted_cnames == 1 &&
3079 !config_has_permitted_cnames(options)) {
3080 /* clean up CanonicalizePermittedCNAMEs=none */
3081 free(options->permitted_cnames[0].source_list);
3082 free(options->permitted_cnames[0].target_list);
3083 memset(options->permitted_cnames, '\0',
3084 sizeof(*options->permitted_cnames));
3085 options->num_permitted_cnames = 0;
3086 }
3087 /* options->identity_agent distinguishes NULL from 'none' */
3088 /* options->user will be set in the main program if appropriate */
3089 /* options->hostname will be set in the main program if appropriate */
3090 /* options->host_key_alias should not be set by default */
3091 /* options->preferred_authentications will be set in ssh */
3092
3093 /* success */
3094 ret = 0;
3095 fail:
3096 free(all_cipher);
3097 free(all_mac);
3098 free(all_kex);
3099 free(all_key);
3100 free(all_sig);
3101 free(def_cipher);
3102 free(def_mac);
3103 free(def_kex);
3104 free(def_key);
3105 free(def_sig);
3106 return ret;
3107}
3108
3109void
3110free_options(Options *o)
3111{
3112 int i;
3113
3114 if (o == NULL)
3115 return;
3116
3117#define FREE_ARRAY(type, n, a) \
3118 do { \
3119 type _i; \
3120 for (_i = 0; _i < (n); _i++) \
3121 free((a)[_i]); \
3122 } while (0)
3123
3124 free(o->forward_agent_sock_path);
3125 free(o->xauth_location);
3126 FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
3127 free(o->log_verbose);
3128 free(o->ciphers);
3129 free(o->macs);
3130 free(o->hostkeyalgorithms);
3131 free(o->kex_algorithms);
3132 free(o->ca_sign_algorithms);
3133 free(o->hostname);
3134 free(o->host_key_alias);
3135 free(o->proxy_command);
3136 free(o->user);
3137 FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles);
3138 FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles);
3139 free(o->preferred_authentications);
3140 free(o->bind_address);
3141 free(o->bind_interface);
3142 free(o->pkcs11_provider);
3143 free(o->sk_provider);
3144 for (i = 0; i < o->num_identity_files; i++) {
3145 free(o->identity_files[i]);
3146 sshkey_free(o->identity_keys[i]);
3147 }
3148 for (i = 0; i < o->num_certificate_files; i++) {
3149 free(o->certificate_files[i]);
3150 sshkey_free(o->certificates[i]);
3151 }
3152 free(o->identity_agent);
3153 for (i = 0; i < o->num_local_forwards; i++) {
3154 free(o->local_forwards[i].listen_host);
3155 free(o->local_forwards[i].listen_path);
3156 free(o->local_forwards[i].connect_host);
3157 free(o->local_forwards[i].connect_path);
3158 }
3159 free(o->local_forwards);
3160 for (i = 0; i < o->num_remote_forwards; i++) {
3161 free(o->remote_forwards[i].listen_host);
3162 free(o->remote_forwards[i].listen_path);
3163 free(o->remote_forwards[i].connect_host);
3164 free(o->remote_forwards[i].connect_path);
3165 }
3166 free(o->remote_forwards);
3167 free(o->stdio_forward_host);
3168 FREE_ARRAY(u_int, o->num_send_env, o->send_env);
3169 free(o->send_env);
3170 FREE_ARRAY(u_int, o->num_setenv, o->setenv);
3171 free(o->setenv);
3172 free(o->control_path);
3173 free(o->local_command);
3174 free(o->remote_command);
3175 FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains);
3176 for (i = 0; i < o->num_permitted_cnames; i++) {
3177 free(o->permitted_cnames[i].source_list);
3178 free(o->permitted_cnames[i].target_list);
3179 }
3180 FREE_ARRAY(u_int, o->num_revoked_host_keys, o->revoked_host_keys);
3181 free(o->revoked_host_keys);
3182 free(o->hostbased_accepted_algos);
3183 free(o->pubkey_accepted_algos);
3184 free(o->jump_user);
3185 free(o->jump_host);
3186 free(o->jump_extra);
3187 free(o->ignored_unknown);
3188 explicit_bzero(o, sizeof(*o));
3189#undef FREE_ARRAY
3190}
3191
3192struct fwdarg {
3193 char *arg;
3194 int ispath;
3195};
3196
3197/*
3198 * parse_fwd_field
3199 * parses the next field in a port forwarding specification.
3200 * sets fwd to the parsed field and advances p past the colon
3201 * or sets it to NULL at end of string.
3202 * returns 0 on success, else non-zero.
3203 */
3204static int
3205parse_fwd_field(char **p, struct fwdarg *fwd)
3206{
3207 char *ep, *cp = *p;
3208 int ispath = 0;
3209
3210 if (*cp == '\0') {
3211 *p = NULL;
3212 return -1; /* end of string */
3213 }
3214
3215 /*
3216 * A field escaped with square brackets is used literally.
3217 * XXX - allow ']' to be escaped via backslash?
3218 */
3219 if (*cp == '[') {
3220 /* find matching ']' */
3221 for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) {
3222 if (*ep == '/')
3223 ispath = 1;
3224 }
3225 /* no matching ']' or not at end of field. */
3226 if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0'))
3227 return -1;
3228 /* NUL terminate the field and advance p past the colon */
3229 *ep++ = '\0';
3230 if (*ep != '\0')
3231 *ep++ = '\0';
3232 fwd->arg = cp + 1;
3233 fwd->ispath = ispath;
3234 *p = ep;
3235 return 0;
3236 }
3237
3238 for (cp = *p; *cp != '\0'; cp++) {
3239 switch (*cp) {
3240 case '\\':
3241 memmove(cp, cp + 1, strlen(cp + 1) + 1);
3242 if (*cp == '\0')
3243 return -1;
3244 break;
3245 case '/':
3246 ispath = 1;
3247 break;
3248 case ':':
3249 *cp++ = '\0';
3250 goto done;
3251 }
3252 }
3253done:
3254 fwd->arg = *p;
3255 fwd->ispath = ispath;
3256 *p = cp;
3257 return 0;
3258}
3259
3260/*
3261 * parse_forward
3262 * parses a string containing a port forwarding specification of the form:
3263 * dynamicfwd == 0
3264 * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath
3265 * listenpath:connectpath
3266 * dynamicfwd == 1
3267 * [listenhost:]listenport
3268 * returns number of arguments parsed or zero on error
3269 */
3270int
3271parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
3272{
3273 struct fwdarg fwdargs[4];
3274 char *p, *cp;
3275 int i, err;
3276
3277 memset(fwd, 0, sizeof(*fwd));
3278 memset(fwdargs, 0, sizeof(fwdargs));
3279
3280 /*
3281 * We expand environment variables before checking if we think they're
3282 * paths so that if ${VAR} expands to a fully qualified path it is
3283 * treated as a path.
3284 */
3285 cp = p = dollar_expand(&err, fwdspec);
3286 if (p == NULL || err)
3287 return 0;
3288
3289 /* skip leading spaces */
3290 while (isspace((u_char)*cp))
3291 cp++;
3292
3293 for (i = 0; i < 4; ++i) {
3294 if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
3295 break;
3296 }
3297
3298 /* Check for trailing garbage */
3299 if (cp != NULL && *cp != '\0') {
3300 i = 0; /* failure */
3301 }
3302
3303 switch (i) {
3304 case 1:
3305 if (fwdargs[0].ispath) {
3306 fwd->listen_path = xstrdup(fwdargs[0].arg);
3307 fwd->listen_port = PORT_STREAMLOCAL;
3308 } else {
3309 fwd->listen_host = NULL;
3310 fwd->listen_port = a2port(fwdargs[0].arg);
3311 }
3312 fwd->connect_host = xstrdup("socks");
3313 break;
3314
3315 case 2:
3316 if (fwdargs[0].ispath && fwdargs[1].ispath) {
3317 fwd->listen_path = xstrdup(fwdargs[0].arg);
3318 fwd->listen_port = PORT_STREAMLOCAL;
3319 fwd->connect_path = xstrdup(fwdargs[1].arg);
3320 fwd->connect_port = PORT_STREAMLOCAL;
3321 } else if (fwdargs[1].ispath) {
3322 fwd->listen_host = NULL;
3323 fwd->listen_port = a2port(fwdargs[0].arg);
3324 fwd->connect_path = xstrdup(fwdargs[1].arg);
3325 fwd->connect_port = PORT_STREAMLOCAL;
3326 } else {
3327 fwd->listen_host = xstrdup(fwdargs[0].arg);
3328 fwd->listen_port = a2port(fwdargs[1].arg);
3329 fwd->connect_host = xstrdup("socks");
3330 }
3331 break;
3332
3333 case 3:
3334 if (fwdargs[0].ispath) {
3335 fwd->listen_path = xstrdup(fwdargs[0].arg);
3336 fwd->listen_port = PORT_STREAMLOCAL;
3337 fwd->connect_host = xstrdup(fwdargs[1].arg);
3338 fwd->connect_port = a2port(fwdargs[2].arg);
3339 } else if (fwdargs[2].ispath) {
3340 fwd->listen_host = xstrdup(fwdargs[0].arg);
3341 fwd->listen_port = a2port(fwdargs[1].arg);
3342 fwd->connect_path = xstrdup(fwdargs[2].arg);
3343 fwd->connect_port = PORT_STREAMLOCAL;
3344 } else {
3345 fwd->listen_host = NULL;
3346 fwd->listen_port = a2port(fwdargs[0].arg);
3347 fwd->connect_host = xstrdup(fwdargs[1].arg);
3348 fwd->connect_port = a2port(fwdargs[2].arg);
3349 }
3350 break;
3351
3352 case 4:
3353 fwd->listen_host = xstrdup(fwdargs[0].arg);
3354 fwd->listen_port = a2port(fwdargs[1].arg);
3355 fwd->connect_host = xstrdup(fwdargs[2].arg);
3356 fwd->connect_port = a2port(fwdargs[3].arg);
3357 break;
3358 default:
3359 i = 0; /* failure */
3360 }
3361
3362 free(p);
3363
3364 if (dynamicfwd) {
3365 if (!(i == 1 || i == 2))
3366 goto fail_free;
3367 } else {
3368 if (!(i == 3 || i == 4)) {
3369 if (fwd->connect_path == NULL &&
3370 fwd->listen_path == NULL)
3371 goto fail_free;
3372 }
3373 if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3374 goto fail_free;
3375 }
3376
3377 if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
3378 (!remotefwd && fwd->listen_port == 0))
3379 goto fail_free;
3380 if (fwd->connect_host != NULL &&
3381 strlen(fwd->connect_host) >= NI_MAXHOST)
3382 goto fail_free;
3383 /*
3384 * XXX - if connecting to a remote socket, max sun len may not
3385 * match this host
3386 */
3387 if (fwd->connect_path != NULL &&
3388 strlen(fwd->connect_path) >= PATH_MAX_SUN)
3389 goto fail_free;
3390 if (fwd->listen_host != NULL &&
3391 strlen(fwd->listen_host) >= NI_MAXHOST)
3392 goto fail_free;
3393 if (fwd->listen_path != NULL &&
3394 strlen(fwd->listen_path) >= PATH_MAX_SUN)
3395 goto fail_free;
3396
3397 return (i);
3398
3399 fail_free:
3400 free(fwd->connect_host);
3401 fwd->connect_host = NULL;
3402 free(fwd->connect_path);
3403 fwd->connect_path = NULL;
3404 free(fwd->listen_host);
3405 fwd->listen_host = NULL;
3406 free(fwd->listen_path);
3407 fwd->listen_path = NULL;
3408 return (0);
3409}
3410
3411int
3412parse_jump(const char *s, Options *o, int active)
3413{
3414 char *orig, *sdup, *cp;
3415 char *host = NULL, *user = NULL;
3416 int r, ret = -1, port = -1, first;
3417
3418 active &= o->proxy_command == NULL && o->jump_host == NULL;
3419
3420 orig = sdup = xstrdup(s);
3421
3422 /* Remove comment and trailing whitespace */
3423 if ((cp = strchr(orig, '#')) != NULL)
3424 *cp = '\0';
3425 rtrim(orig);
3426
3427 first = active;
3428 do {
3429 if (strcasecmp(s, "none") == 0)
3430 break;
3431 if ((cp = strrchr(sdup, ',')) == NULL)
3432 cp = sdup; /* last */
3433 else
3434 *cp++ = '\0';
3435
3436 if (first) {
3437 /* First argument and configuration is active */
3438 r = parse_ssh_uri(cp, &user, &host, &port);
3439 if (r == -1 || (r == 1 &&
3440 parse_user_host_port(cp, &user, &host, &port) != 0))
3441 goto out;
3442 } else {
3443 /* Subsequent argument or inactive configuration */
3444 r = parse_ssh_uri(cp, NULL, NULL, NULL);
3445 if (r == -1 || (r == 1 &&
3446 parse_user_host_port(cp, NULL, NULL, NULL) != 0))
3447 goto out;
3448 }
3449 first = 0; /* only check syntax for subsequent hosts */
3450 } while (cp != sdup);
3451 /* success */
3452 if (active) {
3453 if (strcasecmp(s, "none") == 0) {
3454 o->jump_host = xstrdup("none");
3455 o->jump_port = 0;
3456 } else {
3457 o->jump_user = user;
3458 o->jump_host = host;
3459 o->jump_port = port;
3460 o->proxy_command = xstrdup("none");
3461 user = host = NULL;
3462 if ((cp = strrchr(s, ',')) != NULL && cp != s) {
3463 o->jump_extra = xstrdup(s);
3464 o->jump_extra[cp - s] = '\0';
3465 }
3466 }
3467 }
3468 ret = 0;
3469 out:
3470 free(orig);
3471 free(user);
3472 free(host);
3473 return ret;
3474}
3475
3476int
3477parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
3478{
3479 char *user = NULL, *host = NULL, *path = NULL;
3480 int r, port;
3481
3482 r = parse_uri("ssh", uri, &user, &host, &port, &path);
3483 if (r == 0 && path != NULL)
3484 r = -1; /* path not allowed */
3485 if (r == 0) {
3486 if (userp != NULL) {
3487 *userp = user;
3488 user = NULL;
3489 }
3490 if (hostp != NULL) {
3491 *hostp = host;
3492 host = NULL;
3493 }
3494 if (portp != NULL)
3495 *portp = port;
3496 }
3497 free(user);
3498 free(host);
3499 free(path);
3500 return r;
3501}
3502
3503/* XXX the following is a near-verbatim copy from servconf.c; refactor */
3504static const char *
3505fmt_multistate_int(int val, const struct multistate *m)
3506{
3507 u_int i;
3508
3509 for (i = 0; m[i].key != NULL; i++) {
3510 if (m[i].value == val)
3511 return m[i].key;
3512 }
3513 return "UNKNOWN";
3514}
3515
3516static const char *
3517fmt_intarg(OpCodes code, int val)
3518{
3519 if (val == -1)
3520 return "unset";
3521 switch (code) {
3522 case oAddressFamily:
3523 return fmt_multistate_int(val, multistate_addressfamily);
3524 case oCompression:
3525 return fmt_multistate_int(val, multistate_compression);
3526 case oVerifyHostKeyDNS:
3527 case oUpdateHostkeys:
3528 return fmt_multistate_int(val, multistate_yesnoask);
3529 case oStrictHostKeyChecking:
3530 return fmt_multistate_int(val, multistate_strict_hostkey);
3531 case oControlMaster:
3532 return fmt_multistate_int(val, multistate_controlmaster);
3533 case oTunnel:
3534 return fmt_multistate_int(val, multistate_tunnel);
3535 case oRequestTTY:
3536 return fmt_multistate_int(val, multistate_requesttty);
3537 case oSessionType:
3538 return fmt_multistate_int(val, multistate_sessiontype);
3539 case oCanonicalizeHostname:
3540 return fmt_multistate_int(val, multistate_canonicalizehostname);
3541 case oAddKeysToAgent:
3542 return fmt_multistate_int(val, multistate_yesnoaskconfirm);
3543 case oPubkeyAuthentication:
3544 return fmt_multistate_int(val, multistate_pubkey_auth);
3545 case oFingerprintHash:
3546 return ssh_digest_alg_name(val);
3547 default:
3548 switch (val) {
3549 case 0:
3550 return "no";
3551 case 1:
3552 return "yes";
3553 default:
3554 return "UNKNOWN";
3555 }
3556 }
3557}
3558
3559static const char *
3560lookup_opcode_name(OpCodes code)
3561{
3562 u_int i;
3563
3564 for (i = 0; keywords[i].name != NULL; i++)
3565 if (keywords[i].opcode == code)
3566 return(keywords[i].name);
3567 return "UNKNOWN";
3568}
3569
3570static void
3571dump_cfg_int(OpCodes code, int val)
3572{
3573 if (code == oObscureKeystrokeTiming) {
3574 if (val == 0) {
3575 printf("%s no\n", lookup_opcode_name(code));
3576 return;
3577 } else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) {
3578 printf("%s yes\n", lookup_opcode_name(code));
3579 return;
3580 }
3581 /* FALLTHROUGH */
3582 }
3583 printf("%s %d\n", lookup_opcode_name(code), val);
3584}
3585
3586static void
3587dump_cfg_fmtint(OpCodes code, int val)
3588{
3589 printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
3590}
3591
3592static void
3593dump_cfg_string(OpCodes code, const char *val)
3594{
3595 if (val == NULL)
3596 return;
3597 printf("%s %s\n", lookup_opcode_name(code), val);
3598}
3599
3600static void
3601dump_cfg_strarray(OpCodes code, u_int count, char **vals)
3602{
3603 u_int i;
3604
3605 for (i = 0; i < count; i++)
3606 printf("%s %s\n", lookup_opcode_name(code), vals[i]);
3607}
3608
3609static void
3610dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
3611{
3612 u_int i;
3613
3614 printf("%s", lookup_opcode_name(code));
3615 if (count == 0)
3616 printf(" none");
3617 for (i = 0; i < count; i++)
3618 printf(" %s", vals[i]);
3619 printf("\n");
3620}
3621
3622static void
3623dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
3624{
3625 const struct Forward *fwd;
3626 u_int i;
3627
3628 /* oDynamicForward */
3629 for (i = 0; i < count; i++) {
3630 fwd = &fwds[i];
3631 if (code == oDynamicForward && fwd->connect_host != NULL &&
3632 strcmp(fwd->connect_host, "socks") != 0)
3633 continue;
3634 if (code == oLocalForward && fwd->connect_host != NULL &&
3635 strcmp(fwd->connect_host, "socks") == 0)
3636 continue;
3637 printf("%s", lookup_opcode_name(code));
3638 if (fwd->listen_port == PORT_STREAMLOCAL)
3639 printf(" %s", fwd->listen_path);
3640 else if (fwd->listen_host == NULL)
3641 printf(" %d", fwd->listen_port);
3642 else {
3643 printf(" [%s]:%d",
3644 fwd->listen_host, fwd->listen_port);
3645 }
3646 if (code != oDynamicForward) {
3647 if (fwd->connect_port == PORT_STREAMLOCAL)
3648 printf(" %s", fwd->connect_path);
3649 else if (fwd->connect_host == NULL)
3650 printf(" %d", fwd->connect_port);
3651 else {
3652 printf(" [%s]:%d",
3653 fwd->connect_host, fwd->connect_port);
3654 }
3655 }
3656 printf("\n");
3657 }
3658}
3659
3660void
3661dump_client_config(Options *o, const char *host)
3662{
3663 int i, r;
3664 char buf[8], *all_key;
3665
3666 /*
3667 * Expand HostKeyAlgorithms name lists. This isn't handled in
3668 * fill_default_options() like the other algorithm lists because
3669 * the host key algorithms are by default dynamically chosen based
3670 * on the host's keys found in known_hosts.
3671 */
3672 all_key = sshkey_alg_list(0, 0, 1, ',');
3673 if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(),
3674 all_key)) != 0)
3675 fatal_fr(r, "expand HostKeyAlgorithms");
3676 free(all_key);
3677
3678 /* Most interesting options first: user, host, port */
3679 dump_cfg_string(oHost, o->host_arg);
3680 dump_cfg_string(oUser, o->user);
3681 dump_cfg_string(oHostname, host);
3682 dump_cfg_int(oPort, o->port);
3683
3684 /* Flag options */
3685 dump_cfg_fmtint(oAddressFamily, o->address_family);
3686 dump_cfg_fmtint(oBatchMode, o->batch_mode);
3687 dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
3688 dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
3689 dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
3690 dump_cfg_fmtint(oCompression, o->compression);
3691 dump_cfg_fmtint(oControlMaster, o->control_master);
3692 dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
3693 dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings);
3694 dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
3695 dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash);
3696 dump_cfg_fmtint(oForwardX11, o->forward_x11);
3697 dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
3698 dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3699#ifdef GSSAPI
3700 dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3701 dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3702#endif /* GSSAPI */
3703 dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3704 dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3705 dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
3706 dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
3707 dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
3708 dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
3709 dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
3710 dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
3711 dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
3712 dump_cfg_fmtint(oRequestTTY, o->request_tty);
3713 dump_cfg_fmtint(oSessionType, o->session_type);
3714 dump_cfg_fmtint(oStdinNull, o->stdin_null);
3715 dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication);
3716 dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
3717 dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
3718 dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
3719 dump_cfg_fmtint(oTunnel, o->tun_open);
3720 dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
3721 dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
3722 dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
3723 dump_cfg_fmtint(oEnableEscapeCommandline, o->enable_escape_commandline);
3724 dump_cfg_fmtint(oWarnWeakCrypto, o->warn_weak_crypto);
3725
3726 /* Integer options */
3727 dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
3728 dump_cfg_int(oConnectionAttempts, o->connection_attempts);
3729 dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
3730 dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
3731 dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
3732 dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
3733 dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
3734 dump_cfg_int(oObscureKeystrokeTiming,
3735 o->obscure_keystroke_timing_interval);
3736
3737 /* String options */
3738 dump_cfg_string(oBindAddress, o->bind_address);
3739 dump_cfg_string(oBindInterface, o->bind_interface);
3740 dump_cfg_string(oCiphers, o->ciphers);
3741 dump_cfg_string(oControlPath, o->control_path);
3742 dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
3743 dump_cfg_string(oHostKeyAlias, o->host_key_alias);
3744 dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos);
3745 dump_cfg_string(oIdentityAgent, o->identity_agent);
3746 dump_cfg_string(oIgnoreUnknown, o->ignored_unknown);
3747 dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
3748 dump_cfg_string(oKexAlgorithms, o->kex_algorithms);
3749 dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms);
3750 dump_cfg_string(oLocalCommand, o->local_command);
3751 dump_cfg_string(oRemoteCommand, o->remote_command);
3752 dump_cfg_string(oLogLevel, log_level_name(o->log_level));
3753 dump_cfg_string(oMacs, o->macs);
3754#ifdef ENABLE_PKCS11
3755 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
3756#endif
3757 dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
3758 dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
3759 dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
3760 dump_cfg_string(oXAuthLocation, o->xauth_location);
3761 dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
3762 dump_cfg_string(oTag, o->tag);
3763 dump_cfg_string(oVersionAddendum, o->version_addendum);
3764
3765 /* Forwards */
3766 dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
3767 dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
3768 dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
3769
3770 /* String array options */
3771 dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
3772 dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
3773 dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
3774 dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
3775 dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
3776 dump_cfg_strarray_oneline(oRevokedHostKeys, o->num_revoked_host_keys, o->revoked_host_keys);
3777 dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
3778 dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
3779 dump_cfg_strarray_oneline(oLogVerbose,
3780 o->num_log_verbose, o->log_verbose);
3781 dump_cfg_strarray_oneline(oChannelTimeout,
3782 o->num_channel_timeouts, o->channel_timeouts);
3783
3784 /* Special cases */
3785
3786 /* PermitRemoteOpen */
3787 if (o->num_permitted_remote_opens == 0)
3788 printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen));
3789 else
3790 dump_cfg_strarray_oneline(oPermitRemoteOpen,
3791 o->num_permitted_remote_opens, o->permitted_remote_opens);
3792
3793 /* AddKeysToAgent */
3794 if (o->add_keys_to_agent_lifespan <= 0)
3795 dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent);
3796 else {
3797 printf("addkeystoagent%s %d\n",
3798 o->add_keys_to_agent == 3 ? " confirm" : "",
3799 o->add_keys_to_agent_lifespan);
3800 }
3801
3802 /* oForwardAgent */
3803 if (o->forward_agent_sock_path == NULL)
3804 dump_cfg_fmtint(oForwardAgent, o->forward_agent);
3805 else
3806 dump_cfg_string(oForwardAgent, o->forward_agent_sock_path);
3807
3808 /* oConnectTimeout */
3809 if (o->connection_timeout == -1)
3810 printf("connecttimeout none\n");
3811 else
3812 dump_cfg_int(oConnectTimeout, o->connection_timeout);
3813
3814 /* oTunnelDevice */
3815 printf("tunneldevice");
3816 if (o->tun_local == SSH_TUNID_ANY)
3817 printf(" any");
3818 else
3819 printf(" %d", o->tun_local);
3820 if (o->tun_remote == SSH_TUNID_ANY)
3821 printf(":any");
3822 else
3823 printf(":%d", o->tun_remote);
3824 printf("\n");
3825
3826 /* oCanonicalizePermittedCNAMEs */
3827 printf("canonicalizePermittedcnames");
3828 if (o->num_permitted_cnames == 0)
3829 printf(" none");
3830 for (i = 0; i < o->num_permitted_cnames; i++) {
3831 printf(" %s:%s", o->permitted_cnames[i].source_list,
3832 o->permitted_cnames[i].target_list);
3833 }
3834 printf("\n");
3835
3836 /* oControlPersist */
3837 if (o->control_persist == 0 || o->control_persist_timeout == 0)
3838 dump_cfg_fmtint(oControlPersist, o->control_persist);
3839 else
3840 dump_cfg_int(oControlPersist, o->control_persist_timeout);
3841
3842 /* oEscapeChar */
3843 if (o->escape_char == SSH_ESCAPECHAR_NONE)
3844 printf("escapechar none\n");
3845 else {
3846 vis(buf, o->escape_char, VIS_WHITE, 0);
3847 printf("escapechar %s\n", buf);
3848 }
3849
3850 /* oIPQoS */
3851 printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
3852 printf("%s\n", iptos2str(o->ip_qos_bulk));
3853
3854 /* oRekeyLimit */
3855 printf("rekeylimit %llu %d\n",
3856 (unsigned long long)o->rekey_limit, o->rekey_interval);
3857
3858 /* oStreamLocalBindMask */
3859 printf("streamlocalbindmask 0%o\n",
3860 o->fwd_opts.streamlocal_bind_mask);
3861
3862 /* oLogFacility */
3863 printf("syslogfacility %s\n", log_facility_name(o->log_facility));
3864
3865 /* oProxyCommand / oProxyJump */
3866 if (o->jump_host == NULL)
3867 dump_cfg_string(oProxyCommand, o->proxy_command);
3868 else {
3869 /* Check for numeric addresses */
3870 i = strchr(o->jump_host, ':') != NULL ||
3871 strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
3872 snprintf(buf, sizeof(buf), "%d", o->jump_port);
3873 printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
3874 /* optional additional jump spec */
3875 o->jump_extra == NULL ? "" : o->jump_extra,
3876 o->jump_extra == NULL ? "" : ",",
3877 /* optional user */
3878 o->jump_user == NULL ? "" : o->jump_user,
3879 o->jump_user == NULL ? "" : "@",
3880 /* opening [ if hostname is numeric */
3881 i ? "[" : "",
3882 /* mandatory hostname */
3883 o->jump_host,
3884 /* closing ] if hostname is numeric */
3885 i ? "]" : "",
3886 /* optional port number */
3887 o->jump_port <= 0 ? "" : ":",
3888 o->jump_port <= 0 ? "" : buf);
3889 }
3890}