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