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