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