xref: /dflybsd-src/crypto/openssh/readconf.c (revision ba1276acd1c8c22d225b1bcf370a14c878644f44)
1*ba1276acSMatthew Dillon /* $OpenBSD: readconf.c,v 1.387 2024/05/17 02:39:11 jsg Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos  * Author: Tatu Ylonen <ylo@cs.hut.fi>
418de8d7fSPeter Avalos  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
518de8d7fSPeter Avalos  *                    All rights reserved
618de8d7fSPeter Avalos  * Functions for reading the configuration files.
718de8d7fSPeter Avalos  *
818de8d7fSPeter Avalos  * As far as I am concerned, the code I have written for this software
918de8d7fSPeter Avalos  * can be used freely for any purpose.  Any derived versions of this
1018de8d7fSPeter Avalos  * software must be clearly marked as such, and if the derived work is
1118de8d7fSPeter Avalos  * incompatible with the protocol description in the RFC file, it must be
1218de8d7fSPeter Avalos  * called by a name other than "ssh" or "Secure Shell".
1318de8d7fSPeter Avalos  */
1418de8d7fSPeter Avalos 
1518de8d7fSPeter Avalos #include "includes.h"
1618de8d7fSPeter Avalos 
1718de8d7fSPeter Avalos #include <sys/types.h>
1818de8d7fSPeter Avalos #include <sys/stat.h>
1918de8d7fSPeter Avalos #include <sys/socket.h>
2036e94dc5SPeter Avalos #include <sys/wait.h>
2136e94dc5SPeter Avalos #include <sys/un.h>
2218de8d7fSPeter Avalos 
23*ba1276acSMatthew Dillon #include <net/if.h>
2418de8d7fSPeter Avalos #include <netinet/in.h>
259f304aafSPeter Avalos #include <netinet/in_systm.h>
269f304aafSPeter Avalos #include <netinet/ip.h>
2736e94dc5SPeter Avalos #include <arpa/inet.h>
2818de8d7fSPeter Avalos 
2918de8d7fSPeter Avalos #include <ctype.h>
3018de8d7fSPeter Avalos #include <errno.h>
3136e94dc5SPeter Avalos #include <fcntl.h>
32*ba1276acSMatthew Dillon #ifdef HAVE_IFADDRS_H
33*ba1276acSMatthew Dillon # include <ifaddrs.h>
34*ba1276acSMatthew Dillon #endif
35e9778795SPeter Avalos #include <limits.h>
3618de8d7fSPeter Avalos #include <netdb.h>
3736e94dc5SPeter Avalos #ifdef HAVE_PATHS_H
3836e94dc5SPeter Avalos # include <paths.h>
3936e94dc5SPeter Avalos #endif
4036e94dc5SPeter Avalos #include <pwd.h>
4118de8d7fSPeter Avalos #include <signal.h>
4218de8d7fSPeter Avalos #include <stdio.h>
4318de8d7fSPeter Avalos #include <string.h>
440cbfa66cSDaniel Fojt #include <stdarg.h>
4518de8d7fSPeter Avalos #include <unistd.h>
46e9778795SPeter Avalos #ifdef USE_SYSTEM_GLOB
47e9778795SPeter Avalos # include <glob.h>
48e9778795SPeter Avalos #else
49e9778795SPeter Avalos # include "openbsd-compat/glob.h"
50e9778795SPeter Avalos #endif
5136e94dc5SPeter Avalos #ifdef HAVE_UTIL_H
5236e94dc5SPeter Avalos #include <util.h>
5336e94dc5SPeter Avalos #endif
54e9778795SPeter Avalos #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
55e9778795SPeter Avalos # include <vis.h>
56e9778795SPeter Avalos #endif
5718de8d7fSPeter Avalos 
5818de8d7fSPeter Avalos #include "xmalloc.h"
5918de8d7fSPeter Avalos #include "ssh.h"
60664f4763Szrj #include "ssherr.h"
6118de8d7fSPeter Avalos #include "cipher.h"
6218de8d7fSPeter Avalos #include "pathnames.h"
6318de8d7fSPeter Avalos #include "log.h"
64e9778795SPeter Avalos #include "sshkey.h"
6536e94dc5SPeter Avalos #include "misc.h"
6618de8d7fSPeter Avalos #include "readconf.h"
6718de8d7fSPeter Avalos #include "match.h"
6818de8d7fSPeter Avalos #include "kex.h"
6918de8d7fSPeter Avalos #include "mac.h"
7036e94dc5SPeter Avalos #include "uidswap.h"
71e9778795SPeter Avalos #include "myproposal.h"
72e9778795SPeter Avalos #include "digest.h"
7318de8d7fSPeter Avalos 
7418de8d7fSPeter Avalos /* Format of the configuration file:
7518de8d7fSPeter Avalos 
7618de8d7fSPeter Avalos    # Configuration data is parsed as follows:
7718de8d7fSPeter Avalos    #  1. command line options
7818de8d7fSPeter Avalos    #  2. user-specific file
7918de8d7fSPeter Avalos    #  3. system-wide file
8018de8d7fSPeter Avalos    # Any configuration value is only changed the first time it is set.
8118de8d7fSPeter Avalos    # Thus, host-specific definitions should be at the beginning of the
8218de8d7fSPeter Avalos    # configuration file, and defaults at the end.
8318de8d7fSPeter Avalos 
8418de8d7fSPeter Avalos    # Host-specific declarations.  These may override anything above.  A single
8518de8d7fSPeter Avalos    # host may match multiple declarations; these are processed in the order
8618de8d7fSPeter Avalos    # that they are given in.
8718de8d7fSPeter Avalos 
8818de8d7fSPeter Avalos    Host *.ngs.fi ngs.fi
8918de8d7fSPeter Avalos      User foo
9018de8d7fSPeter Avalos 
9118de8d7fSPeter Avalos    Host fake.com
920cbfa66cSDaniel Fojt      Hostname another.host.name.real.org
9318de8d7fSPeter Avalos      User blaah
9418de8d7fSPeter Avalos      Port 34289
9518de8d7fSPeter Avalos      ForwardX11 no
9618de8d7fSPeter Avalos      ForwardAgent no
9718de8d7fSPeter Avalos 
9818de8d7fSPeter Avalos    Host books.com
9918de8d7fSPeter Avalos      RemoteForward 9999 shadows.cs.hut.fi:9999
100ce74bacaSMatthew Dillon      Ciphers 3des-cbc
10118de8d7fSPeter Avalos 
10218de8d7fSPeter Avalos    Host fascist.blob.com
10318de8d7fSPeter Avalos      Port 23123
10418de8d7fSPeter Avalos      User tylonen
10518de8d7fSPeter Avalos      PasswordAuthentication no
10618de8d7fSPeter Avalos 
10718de8d7fSPeter Avalos    Host puukko.hut.fi
10818de8d7fSPeter Avalos      User t35124p
10918de8d7fSPeter Avalos      ProxyCommand ssh-proxy %h %p
11018de8d7fSPeter Avalos 
11118de8d7fSPeter Avalos    Host *.fr
11218de8d7fSPeter Avalos      PublicKeyAuthentication no
11318de8d7fSPeter Avalos 
11418de8d7fSPeter Avalos    Host *.su
115ce74bacaSMatthew Dillon      Ciphers aes128-ctr
11618de8d7fSPeter Avalos      PasswordAuthentication no
11718de8d7fSPeter Avalos 
11818de8d7fSPeter Avalos    Host vpn.fake.com
11918de8d7fSPeter Avalos      Tunnel yes
12018de8d7fSPeter Avalos      TunnelDevice 3
12118de8d7fSPeter Avalos 
12218de8d7fSPeter Avalos    # Defaults for various options
12318de8d7fSPeter Avalos    Host *
12418de8d7fSPeter Avalos      ForwardAgent no
12518de8d7fSPeter Avalos      ForwardX11 no
12618de8d7fSPeter Avalos      PasswordAuthentication yes
12718de8d7fSPeter Avalos      StrictHostKeyChecking yes
12818de8d7fSPeter Avalos      TcpKeepAlive no
12918de8d7fSPeter Avalos      IdentityFile ~/.ssh/identity
13018de8d7fSPeter Avalos      Port 22
13118de8d7fSPeter Avalos      EscapeChar ~
13218de8d7fSPeter Avalos 
13318de8d7fSPeter Avalos */
13418de8d7fSPeter Avalos 
135e9778795SPeter Avalos static int read_config_file_depth(const char *filename, struct passwd *pw,
136e9778795SPeter Avalos     const char *host, const char *original_host, Options *options,
137664f4763Szrj     int flags, int *activep, int *want_final_pass, int depth);
138e9778795SPeter Avalos static int process_config_line_depth(Options *options, struct passwd *pw,
139e9778795SPeter Avalos     const char *host, const char *original_host, char *line,
140664f4763Szrj     const char *filename, int linenum, int *activep, int flags,
141664f4763Szrj     int *want_final_pass, int depth);
142e9778795SPeter Avalos 
14318de8d7fSPeter Avalos /* Keyword tokens. */
14418de8d7fSPeter Avalos 
14518de8d7fSPeter Avalos typedef enum {
14618de8d7fSPeter Avalos 	oBadOption,
147*ba1276acSMatthew Dillon 	oHost, oMatch, oInclude, oTag,
148856ea928SPeter Avalos 	oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
149856ea928SPeter Avalos 	oGatewayPorts, oExitOnForwardFailure,
1500cbfa66cSDaniel Fojt 	oPasswordAuthentication,
15150a69bb5SSascha Wildner 	oXAuthLocation,
1520cbfa66cSDaniel Fojt 	oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward,
15350a69bb5SSascha Wildner 	oPermitRemoteOpen,
154e9778795SPeter Avalos 	oCertificateFile, oAddKeysToAgent, oIdentityAgent,
1550cbfa66cSDaniel Fojt 	oUser, oEscapeChar, oProxyCommand,
15618de8d7fSPeter Avalos 	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
15718de8d7fSPeter Avalos 	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
1580cbfa66cSDaniel Fojt 	oTCPKeepAlive, oNumberOfPasswordPrompts,
15950a69bb5SSascha Wildner 	oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs,
160e9778795SPeter Avalos 	oPubkeyAuthentication,
16118de8d7fSPeter Avalos 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
16218de8d7fSPeter Avalos 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
163664f4763Szrj 	oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
16418de8d7fSPeter Avalos 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
16518de8d7fSPeter Avalos 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
16618de8d7fSPeter Avalos 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
16718de8d7fSPeter Avalos 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
168664f4763Szrj 	oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
169856ea928SPeter Avalos 	oHashKnownHosts,
170ce74bacaSMatthew Dillon 	oTunnel, oTunnelDevice,
171ce74bacaSMatthew Dillon 	oLocalCommand, oPermitLocalCommand, oRemoteCommand,
172e9778795SPeter Avalos 	oVisualHostKey,
17350a69bb5SSascha Wildner 	oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
17450a69bb5SSascha Wildner 	oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass,
17536e94dc5SPeter Avalos 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
17636e94dc5SPeter Avalos 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
177e9778795SPeter Avalos 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
17850a69bb5SSascha Wildner 	oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
17950a69bb5SSascha Wildner 	oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
180ee116499SAntonio Huete Jimenez 	oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
181*ba1276acSMatthew Dillon 	oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
182ce74bacaSMatthew Dillon 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
18318de8d7fSPeter Avalos } OpCodes;
18418de8d7fSPeter Avalos 
18518de8d7fSPeter Avalos /* Textual representations of the tokens. */
18618de8d7fSPeter Avalos 
18718de8d7fSPeter Avalos static struct {
18818de8d7fSPeter Avalos 	const char *name;
18918de8d7fSPeter Avalos 	OpCodes opcode;
19018de8d7fSPeter Avalos } keywords[] = {
191ce74bacaSMatthew Dillon 	/* Deprecated options */
192ce74bacaSMatthew Dillon 	{ "protocol", oIgnore }, /* NB. silently ignored */
193ce74bacaSMatthew Dillon 	{ "cipher", oDeprecated },
194ce74bacaSMatthew Dillon 	{ "fallbacktorsh", oDeprecated },
195ce74bacaSMatthew Dillon 	{ "globalknownhostsfile2", oDeprecated },
196ce74bacaSMatthew Dillon 	{ "rhostsauthentication", oDeprecated },
197ce74bacaSMatthew Dillon 	{ "userknownhostsfile2", oDeprecated },
198ce74bacaSMatthew Dillon 	{ "useroaming", oDeprecated },
199ce74bacaSMatthew Dillon 	{ "usersh", oDeprecated },
200664f4763Szrj 	{ "useprivilegedport", oDeprecated },
201ce74bacaSMatthew Dillon 
202ce74bacaSMatthew Dillon 	/* Unsupported options */
203ce74bacaSMatthew Dillon 	{ "afstokenpassing", oUnsupported },
204ce74bacaSMatthew Dillon 	{ "kerberosauthentication", oUnsupported },
205ce74bacaSMatthew Dillon 	{ "kerberostgtpassing", oUnsupported },
2060cbfa66cSDaniel Fojt 	{ "rsaauthentication", oUnsupported },
2070cbfa66cSDaniel Fojt 	{ "rhostsrsaauthentication", oUnsupported },
2080cbfa66cSDaniel Fojt 	{ "compressionlevel", oUnsupported },
209ce74bacaSMatthew Dillon 
210ce74bacaSMatthew Dillon 	/* Sometimes-unsupported options */
211ce74bacaSMatthew Dillon #if defined(GSSAPI)
212ce74bacaSMatthew Dillon 	{ "gssapiauthentication", oGssAuthentication },
213ce74bacaSMatthew Dillon 	{ "gssapidelegatecredentials", oGssDelegateCreds },
214ce74bacaSMatthew Dillon # else
215ce74bacaSMatthew Dillon 	{ "gssapiauthentication", oUnsupported },
216ce74bacaSMatthew Dillon 	{ "gssapidelegatecredentials", oUnsupported },
217ce74bacaSMatthew Dillon #endif
218ce74bacaSMatthew Dillon #ifdef ENABLE_PKCS11
219ce74bacaSMatthew Dillon 	{ "pkcs11provider", oPKCS11Provider },
220664f4763Szrj 	{ "smartcarddevice", oPKCS11Provider },
221ce74bacaSMatthew Dillon # else
222ce74bacaSMatthew Dillon 	{ "smartcarddevice", oUnsupported },
223ce74bacaSMatthew Dillon 	{ "pkcs11provider", oUnsupported },
224ce74bacaSMatthew Dillon #endif
225ce74bacaSMatthew Dillon 
22618de8d7fSPeter Avalos 	{ "forwardagent", oForwardAgent },
22718de8d7fSPeter Avalos 	{ "forwardx11", oForwardX11 },
22818de8d7fSPeter Avalos 	{ "forwardx11trusted", oForwardX11Trusted },
229856ea928SPeter Avalos 	{ "forwardx11timeout", oForwardX11Timeout },
23018de8d7fSPeter Avalos 	{ "exitonforwardfailure", oExitOnForwardFailure },
23118de8d7fSPeter Avalos 	{ "xauthlocation", oXAuthLocation },
23218de8d7fSPeter Avalos 	{ "gatewayports", oGatewayPorts },
23318de8d7fSPeter Avalos 	{ "passwordauthentication", oPasswordAuthentication },
23418de8d7fSPeter Avalos 	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
23518de8d7fSPeter Avalos 	{ "kbdinteractivedevices", oKbdInteractiveDevices },
23650a69bb5SSascha Wildner 	{ "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */
23750a69bb5SSascha Wildner 	{ "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */
23850a69bb5SSascha Wildner 	{ "tisauthentication", oKbdInteractiveAuthentication },  /* alias */
23918de8d7fSPeter Avalos 	{ "pubkeyauthentication", oPubkeyAuthentication },
24018de8d7fSPeter Avalos 	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
24118de8d7fSPeter Avalos 	{ "hostbasedauthentication", oHostbasedAuthentication },
24218de8d7fSPeter Avalos 	{ "identityfile", oIdentityFile },
243cb5eb4f1SPeter Avalos 	{ "identityfile2", oIdentityFile },			/* obsolete */
24418de8d7fSPeter Avalos 	{ "identitiesonly", oIdentitiesOnly },
245e9778795SPeter Avalos 	{ "certificatefile", oCertificateFile },
246e9778795SPeter Avalos 	{ "addkeystoagent", oAddKeysToAgent },
247e9778795SPeter Avalos 	{ "identityagent", oIdentityAgent },
2480cbfa66cSDaniel Fojt 	{ "hostname", oHostname },
24918de8d7fSPeter Avalos 	{ "hostkeyalias", oHostKeyAlias },
25018de8d7fSPeter Avalos 	{ "proxycommand", oProxyCommand },
25118de8d7fSPeter Avalos 	{ "port", oPort },
25218de8d7fSPeter Avalos 	{ "ciphers", oCiphers },
25318de8d7fSPeter Avalos 	{ "macs", oMacs },
25418de8d7fSPeter Avalos 	{ "remoteforward", oRemoteForward },
25518de8d7fSPeter Avalos 	{ "localforward", oLocalForward },
25650a69bb5SSascha Wildner 	{ "permitremoteopen", oPermitRemoteOpen },
25718de8d7fSPeter Avalos 	{ "user", oUser },
25818de8d7fSPeter Avalos 	{ "host", oHost },
25936e94dc5SPeter Avalos 	{ "match", oMatch },
260*ba1276acSMatthew Dillon 	{ "tag", oTag },
26118de8d7fSPeter Avalos 	{ "escapechar", oEscapeChar },
26218de8d7fSPeter Avalos 	{ "globalknownhostsfile", oGlobalKnownHostsFile },
263cb5eb4f1SPeter Avalos 	{ "userknownhostsfile", oUserKnownHostsFile },
26418de8d7fSPeter Avalos 	{ "connectionattempts", oConnectionAttempts },
26518de8d7fSPeter Avalos 	{ "batchmode", oBatchMode },
26618de8d7fSPeter Avalos 	{ "checkhostip", oCheckHostIP },
26718de8d7fSPeter Avalos 	{ "stricthostkeychecking", oStrictHostKeyChecking },
26818de8d7fSPeter Avalos 	{ "compression", oCompression },
26918de8d7fSPeter Avalos 	{ "tcpkeepalive", oTCPKeepAlive },
27018de8d7fSPeter Avalos 	{ "keepalive", oTCPKeepAlive },				/* obsolete */
27118de8d7fSPeter Avalos 	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
272ce74bacaSMatthew Dillon 	{ "syslogfacility", oLogFacility },
27318de8d7fSPeter Avalos 	{ "loglevel", oLogLevel },
27450a69bb5SSascha Wildner 	{ "logverbose", oLogVerbose },
27518de8d7fSPeter Avalos 	{ "dynamicforward", oDynamicForward },
27618de8d7fSPeter Avalos 	{ "preferredauthentications", oPreferredAuthentications },
27718de8d7fSPeter Avalos 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
278664f4763Szrj 	{ "casignaturealgorithms", oCASignatureAlgorithms },
27918de8d7fSPeter Avalos 	{ "bindaddress", oBindAddress },
280664f4763Szrj 	{ "bindinterface", oBindInterface },
28118de8d7fSPeter Avalos 	{ "clearallforwardings", oClearAllForwardings },
28218de8d7fSPeter Avalos 	{ "enablesshkeysign", oEnableSSHKeysign },
28318de8d7fSPeter Avalos 	{ "verifyhostkeydns", oVerifyHostKeyDNS },
28418de8d7fSPeter Avalos 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
28518de8d7fSPeter Avalos 	{ "rekeylimit", oRekeyLimit },
28618de8d7fSPeter Avalos 	{ "connecttimeout", oConnectTimeout },
28718de8d7fSPeter Avalos 	{ "addressfamily", oAddressFamily },
28818de8d7fSPeter Avalos 	{ "serveraliveinterval", oServerAliveInterval },
28918de8d7fSPeter Avalos 	{ "serveralivecountmax", oServerAliveCountMax },
29018de8d7fSPeter Avalos 	{ "sendenv", oSendEnv },
291664f4763Szrj 	{ "setenv", oSetEnv },
29218de8d7fSPeter Avalos 	{ "controlpath", oControlPath },
29318de8d7fSPeter Avalos 	{ "controlmaster", oControlMaster },
294856ea928SPeter Avalos 	{ "controlpersist", oControlPersist },
29518de8d7fSPeter Avalos 	{ "hashknownhosts", oHashKnownHosts },
296e9778795SPeter Avalos 	{ "include", oInclude },
29718de8d7fSPeter Avalos 	{ "tunnel", oTunnel },
29818de8d7fSPeter Avalos 	{ "tunneldevice", oTunnelDevice },
29918de8d7fSPeter Avalos 	{ "localcommand", oLocalCommand },
30018de8d7fSPeter Avalos 	{ "permitlocalcommand", oPermitLocalCommand },
301ce74bacaSMatthew Dillon 	{ "remotecommand", oRemoteCommand },
30218de8d7fSPeter Avalos 	{ "visualhostkey", oVisualHostKey },
3039f304aafSPeter Avalos 	{ "kexalgorithms", oKexAlgorithms },
3049f304aafSPeter Avalos 	{ "ipqos", oIPQoS },
3051c188a7fSPeter Avalos 	{ "requesttty", oRequestTTY },
30650a69bb5SSascha Wildner 	{ "sessiontype", oSessionType },
30750a69bb5SSascha Wildner 	{ "stdinnull", oStdinNull },
30850a69bb5SSascha Wildner 	{ "forkafterauthentication", oForkAfterAuthentication },
30936e94dc5SPeter Avalos 	{ "proxyusefdpass", oProxyUseFdpass },
31036e94dc5SPeter Avalos 	{ "canonicaldomains", oCanonicalDomains },
31136e94dc5SPeter Avalos 	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
31236e94dc5SPeter Avalos 	{ "canonicalizehostname", oCanonicalizeHostname },
31336e94dc5SPeter Avalos 	{ "canonicalizemaxdots", oCanonicalizeMaxDots },
31436e94dc5SPeter Avalos 	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
31536e94dc5SPeter Avalos 	{ "streamlocalbindmask", oStreamLocalBindMask },
31636e94dc5SPeter Avalos 	{ "streamlocalbindunlink", oStreamLocalBindUnlink },
317e9778795SPeter Avalos 	{ "revokedhostkeys", oRevokedHostKeys },
318e9778795SPeter Avalos 	{ "fingerprinthash", oFingerprintHash },
319e9778795SPeter Avalos 	{ "updatehostkeys", oUpdateHostkeys },
32050a69bb5SSascha Wildner 	{ "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms },
32150a69bb5SSascha Wildner 	{ "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */
32250a69bb5SSascha Wildner 	{ "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
32350a69bb5SSascha Wildner 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
32436e94dc5SPeter Avalos 	{ "ignoreunknown", oIgnoreUnknown },
325e9778795SPeter Avalos 	{ "proxyjump", oProxyJump },
3260cbfa66cSDaniel Fojt 	{ "securitykeyprovider", oSecurityKeyProvider },
32750a69bb5SSascha Wildner 	{ "knownhostscommand", oKnownHostsCommand },
328ee116499SAntonio Huete Jimenez 	{ "requiredrsasize", oRequiredRSASize },
329*ba1276acSMatthew Dillon 	{ "enableescapecommandline", oEnableEscapeCommandline },
330*ba1276acSMatthew Dillon 	{ "obscurekeystroketiming", oObscureKeystrokeTiming },
331*ba1276acSMatthew Dillon 	{ "channeltimeout", oChannelTimeout },
332cb5eb4f1SPeter Avalos 
33318de8d7fSPeter Avalos 	{ NULL, oBadOption }
33418de8d7fSPeter Avalos };
33518de8d7fSPeter Avalos 
33650a69bb5SSascha Wildner static const char *lookup_opcode_name(OpCodes code);
3370cbfa66cSDaniel Fojt 
3380cbfa66cSDaniel Fojt const char *
kex_default_pk_alg(void)3390cbfa66cSDaniel Fojt kex_default_pk_alg(void)
3400cbfa66cSDaniel Fojt {
34150a69bb5SSascha Wildner 	static char *pkalgs;
34250a69bb5SSascha Wildner 
34350a69bb5SSascha Wildner 	if (pkalgs == NULL) {
34450a69bb5SSascha Wildner 		char *all_key;
34550a69bb5SSascha Wildner 
34650a69bb5SSascha Wildner 		all_key = sshkey_alg_list(0, 0, 1, ',');
34750a69bb5SSascha Wildner 		pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
34850a69bb5SSascha Wildner 		free(all_key);
34950a69bb5SSascha Wildner 	}
35050a69bb5SSascha Wildner 	return pkalgs;
3510cbfa66cSDaniel Fojt }
3520cbfa66cSDaniel Fojt 
3530cbfa66cSDaniel Fojt char *
ssh_connection_hash(const char * thishost,const char * host,const char * portstr,const char * user,const char * jumphost)3540cbfa66cSDaniel Fojt ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
355*ba1276acSMatthew Dillon     const char *user, const char *jumphost)
3560cbfa66cSDaniel Fojt {
3570cbfa66cSDaniel Fojt 	struct ssh_digest_ctx *md;
3580cbfa66cSDaniel Fojt 	u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
3590cbfa66cSDaniel Fojt 
3600cbfa66cSDaniel Fojt 	if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
3610cbfa66cSDaniel Fojt 	    ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
3620cbfa66cSDaniel Fojt 	    ssh_digest_update(md, host, strlen(host)) < 0 ||
3630cbfa66cSDaniel Fojt 	    ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
3640cbfa66cSDaniel Fojt 	    ssh_digest_update(md, user, strlen(user)) < 0 ||
365*ba1276acSMatthew Dillon 	    ssh_digest_update(md, jumphost, strlen(jumphost)) < 0 ||
3660cbfa66cSDaniel Fojt 	    ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
36750a69bb5SSascha Wildner 		fatal_f("mux digest failed");
3680cbfa66cSDaniel Fojt 	ssh_digest_free(md);
3690cbfa66cSDaniel Fojt 	return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
3700cbfa66cSDaniel Fojt }
3710cbfa66cSDaniel Fojt 
37218de8d7fSPeter Avalos /*
37318de8d7fSPeter Avalos  * Adds a local TCP/IP port forward to options.  Never returns if there is an
37418de8d7fSPeter Avalos  * error.
37518de8d7fSPeter Avalos  */
37618de8d7fSPeter Avalos 
37718de8d7fSPeter Avalos void
add_local_forward(Options * options,const struct Forward * newfwd)37836e94dc5SPeter Avalos add_local_forward(Options *options, const struct Forward *newfwd)
37918de8d7fSPeter Avalos {
38036e94dc5SPeter Avalos 	struct Forward *fwd;
381e9778795SPeter Avalos 	int i;
382e9778795SPeter Avalos 
383e9778795SPeter Avalos 	/* Don't add duplicates */
384e9778795SPeter Avalos 	for (i = 0; i < options->num_local_forwards; i++) {
385e9778795SPeter Avalos 		if (forward_equals(newfwd, options->local_forwards + i))
386e9778795SPeter Avalos 			return;
387e9778795SPeter Avalos 	}
388e9778795SPeter Avalos 	options->local_forwards = xreallocarray(options->local_forwards,
389856ea928SPeter Avalos 	    options->num_local_forwards + 1,
390856ea928SPeter Avalos 	    sizeof(*options->local_forwards));
39118de8d7fSPeter Avalos 	fwd = &options->local_forwards[options->num_local_forwards++];
39218de8d7fSPeter Avalos 
393cb5eb4f1SPeter Avalos 	fwd->listen_host = newfwd->listen_host;
39418de8d7fSPeter Avalos 	fwd->listen_port = newfwd->listen_port;
39536e94dc5SPeter Avalos 	fwd->listen_path = newfwd->listen_path;
396cb5eb4f1SPeter Avalos 	fwd->connect_host = newfwd->connect_host;
39718de8d7fSPeter Avalos 	fwd->connect_port = newfwd->connect_port;
39836e94dc5SPeter Avalos 	fwd->connect_path = newfwd->connect_path;
39918de8d7fSPeter Avalos }
40018de8d7fSPeter Avalos 
40118de8d7fSPeter Avalos /*
40218de8d7fSPeter Avalos  * Adds a remote TCP/IP port forward to options.  Never returns if there is
40318de8d7fSPeter Avalos  * an error.
40418de8d7fSPeter Avalos  */
40518de8d7fSPeter Avalos 
40618de8d7fSPeter Avalos void
add_remote_forward(Options * options,const struct Forward * newfwd)40736e94dc5SPeter Avalos add_remote_forward(Options *options, const struct Forward *newfwd)
40818de8d7fSPeter Avalos {
40936e94dc5SPeter Avalos 	struct Forward *fwd;
410e9778795SPeter Avalos 	int i;
411856ea928SPeter Avalos 
412e9778795SPeter Avalos 	/* Don't add duplicates */
413e9778795SPeter Avalos 	for (i = 0; i < options->num_remote_forwards; i++) {
414e9778795SPeter Avalos 		if (forward_equals(newfwd, options->remote_forwards + i))
415e9778795SPeter Avalos 			return;
416e9778795SPeter Avalos 	}
417e9778795SPeter Avalos 	options->remote_forwards = xreallocarray(options->remote_forwards,
418856ea928SPeter Avalos 	    options->num_remote_forwards + 1,
419856ea928SPeter Avalos 	    sizeof(*options->remote_forwards));
42018de8d7fSPeter Avalos 	fwd = &options->remote_forwards[options->num_remote_forwards++];
42118de8d7fSPeter Avalos 
422cb5eb4f1SPeter Avalos 	fwd->listen_host = newfwd->listen_host;
42318de8d7fSPeter Avalos 	fwd->listen_port = newfwd->listen_port;
42436e94dc5SPeter Avalos 	fwd->listen_path = newfwd->listen_path;
425cb5eb4f1SPeter Avalos 	fwd->connect_host = newfwd->connect_host;
42618de8d7fSPeter Avalos 	fwd->connect_port = newfwd->connect_port;
42736e94dc5SPeter Avalos 	fwd->connect_path = newfwd->connect_path;
42899e85e0dSPeter Avalos 	fwd->handle = newfwd->handle;
429856ea928SPeter Avalos 	fwd->allocated_port = 0;
43018de8d7fSPeter Avalos }
43118de8d7fSPeter Avalos 
43218de8d7fSPeter Avalos static void
clear_forwardings(Options * options)43318de8d7fSPeter Avalos clear_forwardings(Options *options)
43418de8d7fSPeter Avalos {
43518de8d7fSPeter Avalos 	int i;
43618de8d7fSPeter Avalos 
43718de8d7fSPeter Avalos 	for (i = 0; i < options->num_local_forwards; i++) {
43836e94dc5SPeter Avalos 		free(options->local_forwards[i].listen_host);
43936e94dc5SPeter Avalos 		free(options->local_forwards[i].listen_path);
44036e94dc5SPeter Avalos 		free(options->local_forwards[i].connect_host);
44136e94dc5SPeter Avalos 		free(options->local_forwards[i].connect_path);
44218de8d7fSPeter Avalos 	}
443856ea928SPeter Avalos 	if (options->num_local_forwards > 0) {
44436e94dc5SPeter Avalos 		free(options->local_forwards);
445856ea928SPeter Avalos 		options->local_forwards = NULL;
446856ea928SPeter Avalos 	}
44718de8d7fSPeter Avalos 	options->num_local_forwards = 0;
44818de8d7fSPeter Avalos 	for (i = 0; i < options->num_remote_forwards; i++) {
44936e94dc5SPeter Avalos 		free(options->remote_forwards[i].listen_host);
45036e94dc5SPeter Avalos 		free(options->remote_forwards[i].listen_path);
45136e94dc5SPeter Avalos 		free(options->remote_forwards[i].connect_host);
45236e94dc5SPeter Avalos 		free(options->remote_forwards[i].connect_path);
45318de8d7fSPeter Avalos 	}
454856ea928SPeter Avalos 	if (options->num_remote_forwards > 0) {
45536e94dc5SPeter Avalos 		free(options->remote_forwards);
456856ea928SPeter Avalos 		options->remote_forwards = NULL;
457856ea928SPeter Avalos 	}
45818de8d7fSPeter Avalos 	options->num_remote_forwards = 0;
45918de8d7fSPeter Avalos 	options->tun_open = SSH_TUNMODE_NO;
46018de8d7fSPeter Avalos }
46118de8d7fSPeter Avalos 
46236e94dc5SPeter Avalos void
add_certificate_file(Options * options,const char * path,int userprovided)463e9778795SPeter Avalos add_certificate_file(Options *options, const char *path, int userprovided)
464e9778795SPeter Avalos {
465e9778795SPeter Avalos 	int i;
466e9778795SPeter Avalos 
467e9778795SPeter Avalos 	if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
468e9778795SPeter Avalos 		fatal("Too many certificate files specified (max %d)",
469e9778795SPeter Avalos 		    SSH_MAX_CERTIFICATE_FILES);
470e9778795SPeter Avalos 
471e9778795SPeter Avalos 	/* Avoid registering duplicates */
472e9778795SPeter Avalos 	for (i = 0; i < options->num_certificate_files; i++) {
473e9778795SPeter Avalos 		if (options->certificate_file_userprovided[i] == userprovided &&
474e9778795SPeter Avalos 		    strcmp(options->certificate_files[i], path) == 0) {
47550a69bb5SSascha Wildner 			debug2_f("ignoring duplicate key %s", path);
476e9778795SPeter Avalos 			return;
477e9778795SPeter Avalos 		}
478e9778795SPeter Avalos 	}
479e9778795SPeter Avalos 
480e9778795SPeter Avalos 	options->certificate_file_userprovided[options->num_certificate_files] =
481e9778795SPeter Avalos 	    userprovided;
482e9778795SPeter Avalos 	options->certificate_files[options->num_certificate_files++] =
483e9778795SPeter Avalos 	    xstrdup(path);
484e9778795SPeter Avalos }
485e9778795SPeter Avalos 
486e9778795SPeter Avalos void
add_identity_file(Options * options,const char * dir,const char * filename,int userprovided)48736e94dc5SPeter Avalos add_identity_file(Options *options, const char *dir, const char *filename,
48836e94dc5SPeter Avalos     int userprovided)
48936e94dc5SPeter Avalos {
49036e94dc5SPeter Avalos 	char *path;
49136e94dc5SPeter Avalos 	int i;
49236e94dc5SPeter Avalos 
49336e94dc5SPeter Avalos 	if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
49436e94dc5SPeter Avalos 		fatal("Too many identity files specified (max %d)",
49536e94dc5SPeter Avalos 		    SSH_MAX_IDENTITY_FILES);
49636e94dc5SPeter Avalos 
49736e94dc5SPeter Avalos 	if (dir == NULL) /* no dir, filename is absolute */
49836e94dc5SPeter Avalos 		path = xstrdup(filename);
499ce74bacaSMatthew Dillon 	else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX)
500ce74bacaSMatthew Dillon 		fatal("Identity file path %s too long", path);
50136e94dc5SPeter Avalos 
50236e94dc5SPeter Avalos 	/* Avoid registering duplicates */
50336e94dc5SPeter Avalos 	for (i = 0; i < options->num_identity_files; i++) {
50436e94dc5SPeter Avalos 		if (options->identity_file_userprovided[i] == userprovided &&
50536e94dc5SPeter Avalos 		    strcmp(options->identity_files[i], path) == 0) {
50650a69bb5SSascha Wildner 			debug2_f("ignoring duplicate key %s", path);
50736e94dc5SPeter Avalos 			free(path);
50836e94dc5SPeter Avalos 			return;
50936e94dc5SPeter Avalos 		}
51036e94dc5SPeter Avalos 	}
51136e94dc5SPeter Avalos 
51236e94dc5SPeter Avalos 	options->identity_file_userprovided[options->num_identity_files] =
51336e94dc5SPeter Avalos 	    userprovided;
51436e94dc5SPeter Avalos 	options->identity_files[options->num_identity_files++] = path;
51536e94dc5SPeter Avalos }
51636e94dc5SPeter Avalos 
51736e94dc5SPeter Avalos int
default_ssh_port(void)51836e94dc5SPeter Avalos default_ssh_port(void)
51936e94dc5SPeter Avalos {
52036e94dc5SPeter Avalos 	static int port;
52136e94dc5SPeter Avalos 	struct servent *sp;
52236e94dc5SPeter Avalos 
52336e94dc5SPeter Avalos 	if (port == 0) {
52436e94dc5SPeter Avalos 		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
52536e94dc5SPeter Avalos 		port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
52636e94dc5SPeter Avalos 	}
52736e94dc5SPeter Avalos 	return port;
52836e94dc5SPeter Avalos }
52936e94dc5SPeter Avalos 
53036e94dc5SPeter Avalos /*
53136e94dc5SPeter Avalos  * Execute a command in a shell.
53236e94dc5SPeter Avalos  * Return its exit status or -1 on abnormal exit.
53336e94dc5SPeter Avalos  */
53436e94dc5SPeter Avalos static int
execute_in_shell(const char * cmd)53536e94dc5SPeter Avalos execute_in_shell(const char *cmd)
53636e94dc5SPeter Avalos {
537e9778795SPeter Avalos 	char *shell;
53836e94dc5SPeter Avalos 	pid_t pid;
53950a69bb5SSascha Wildner 	int status;
54036e94dc5SPeter Avalos 
54136e94dc5SPeter Avalos 	if ((shell = getenv("SHELL")) == NULL)
54236e94dc5SPeter Avalos 		shell = _PATH_BSHELL;
54336e94dc5SPeter Avalos 
5440cbfa66cSDaniel Fojt 	if (access(shell, X_OK) == -1) {
5450cbfa66cSDaniel Fojt 		fatal("Shell \"%s\" is not executable: %s",
5460cbfa66cSDaniel Fojt 		    shell, strerror(errno));
5470cbfa66cSDaniel Fojt 	}
5480cbfa66cSDaniel Fojt 
54936e94dc5SPeter Avalos 	debug("Executing command: '%.500s'", cmd);
55036e94dc5SPeter Avalos 
55136e94dc5SPeter Avalos 	/* Fork and execute the command. */
55236e94dc5SPeter Avalos 	if ((pid = fork()) == 0) {
55336e94dc5SPeter Avalos 		char *argv[4];
55436e94dc5SPeter Avalos 
55550a69bb5SSascha Wildner 		if (stdfd_devnull(1, 1, 0) == -1)
55650a69bb5SSascha Wildner 			fatal_f("stdfd_devnull failed");
55736e94dc5SPeter Avalos 		closefrom(STDERR_FILENO + 1);
55836e94dc5SPeter Avalos 
55936e94dc5SPeter Avalos 		argv[0] = shell;
56036e94dc5SPeter Avalos 		argv[1] = "-c";
561e9778795SPeter Avalos 		argv[2] = xstrdup(cmd);
56236e94dc5SPeter Avalos 		argv[3] = NULL;
56336e94dc5SPeter Avalos 
56436e94dc5SPeter Avalos 		execv(argv[0], argv);
56536e94dc5SPeter Avalos 		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
56636e94dc5SPeter Avalos 		/* Die with signal to make this error apparent to parent. */
5670cbfa66cSDaniel Fojt 		ssh_signal(SIGTERM, SIG_DFL);
56836e94dc5SPeter Avalos 		kill(getpid(), SIGTERM);
56936e94dc5SPeter Avalos 		_exit(1);
57036e94dc5SPeter Avalos 	}
57136e94dc5SPeter Avalos 	/* Parent. */
5720cbfa66cSDaniel Fojt 	if (pid == -1)
57350a69bb5SSascha Wildner 		fatal_f("fork: %.100s", strerror(errno));
57436e94dc5SPeter Avalos 
57536e94dc5SPeter Avalos 	while (waitpid(pid, &status, 0) == -1) {
57636e94dc5SPeter Avalos 		if (errno != EINTR && errno != EAGAIN)
57750a69bb5SSascha Wildner 			fatal_f("waitpid: %s", strerror(errno));
57836e94dc5SPeter Avalos 	}
57936e94dc5SPeter Avalos 	if (!WIFEXITED(status)) {
58036e94dc5SPeter Avalos 		error("command '%.100s' exited abnormally", cmd);
58136e94dc5SPeter Avalos 		return -1;
58236e94dc5SPeter Avalos 	}
58336e94dc5SPeter Avalos 	debug3("command returned status %d", WEXITSTATUS(status));
58436e94dc5SPeter Avalos 	return WEXITSTATUS(status);
58536e94dc5SPeter Avalos }
58636e94dc5SPeter Avalos 
58736e94dc5SPeter Avalos /*
588*ba1276acSMatthew Dillon  * Check whether a local network interface address appears in CIDR pattern-
589*ba1276acSMatthew Dillon  * list 'addrlist'. Returns 1 if matched or 0 otherwise.
590*ba1276acSMatthew Dillon  */
591*ba1276acSMatthew Dillon static int
check_match_ifaddrs(const char * addrlist)592*ba1276acSMatthew Dillon check_match_ifaddrs(const char *addrlist)
593*ba1276acSMatthew Dillon {
594*ba1276acSMatthew Dillon #ifdef HAVE_IFADDRS_H
595*ba1276acSMatthew Dillon 	struct ifaddrs *ifa, *ifaddrs = NULL;
596*ba1276acSMatthew Dillon 	int r, found = 0;
597*ba1276acSMatthew Dillon 	char addr[NI_MAXHOST];
598*ba1276acSMatthew Dillon 	socklen_t salen;
599*ba1276acSMatthew Dillon 
600*ba1276acSMatthew Dillon 	if (getifaddrs(&ifaddrs) != 0) {
601*ba1276acSMatthew Dillon 		error("match localnetwork: getifaddrs failed: %s",
602*ba1276acSMatthew Dillon 		    strerror(errno));
603*ba1276acSMatthew Dillon 		return 0;
604*ba1276acSMatthew Dillon 	}
605*ba1276acSMatthew Dillon 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
606*ba1276acSMatthew Dillon 		if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
607*ba1276acSMatthew Dillon 		    (ifa->ifa_flags & IFF_UP) == 0)
608*ba1276acSMatthew Dillon 			continue;
609*ba1276acSMatthew Dillon 		switch (ifa->ifa_addr->sa_family) {
610*ba1276acSMatthew Dillon 		case AF_INET:
611*ba1276acSMatthew Dillon 			salen = sizeof(struct sockaddr_in);
612*ba1276acSMatthew Dillon 			break;
613*ba1276acSMatthew Dillon 		case AF_INET6:
614*ba1276acSMatthew Dillon 			salen = sizeof(struct sockaddr_in6);
615*ba1276acSMatthew Dillon 			break;
616*ba1276acSMatthew Dillon #ifdef AF_LINK
617*ba1276acSMatthew Dillon 		case AF_LINK:
618*ba1276acSMatthew Dillon 			/* ignore */
619*ba1276acSMatthew Dillon 			continue;
620*ba1276acSMatthew Dillon #endif /* AF_LINK */
621*ba1276acSMatthew Dillon 		default:
622*ba1276acSMatthew Dillon 			debug2_f("interface %s: unsupported address family %d",
623*ba1276acSMatthew Dillon 			    ifa->ifa_name, ifa->ifa_addr->sa_family);
624*ba1276acSMatthew Dillon 			continue;
625*ba1276acSMatthew Dillon 		}
626*ba1276acSMatthew Dillon 		if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr),
627*ba1276acSMatthew Dillon 		    NULL, 0, NI_NUMERICHOST)) != 0) {
628*ba1276acSMatthew Dillon 			debug2_f("interface %s getnameinfo failed: %s",
629*ba1276acSMatthew Dillon 			    ifa->ifa_name, gai_strerror(r));
630*ba1276acSMatthew Dillon 			continue;
631*ba1276acSMatthew Dillon 		}
632*ba1276acSMatthew Dillon 		debug3_f("interface %s addr %s", ifa->ifa_name, addr);
633*ba1276acSMatthew Dillon 		if (addr_match_cidr_list(addr, addrlist) == 1) {
634*ba1276acSMatthew Dillon 			debug3_f("matched interface %s: address %s in %s",
635*ba1276acSMatthew Dillon 			    ifa->ifa_name, addr, addrlist);
636*ba1276acSMatthew Dillon 			found = 1;
637*ba1276acSMatthew Dillon 			break;
638*ba1276acSMatthew Dillon 		}
639*ba1276acSMatthew Dillon 	}
640*ba1276acSMatthew Dillon 	freeifaddrs(ifaddrs);
641*ba1276acSMatthew Dillon 	return found;
642*ba1276acSMatthew Dillon #else /* HAVE_IFADDRS_H */
643*ba1276acSMatthew Dillon 	error("match localnetwork: not supported on this platform");
644*ba1276acSMatthew Dillon 	return 0;
645*ba1276acSMatthew Dillon #endif /* HAVE_IFADDRS_H */
646*ba1276acSMatthew Dillon }
647*ba1276acSMatthew Dillon 
648*ba1276acSMatthew Dillon /*
64936e94dc5SPeter Avalos  * Parse and execute a Match directive.
65036e94dc5SPeter Avalos  */
65136e94dc5SPeter Avalos static int
match_cfg_line(Options * options,char ** condition,struct passwd * pw,const char * host_arg,const char * original_host,int final_pass,int * want_final_pass,const char * filename,int linenum)65236e94dc5SPeter Avalos match_cfg_line(Options *options, char **condition, struct passwd *pw,
653664f4763Szrj     const char *host_arg, const char *original_host, int final_pass,
654664f4763Szrj     int *want_final_pass, const char *filename, int linenum)
65536e94dc5SPeter Avalos {
656e9778795SPeter Avalos 	char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria;
65736e94dc5SPeter Avalos 	const char *ruser;
658e9778795SPeter Avalos 	int r, port, this_result, result = 1, attributes = 0, negate;
65936e94dc5SPeter Avalos 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
660664f4763Szrj 	char uidstr[32];
66136e94dc5SPeter Avalos 
66236e94dc5SPeter Avalos 	/*
66336e94dc5SPeter Avalos 	 * Configuration is likely to be incomplete at this point so we
66436e94dc5SPeter Avalos 	 * must be prepared to use default values.
66536e94dc5SPeter Avalos 	 */
66636e94dc5SPeter Avalos 	port = options->port <= 0 ? default_ssh_port() : options->port;
66736e94dc5SPeter Avalos 	ruser = options->user == NULL ? pw->pw_name : options->user;
668664f4763Szrj 	if (final_pass) {
669e9778795SPeter Avalos 		host = xstrdup(options->hostname);
670e9778795SPeter Avalos 	} else if (options->hostname != NULL) {
67136e94dc5SPeter Avalos 		/* NB. Please keep in sync with ssh.c:main() */
67236e94dc5SPeter Avalos 		host = percent_expand(options->hostname,
67336e94dc5SPeter Avalos 		    "h", host_arg, (char *)NULL);
674e9778795SPeter Avalos 	} else {
67536e94dc5SPeter Avalos 		host = xstrdup(host_arg);
676e9778795SPeter Avalos 	}
67736e94dc5SPeter Avalos 
678e9778795SPeter Avalos 	debug2("checking match for '%s' host %s originally %s",
679e9778795SPeter Avalos 	    cp, host, original_host);
680e9778795SPeter Avalos 	while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') {
68150a69bb5SSascha Wildner 		/* Terminate on comment */
68250a69bb5SSascha Wildner 		if (*attrib == '#') {
68350a69bb5SSascha Wildner 			cp = NULL; /* mark all arguments consumed */
68450a69bb5SSascha Wildner 			break;
68550a69bb5SSascha Wildner 		}
68650a69bb5SSascha Wildner 		arg = criteria = NULL;
687e9778795SPeter Avalos 		this_result = 1;
688*ba1276acSMatthew Dillon 		if ((negate = (attrib[0] == '!')))
689e9778795SPeter Avalos 			attrib++;
69050a69bb5SSascha Wildner 		/* Criterion "all" has no argument and must appear alone */
69136e94dc5SPeter Avalos 		if (strcasecmp(attrib, "all") == 0) {
69250a69bb5SSascha Wildner 			if (attributes > 1 || ((arg = strdelim(&cp)) != NULL &&
69350a69bb5SSascha Wildner 			    *arg != '\0' && *arg != '#')) {
694e9778795SPeter Avalos 				error("%.200s line %d: '%s' cannot be combined "
695e9778795SPeter Avalos 				    "with other Match attributes",
696e9778795SPeter Avalos 				    filename, linenum, oattrib);
69736e94dc5SPeter Avalos 				result = -1;
69836e94dc5SPeter Avalos 				goto out;
69936e94dc5SPeter Avalos 			}
70050a69bb5SSascha Wildner 			if (arg != NULL && *arg == '#')
70150a69bb5SSascha Wildner 				cp = NULL; /* mark all arguments consumed */
702e9778795SPeter Avalos 			if (result)
703e9778795SPeter Avalos 				result = negate ? 0 : 1;
70436e94dc5SPeter Avalos 			goto out;
70536e94dc5SPeter Avalos 		}
706e9778795SPeter Avalos 		attributes++;
70750a69bb5SSascha Wildner 		/* criteria "final" and "canonical" have no argument */
708664f4763Szrj 		if (strcasecmp(attrib, "canonical") == 0 ||
709664f4763Szrj 		    strcasecmp(attrib, "final") == 0) {
710664f4763Szrj 			/*
711664f4763Szrj 			 * If the config requests "Match final" then remember
712664f4763Szrj 			 * this so we can perform a second pass later.
713664f4763Szrj 			 */
714664f4763Szrj 			if (strcasecmp(attrib, "final") == 0 &&
715664f4763Szrj 			    want_final_pass != NULL)
716664f4763Szrj 				*want_final_pass = 1;
717664f4763Szrj 			r = !!final_pass;  /* force bitmask member to boolean */
718e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
719e9778795SPeter Avalos 				this_result = result = 0;
720e9778795SPeter Avalos 			debug3("%.200s line %d: %smatched '%s'",
721e9778795SPeter Avalos 			    filename, linenum,
722e9778795SPeter Avalos 			    this_result ? "" : "not ", oattrib);
723e9778795SPeter Avalos 			continue;
724e9778795SPeter Avalos 		}
725e9778795SPeter Avalos 		/* All other criteria require an argument */
72650a69bb5SSascha Wildner 		if ((arg = strdelim(&cp)) == NULL ||
72750a69bb5SSascha Wildner 		    *arg == '\0' || *arg == '#') {
72836e94dc5SPeter Avalos 			error("Missing Match criteria for %s", attrib);
72936e94dc5SPeter Avalos 			result = -1;
73036e94dc5SPeter Avalos 			goto out;
73136e94dc5SPeter Avalos 		}
73236e94dc5SPeter Avalos 		if (strcasecmp(attrib, "host") == 0) {
733e9778795SPeter Avalos 			criteria = xstrdup(host);
734e9778795SPeter Avalos 			r = match_hostname(host, arg) == 1;
735e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
736e9778795SPeter Avalos 				this_result = result = 0;
73736e94dc5SPeter Avalos 		} else if (strcasecmp(attrib, "originalhost") == 0) {
738e9778795SPeter Avalos 			criteria = xstrdup(original_host);
739e9778795SPeter Avalos 			r = match_hostname(original_host, arg) == 1;
740e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
741e9778795SPeter Avalos 				this_result = result = 0;
74236e94dc5SPeter Avalos 		} else if (strcasecmp(attrib, "user") == 0) {
743e9778795SPeter Avalos 			criteria = xstrdup(ruser);
744e9778795SPeter Avalos 			r = match_pattern_list(ruser, arg, 0) == 1;
745e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
746e9778795SPeter Avalos 				this_result = result = 0;
74736e94dc5SPeter Avalos 		} else if (strcasecmp(attrib, "localuser") == 0) {
748e9778795SPeter Avalos 			criteria = xstrdup(pw->pw_name);
749e9778795SPeter Avalos 			r = match_pattern_list(pw->pw_name, arg, 0) == 1;
750e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
751e9778795SPeter Avalos 				this_result = result = 0;
752*ba1276acSMatthew Dillon 		} else if (strcasecmp(attrib, "localnetwork") == 0) {
753*ba1276acSMatthew Dillon 			if (addr_match_cidr_list(NULL, arg) == -1) {
754*ba1276acSMatthew Dillon 				/* Error already printed */
755*ba1276acSMatthew Dillon 				result = -1;
756*ba1276acSMatthew Dillon 				goto out;
757*ba1276acSMatthew Dillon 			}
758*ba1276acSMatthew Dillon 			r = check_match_ifaddrs(arg) == 1;
759*ba1276acSMatthew Dillon 			if (r == (negate ? 1 : 0))
760*ba1276acSMatthew Dillon 				this_result = result = 0;
761*ba1276acSMatthew Dillon 		} else if (strcasecmp(attrib, "tagged") == 0) {
762*ba1276acSMatthew Dillon 			criteria = xstrdup(options->tag == NULL ? "" :
763*ba1276acSMatthew Dillon 			    options->tag);
764*ba1276acSMatthew Dillon 			r = match_pattern_list(criteria, arg, 0) == 1;
765*ba1276acSMatthew Dillon 			if (r == (negate ? 1 : 0))
766*ba1276acSMatthew Dillon 				this_result = result = 0;
76736e94dc5SPeter Avalos 		} else if (strcasecmp(attrib, "exec") == 0) {
768*ba1276acSMatthew Dillon 			char *conn_hash_hex, *keyalias, *jmphost;
7690cbfa66cSDaniel Fojt 
77036e94dc5SPeter Avalos 			if (gethostname(thishost, sizeof(thishost)) == -1)
77136e94dc5SPeter Avalos 				fatal("gethostname: %s", strerror(errno));
772*ba1276acSMatthew Dillon 			jmphost = option_clear_or_none(options->jump_host) ?
773*ba1276acSMatthew Dillon 			    "" : options->jump_host;
77436e94dc5SPeter Avalos 			strlcpy(shorthost, thishost, sizeof(shorthost));
77536e94dc5SPeter Avalos 			shorthost[strcspn(thishost, ".")] = '\0';
77636e94dc5SPeter Avalos 			snprintf(portstr, sizeof(portstr), "%d", port);
777664f4763Szrj 			snprintf(uidstr, sizeof(uidstr), "%llu",
778664f4763Szrj 			    (unsigned long long)pw->pw_uid);
7790cbfa66cSDaniel Fojt 			conn_hash_hex = ssh_connection_hash(thishost, host,
780*ba1276acSMatthew Dillon 			    portstr, ruser, jmphost);
78150a69bb5SSascha Wildner 			keyalias = options->host_key_alias ?
78250a69bb5SSascha Wildner 			    options->host_key_alias : host;
78336e94dc5SPeter Avalos 
78436e94dc5SPeter Avalos 			cmd = percent_expand(arg,
7850cbfa66cSDaniel Fojt 			    "C", conn_hash_hex,
78636e94dc5SPeter Avalos 			    "L", shorthost,
78736e94dc5SPeter Avalos 			    "d", pw->pw_dir,
78836e94dc5SPeter Avalos 			    "h", host,
78950a69bb5SSascha Wildner 			    "k", keyalias,
79036e94dc5SPeter Avalos 			    "l", thishost,
791e9778795SPeter Avalos 			    "n", original_host,
79236e94dc5SPeter Avalos 			    "p", portstr,
79336e94dc5SPeter Avalos 			    "r", ruser,
79436e94dc5SPeter Avalos 			    "u", pw->pw_name,
795664f4763Szrj 			    "i", uidstr,
796*ba1276acSMatthew Dillon 			    "j", jmphost,
79736e94dc5SPeter Avalos 			    (char *)NULL);
7980cbfa66cSDaniel Fojt 			free(conn_hash_hex);
79936e94dc5SPeter Avalos 			if (result != 1) {
80036e94dc5SPeter Avalos 				/* skip execution if prior predicate failed */
801e9778795SPeter Avalos 				debug3("%.200s line %d: skipped exec "
802e9778795SPeter Avalos 				    "\"%.100s\"", filename, linenum, cmd);
803e9778795SPeter Avalos 				free(cmd);
804e9778795SPeter Avalos 				continue;
805e9778795SPeter Avalos 			}
80636e94dc5SPeter Avalos 			r = execute_in_shell(cmd);
80736e94dc5SPeter Avalos 			if (r == -1) {
80836e94dc5SPeter Avalos 				fatal("%.200s line %d: match exec "
80936e94dc5SPeter Avalos 				    "'%.100s' error", filename,
81036e94dc5SPeter Avalos 				    linenum, cmd);
81136e94dc5SPeter Avalos 			}
812e9778795SPeter Avalos 			criteria = xstrdup(cmd);
81336e94dc5SPeter Avalos 			free(cmd);
814e9778795SPeter Avalos 			/* Force exit status to boolean */
815e9778795SPeter Avalos 			r = r == 0;
816e9778795SPeter Avalos 			if (r == (negate ? 1 : 0))
817e9778795SPeter Avalos 				this_result = result = 0;
81836e94dc5SPeter Avalos 		} else {
81936e94dc5SPeter Avalos 			error("Unsupported Match attribute %s", attrib);
82036e94dc5SPeter Avalos 			result = -1;
82136e94dc5SPeter Avalos 			goto out;
82236e94dc5SPeter Avalos 		}
823*ba1276acSMatthew Dillon 		debug3("%.200s line %d: %smatched '%s%s%.100s%s' ",
824*ba1276acSMatthew Dillon 		    filename, linenum, this_result ? "": "not ", oattrib,
825*ba1276acSMatthew Dillon 		    criteria == NULL ? "" : " \"",
826*ba1276acSMatthew Dillon 		    criteria == NULL ? "" : criteria,
827*ba1276acSMatthew Dillon 		    criteria == NULL ? "" : "\"");
828e9778795SPeter Avalos 		free(criteria);
82936e94dc5SPeter Avalos 	}
83036e94dc5SPeter Avalos 	if (attributes == 0) {
83136e94dc5SPeter Avalos 		error("One or more attributes required for Match");
83236e94dc5SPeter Avalos 		result = -1;
83336e94dc5SPeter Avalos 		goto out;
83436e94dc5SPeter Avalos 	}
83536e94dc5SPeter Avalos  out:
836e9778795SPeter Avalos 	if (result != -1)
837e9778795SPeter Avalos 		debug2("match %sfound", result ? "" : "not ");
838e9778795SPeter Avalos 	*condition = cp;
83936e94dc5SPeter Avalos 	free(host);
84036e94dc5SPeter Avalos 	return result;
84136e94dc5SPeter Avalos }
84236e94dc5SPeter Avalos 
843664f4763Szrj /* Remove environment variable by pattern */
84436e94dc5SPeter Avalos static void
rm_env(Options * options,const char * arg,const char * filename,int linenum)845664f4763Szrj rm_env(Options *options, const char *arg, const char *filename, int linenum)
84636e94dc5SPeter Avalos {
847ee116499SAntonio Huete Jimenez 	u_int i, j, onum_send_env = options->num_send_env;
84836e94dc5SPeter Avalos 
849664f4763Szrj 	/* Remove an environment variable */
850664f4763Szrj 	for (i = 0; i < options->num_send_env; ) {
851ee116499SAntonio Huete Jimenez 		if (!match_pattern(options->send_env[i], arg + 1)) {
852664f4763Szrj 			i++;
853664f4763Szrj 			continue;
85436e94dc5SPeter Avalos 		}
855664f4763Szrj 		debug3("%s line %d: removing environment %s",
856ee116499SAntonio Huete Jimenez 		    filename, linenum, options->send_env[i]);
857664f4763Szrj 		free(options->send_env[i]);
858664f4763Szrj 		options->send_env[i] = NULL;
859664f4763Szrj 		for (j = i; j < options->num_send_env - 1; j++) {
860664f4763Szrj 			options->send_env[j] = options->send_env[j + 1];
861664f4763Szrj 			options->send_env[j + 1] = NULL;
862664f4763Szrj 		}
863664f4763Szrj 		options->num_send_env--;
864664f4763Szrj 		/* NB. don't increment i */
865664f4763Szrj 	}
86650a69bb5SSascha Wildner 	if (onum_send_env != options->num_send_env) {
86750a69bb5SSascha Wildner 		options->send_env = xrecallocarray(options->send_env,
86850a69bb5SSascha Wildner 		    onum_send_env, options->num_send_env,
86950a69bb5SSascha Wildner 		    sizeof(*options->send_env));
87050a69bb5SSascha Wildner 	}
87136e94dc5SPeter Avalos }
87236e94dc5SPeter Avalos 
87318de8d7fSPeter Avalos /*
87418de8d7fSPeter Avalos  * Returns the number of the token pointed to by cp or oBadOption.
87518de8d7fSPeter Avalos  */
87618de8d7fSPeter Avalos static OpCodes
parse_token(const char * cp,const char * filename,int linenum,const char * ignored_unknown)87736e94dc5SPeter Avalos parse_token(const char *cp, const char *filename, int linenum,
87836e94dc5SPeter Avalos     const char *ignored_unknown)
87918de8d7fSPeter Avalos {
88036e94dc5SPeter Avalos 	int i;
88118de8d7fSPeter Avalos 
88218de8d7fSPeter Avalos 	for (i = 0; keywords[i].name; i++)
88336e94dc5SPeter Avalos 		if (strcmp(cp, keywords[i].name) == 0)
88418de8d7fSPeter Avalos 			return keywords[i].opcode;
885e9778795SPeter Avalos 	if (ignored_unknown != NULL &&
886e9778795SPeter Avalos 	    match_pattern_list(cp, ignored_unknown, 1) == 1)
88736e94dc5SPeter Avalos 		return oIgnoredUnknownOption;
88818de8d7fSPeter Avalos 	error("%s: line %d: Bad configuration option: %s",
88918de8d7fSPeter Avalos 	    filename, linenum, cp);
89018de8d7fSPeter Avalos 	return oBadOption;
89118de8d7fSPeter Avalos }
89218de8d7fSPeter Avalos 
893*ba1276acSMatthew Dillon static void
free_canon_cnames(struct allowed_cname * cnames,u_int n)894*ba1276acSMatthew Dillon free_canon_cnames(struct allowed_cname *cnames, u_int n)
895*ba1276acSMatthew Dillon {
896*ba1276acSMatthew Dillon 	u_int i;
897*ba1276acSMatthew Dillon 
898*ba1276acSMatthew Dillon 	if (cnames == NULL || n == 0)
899*ba1276acSMatthew Dillon 		return;
900*ba1276acSMatthew Dillon 	for (i = 0; i < n; i++) {
901*ba1276acSMatthew Dillon 		free(cnames[i].source_list);
902*ba1276acSMatthew Dillon 		free(cnames[i].target_list);
903*ba1276acSMatthew Dillon 	}
904*ba1276acSMatthew Dillon 	free(cnames);
905*ba1276acSMatthew Dillon }
906*ba1276acSMatthew Dillon 
90736e94dc5SPeter Avalos /* Multistate option parsing */
90836e94dc5SPeter Avalos struct multistate {
90936e94dc5SPeter Avalos 	char *key;
91036e94dc5SPeter Avalos 	int value;
91136e94dc5SPeter Avalos };
91236e94dc5SPeter Avalos static const struct multistate multistate_flag[] = {
91336e94dc5SPeter Avalos 	{ "true",			1 },
91436e94dc5SPeter Avalos 	{ "false",			0 },
91536e94dc5SPeter Avalos 	{ "yes",			1 },
91636e94dc5SPeter Avalos 	{ "no",				0 },
91736e94dc5SPeter Avalos 	{ NULL, -1 }
91836e94dc5SPeter Avalos };
91936e94dc5SPeter Avalos static const struct multistate multistate_yesnoask[] = {
92036e94dc5SPeter Avalos 	{ "true",			1 },
92136e94dc5SPeter Avalos 	{ "false",			0 },
92236e94dc5SPeter Avalos 	{ "yes",			1 },
92336e94dc5SPeter Avalos 	{ "no",				0 },
92436e94dc5SPeter Avalos 	{ "ask",			2 },
92536e94dc5SPeter Avalos 	{ NULL, -1 }
92636e94dc5SPeter Avalos };
927ce74bacaSMatthew Dillon static const struct multistate multistate_strict_hostkey[] = {
928ce74bacaSMatthew Dillon 	{ "true",			SSH_STRICT_HOSTKEY_YES },
929ce74bacaSMatthew Dillon 	{ "false",			SSH_STRICT_HOSTKEY_OFF },
930ce74bacaSMatthew Dillon 	{ "yes",			SSH_STRICT_HOSTKEY_YES },
931ce74bacaSMatthew Dillon 	{ "no",				SSH_STRICT_HOSTKEY_OFF },
932ce74bacaSMatthew Dillon 	{ "ask",			SSH_STRICT_HOSTKEY_ASK },
933ce74bacaSMatthew Dillon 	{ "off",			SSH_STRICT_HOSTKEY_OFF },
934ce74bacaSMatthew Dillon 	{ "accept-new",			SSH_STRICT_HOSTKEY_NEW },
935ce74bacaSMatthew Dillon 	{ NULL, -1 }
936ce74bacaSMatthew Dillon };
937e9778795SPeter Avalos static const struct multistate multistate_yesnoaskconfirm[] = {
938e9778795SPeter Avalos 	{ "true",			1 },
939e9778795SPeter Avalos 	{ "false",			0 },
940e9778795SPeter Avalos 	{ "yes",			1 },
941e9778795SPeter Avalos 	{ "no",				0 },
942e9778795SPeter Avalos 	{ "ask",			2 },
943e9778795SPeter Avalos 	{ "confirm",			3 },
944e9778795SPeter Avalos 	{ NULL, -1 }
945e9778795SPeter Avalos };
94636e94dc5SPeter Avalos static const struct multistate multistate_addressfamily[] = {
94736e94dc5SPeter Avalos 	{ "inet",			AF_INET },
94836e94dc5SPeter Avalos 	{ "inet6",			AF_INET6 },
94936e94dc5SPeter Avalos 	{ "any",			AF_UNSPEC },
95036e94dc5SPeter Avalos 	{ NULL, -1 }
95136e94dc5SPeter Avalos };
95236e94dc5SPeter Avalos static const struct multistate multistate_controlmaster[] = {
95336e94dc5SPeter Avalos 	{ "true",			SSHCTL_MASTER_YES },
95436e94dc5SPeter Avalos 	{ "yes",			SSHCTL_MASTER_YES },
95536e94dc5SPeter Avalos 	{ "false",			SSHCTL_MASTER_NO },
95636e94dc5SPeter Avalos 	{ "no",				SSHCTL_MASTER_NO },
95736e94dc5SPeter Avalos 	{ "auto",			SSHCTL_MASTER_AUTO },
95836e94dc5SPeter Avalos 	{ "ask",			SSHCTL_MASTER_ASK },
95936e94dc5SPeter Avalos 	{ "autoask",			SSHCTL_MASTER_AUTO_ASK },
96036e94dc5SPeter Avalos 	{ NULL, -1 }
96136e94dc5SPeter Avalos };
96236e94dc5SPeter Avalos static const struct multistate multistate_tunnel[] = {
96336e94dc5SPeter Avalos 	{ "ethernet",			SSH_TUNMODE_ETHERNET },
96436e94dc5SPeter Avalos 	{ "point-to-point",		SSH_TUNMODE_POINTOPOINT },
96536e94dc5SPeter Avalos 	{ "true",			SSH_TUNMODE_DEFAULT },
96636e94dc5SPeter Avalos 	{ "yes",			SSH_TUNMODE_DEFAULT },
96736e94dc5SPeter Avalos 	{ "false",			SSH_TUNMODE_NO },
96836e94dc5SPeter Avalos 	{ "no",				SSH_TUNMODE_NO },
96936e94dc5SPeter Avalos 	{ NULL, -1 }
97036e94dc5SPeter Avalos };
97136e94dc5SPeter Avalos static const struct multistate multistate_requesttty[] = {
97236e94dc5SPeter Avalos 	{ "true",			REQUEST_TTY_YES },
97336e94dc5SPeter Avalos 	{ "yes",			REQUEST_TTY_YES },
97436e94dc5SPeter Avalos 	{ "false",			REQUEST_TTY_NO },
97536e94dc5SPeter Avalos 	{ "no",				REQUEST_TTY_NO },
97636e94dc5SPeter Avalos 	{ "force",			REQUEST_TTY_FORCE },
97736e94dc5SPeter Avalos 	{ "auto",			REQUEST_TTY_AUTO },
97836e94dc5SPeter Avalos 	{ NULL, -1 }
97936e94dc5SPeter Avalos };
98050a69bb5SSascha Wildner static const struct multistate multistate_sessiontype[] = {
98150a69bb5SSascha Wildner 	{ "none",			SESSION_TYPE_NONE },
98250a69bb5SSascha Wildner 	{ "subsystem",			SESSION_TYPE_SUBSYSTEM },
98350a69bb5SSascha Wildner 	{ "default",			SESSION_TYPE_DEFAULT },
98450a69bb5SSascha Wildner 	{ NULL, -1 }
98550a69bb5SSascha Wildner };
98636e94dc5SPeter Avalos static const struct multistate multistate_canonicalizehostname[] = {
98736e94dc5SPeter Avalos 	{ "true",			SSH_CANONICALISE_YES },
98836e94dc5SPeter Avalos 	{ "false",			SSH_CANONICALISE_NO },
98936e94dc5SPeter Avalos 	{ "yes",			SSH_CANONICALISE_YES },
99036e94dc5SPeter Avalos 	{ "no",				SSH_CANONICALISE_NO },
99136e94dc5SPeter Avalos 	{ "always",			SSH_CANONICALISE_ALWAYS },
99236e94dc5SPeter Avalos 	{ NULL, -1 }
99336e94dc5SPeter Avalos };
994ee116499SAntonio Huete Jimenez static const struct multistate multistate_pubkey_auth[] = {
995ee116499SAntonio Huete Jimenez 	{ "true",			SSH_PUBKEY_AUTH_ALL },
996ee116499SAntonio Huete Jimenez 	{ "false",			SSH_PUBKEY_AUTH_NO },
997ee116499SAntonio Huete Jimenez 	{ "yes",			SSH_PUBKEY_AUTH_ALL },
998ee116499SAntonio Huete Jimenez 	{ "no",				SSH_PUBKEY_AUTH_NO },
999ee116499SAntonio Huete Jimenez 	{ "unbound",			SSH_PUBKEY_AUTH_UNBOUND },
1000ee116499SAntonio Huete Jimenez 	{ "host-bound",			SSH_PUBKEY_AUTH_HBOUND },
1001ee116499SAntonio Huete Jimenez 	{ NULL, -1 }
1002ee116499SAntonio Huete Jimenez };
10030cbfa66cSDaniel Fojt static const struct multistate multistate_compression[] = {
10040cbfa66cSDaniel Fojt #ifdef WITH_ZLIB
10050cbfa66cSDaniel Fojt 	{ "yes",			COMP_ZLIB },
10060cbfa66cSDaniel Fojt #endif
10070cbfa66cSDaniel Fojt 	{ "no",				COMP_NONE },
10080cbfa66cSDaniel Fojt 	{ NULL, -1 }
10090cbfa66cSDaniel Fojt };
101036e94dc5SPeter Avalos 
101150a69bb5SSascha Wildner static int
parse_multistate_value(const char * arg,const char * filename,int linenum,const struct multistate * multistate_ptr)101250a69bb5SSascha Wildner parse_multistate_value(const char *arg, const char *filename, int linenum,
101350a69bb5SSascha Wildner     const struct multistate *multistate_ptr)
101450a69bb5SSascha Wildner {
101550a69bb5SSascha Wildner 	int i;
101650a69bb5SSascha Wildner 
101750a69bb5SSascha Wildner 	if (!arg || *arg == '\0') {
101850a69bb5SSascha Wildner 		error("%s line %d: missing argument.", filename, linenum);
101950a69bb5SSascha Wildner 		return -1;
102050a69bb5SSascha Wildner 	}
102150a69bb5SSascha Wildner 	for (i = 0; multistate_ptr[i].key != NULL; i++) {
102250a69bb5SSascha Wildner 		if (strcasecmp(arg, multistate_ptr[i].key) == 0)
102350a69bb5SSascha Wildner 			return multistate_ptr[i].value;
102450a69bb5SSascha Wildner 	}
102550a69bb5SSascha Wildner 	return -1;
102650a69bb5SSascha Wildner }
102750a69bb5SSascha Wildner 
102818de8d7fSPeter Avalos /*
102918de8d7fSPeter Avalos  * Processes a single option line as used in the configuration files. This
103018de8d7fSPeter Avalos  * only sets those values that have not already been set.
103118de8d7fSPeter Avalos  */
103218de8d7fSPeter Avalos int
process_config_line(Options * options,struct passwd * pw,const char * host,const char * original_host,char * line,const char * filename,int linenum,int * activep,int flags)103336e94dc5SPeter Avalos process_config_line(Options *options, struct passwd *pw, const char *host,
1034e9778795SPeter Avalos     const char *original_host, char *line, const char *filename,
1035e9778795SPeter Avalos     int linenum, int *activep, int flags)
1036e9778795SPeter Avalos {
1037e9778795SPeter Avalos 	return process_config_line_depth(options, pw, host, original_host,
1038664f4763Szrj 	    line, filename, linenum, activep, flags, NULL, 0);
1039e9778795SPeter Avalos }
1040e9778795SPeter Avalos 
1041e9778795SPeter Avalos #define WHITESPACE " \t\r\n"
1042e9778795SPeter Avalos static int
process_config_line_depth(Options * options,struct passwd * pw,const char * host,const char * original_host,char * line,const char * filename,int linenum,int * activep,int flags,int * want_final_pass,int depth)1043e9778795SPeter Avalos process_config_line_depth(Options *options, struct passwd *pw, const char *host,
1044e9778795SPeter Avalos     const char *original_host, char *line, const char *filename,
1045664f4763Szrj     int linenum, int *activep, int flags, int *want_final_pass, int depth)
104618de8d7fSPeter Avalos {
1047ee116499SAntonio Huete Jimenez 	char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
104850a69bb5SSascha Wildner 	char **cpptr, ***cppptr, fwdarg[256];
1049*ba1276acSMatthew Dillon 	u_int i, *uintptr, max_entries = 0;
1050e9778795SPeter Avalos 	int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
1051*ba1276acSMatthew Dillon 	int remotefwd, dynamicfwd, ca_only = 0, found = 0;
105218de8d7fSPeter Avalos 	LogLevel *log_level_ptr;
1053ce74bacaSMatthew Dillon 	SyslogFacility *log_facility_ptr;
105436e94dc5SPeter Avalos 	long long val64;
105518de8d7fSPeter Avalos 	size_t len;
105636e94dc5SPeter Avalos 	struct Forward fwd;
105736e94dc5SPeter Avalos 	const struct multistate *multistate_ptr;
1058e9778795SPeter Avalos 	glob_t gl;
1059664f4763Szrj 	const char *errstr;
106050a69bb5SSascha Wildner 	char **oav = NULL, **av;
106150a69bb5SSascha Wildner 	int oac = 0, ac;
106250a69bb5SSascha Wildner 	int ret = -1;
1063*ba1276acSMatthew Dillon 	struct allowed_cname *cnames = NULL;
1064*ba1276acSMatthew Dillon 	u_int ncnames = 0;
1065*ba1276acSMatthew Dillon 	char **strs = NULL; /* string array arguments; freed implicitly */
1066*ba1276acSMatthew Dillon 	u_int nstrs = 0;
106736e94dc5SPeter Avalos 
106836e94dc5SPeter Avalos 	if (activep == NULL) { /* We are processing a command line directive */
106936e94dc5SPeter Avalos 		cmdline = 1;
107036e94dc5SPeter Avalos 		activep = &cmdline;
107136e94dc5SPeter Avalos 	}
107218de8d7fSPeter Avalos 
1073ce74bacaSMatthew Dillon 	/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1074e9778795SPeter Avalos 	if ((len = strlen(line)) == 0)
1075e9778795SPeter Avalos 		return 0;
1076e9778795SPeter Avalos 	for (len--; len > 0; len--) {
1077ce74bacaSMatthew Dillon 		if (strchr(WHITESPACE "\f", line[len]) == NULL)
107818de8d7fSPeter Avalos 			break;
107918de8d7fSPeter Avalos 		line[len] = '\0';
108018de8d7fSPeter Avalos 	}
108118de8d7fSPeter Avalos 
108250a69bb5SSascha Wildner 	str = line;
108318de8d7fSPeter Avalos 	/* Get the keyword. (Each line is supposed to begin with a keyword). */
108450a69bb5SSascha Wildner 	if ((keyword = strdelim(&str)) == NULL)
108518de8d7fSPeter Avalos 		return 0;
108618de8d7fSPeter Avalos 	/* Ignore leading whitespace. */
108718de8d7fSPeter Avalos 	if (*keyword == '\0')
108850a69bb5SSascha Wildner 		keyword = strdelim(&str);
108918de8d7fSPeter Avalos 	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
109018de8d7fSPeter Avalos 		return 0;
109136e94dc5SPeter Avalos 	/* Match lowercase keyword */
109236e94dc5SPeter Avalos 	lowercase(keyword);
109318de8d7fSPeter Avalos 
109450a69bb5SSascha Wildner 	/* Prepare to parse remainder of line */
109550a69bb5SSascha Wildner 	if (str != NULL)
109650a69bb5SSascha Wildner 		str += strspn(str, WHITESPACE);
109750a69bb5SSascha Wildner 	if (str == NULL || *str == '\0') {
109850a69bb5SSascha Wildner 		error("%s line %d: no argument after keyword \"%s\"",
109950a69bb5SSascha Wildner 		    filename, linenum, keyword);
110050a69bb5SSascha Wildner 		return -1;
110150a69bb5SSascha Wildner 	}
110236e94dc5SPeter Avalos 	opcode = parse_token(keyword, filename, linenum,
110336e94dc5SPeter Avalos 	    options->ignored_unknown);
110450a69bb5SSascha Wildner 	if (argv_split(str, &oac, &oav, 1) != 0) {
110550a69bb5SSascha Wildner 		error("%s line %d: invalid quotes", filename, linenum);
110650a69bb5SSascha Wildner 		return -1;
110750a69bb5SSascha Wildner 	}
110850a69bb5SSascha Wildner 	ac = oac;
110950a69bb5SSascha Wildner 	av = oav;
111018de8d7fSPeter Avalos 
111118de8d7fSPeter Avalos 	switch (opcode) {
111218de8d7fSPeter Avalos 	case oBadOption:
111318de8d7fSPeter Avalos 		/* don't panic, but count bad options */
111450a69bb5SSascha Wildner 		goto out;
1115ce74bacaSMatthew Dillon 	case oIgnore:
111650a69bb5SSascha Wildner 		argv_consume(&ac);
111750a69bb5SSascha Wildner 		break;
111836e94dc5SPeter Avalos 	case oIgnoredUnknownOption:
111936e94dc5SPeter Avalos 		debug("%s line %d: Ignored unknown option \"%s\"",
112036e94dc5SPeter Avalos 		    filename, linenum, keyword);
112150a69bb5SSascha Wildner 		argv_consume(&ac);
112250a69bb5SSascha Wildner 		break;
112318de8d7fSPeter Avalos 	case oConnectTimeout:
112418de8d7fSPeter Avalos 		intptr = &options->connection_timeout;
112518de8d7fSPeter Avalos parse_time:
112650a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
112750a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
112850a69bb5SSascha Wildner 			error("%s line %d: missing time value.",
112918de8d7fSPeter Avalos 			    filename, linenum);
113050a69bb5SSascha Wildner 			goto out;
113150a69bb5SSascha Wildner 		}
1132e9778795SPeter Avalos 		if (strcmp(arg, "none") == 0)
1133e9778795SPeter Avalos 			value = -1;
113450a69bb5SSascha Wildner 		else if ((value = convtime(arg)) == -1) {
113550a69bb5SSascha Wildner 			error("%s line %d: invalid time value.",
113618de8d7fSPeter Avalos 			    filename, linenum);
113750a69bb5SSascha Wildner 			goto out;
113850a69bb5SSascha Wildner 		}
113918de8d7fSPeter Avalos 		if (*activep && *intptr == -1)
114018de8d7fSPeter Avalos 			*intptr = value;
114118de8d7fSPeter Avalos 		break;
114218de8d7fSPeter Avalos 
114318de8d7fSPeter Avalos 	case oForwardAgent:
114418de8d7fSPeter Avalos 		intptr = &options->forward_agent;
11450cbfa66cSDaniel Fojt 
114650a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
114750a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
114850a69bb5SSascha Wildner 			error("%s line %d: missing argument.",
11490cbfa66cSDaniel Fojt 			    filename, linenum);
115050a69bb5SSascha Wildner 			goto out;
115150a69bb5SSascha Wildner 		}
11520cbfa66cSDaniel Fojt 
11530cbfa66cSDaniel Fojt 		value = -1;
11540cbfa66cSDaniel Fojt 		multistate_ptr = multistate_flag;
11550cbfa66cSDaniel Fojt 		for (i = 0; multistate_ptr[i].key != NULL; i++) {
11560cbfa66cSDaniel Fojt 			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
11570cbfa66cSDaniel Fojt 				value = multistate_ptr[i].value;
11580cbfa66cSDaniel Fojt 				break;
11590cbfa66cSDaniel Fojt 			}
11600cbfa66cSDaniel Fojt 		}
11610cbfa66cSDaniel Fojt 		if (value != -1) {
11620cbfa66cSDaniel Fojt 			if (*activep && *intptr == -1)
11630cbfa66cSDaniel Fojt 				*intptr = value;
11640cbfa66cSDaniel Fojt 			break;
11650cbfa66cSDaniel Fojt 		}
11660cbfa66cSDaniel Fojt 		/* ForwardAgent wasn't 'yes' or 'no', assume a path */
11670cbfa66cSDaniel Fojt 		if (*activep && *intptr == -1)
11680cbfa66cSDaniel Fojt 			*intptr = 1;
11690cbfa66cSDaniel Fojt 
11700cbfa66cSDaniel Fojt 		charptr = &options->forward_agent_sock_path;
11710cbfa66cSDaniel Fojt 		goto parse_agent_path;
11720cbfa66cSDaniel Fojt 
11730cbfa66cSDaniel Fojt 	case oForwardX11:
11740cbfa66cSDaniel Fojt 		intptr = &options->forward_x11;
117518de8d7fSPeter Avalos  parse_flag:
117636e94dc5SPeter Avalos 		multistate_ptr = multistate_flag;
117736e94dc5SPeter Avalos  parse_multistate:
117850a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
117950a69bb5SSascha Wildner 		if ((value = parse_multistate_value(arg, filename, linenum,
118050a69bb5SSascha Wildner 		    multistate_ptr)) == -1) {
118150a69bb5SSascha Wildner 			error("%s line %d: unsupported option \"%s\".",
118236e94dc5SPeter Avalos 			    filename, linenum, arg);
118350a69bb5SSascha Wildner 			goto out;
118450a69bb5SSascha Wildner 		}
118518de8d7fSPeter Avalos 		if (*activep && *intptr == -1)
118618de8d7fSPeter Avalos 			*intptr = value;
118718de8d7fSPeter Avalos 		break;
118818de8d7fSPeter Avalos 
118918de8d7fSPeter Avalos 	case oForwardX11Trusted:
119018de8d7fSPeter Avalos 		intptr = &options->forward_x11_trusted;
119118de8d7fSPeter Avalos 		goto parse_flag;
119218de8d7fSPeter Avalos 
1193856ea928SPeter Avalos 	case oForwardX11Timeout:
1194856ea928SPeter Avalos 		intptr = &options->forward_x11_timeout;
1195856ea928SPeter Avalos 		goto parse_time;
1196856ea928SPeter Avalos 
119718de8d7fSPeter Avalos 	case oGatewayPorts:
119836e94dc5SPeter Avalos 		intptr = &options->fwd_opts.gateway_ports;
119918de8d7fSPeter Avalos 		goto parse_flag;
120018de8d7fSPeter Avalos 
120118de8d7fSPeter Avalos 	case oExitOnForwardFailure:
120218de8d7fSPeter Avalos 		intptr = &options->exit_on_forward_failure;
120318de8d7fSPeter Avalos 		goto parse_flag;
120418de8d7fSPeter Avalos 
120518de8d7fSPeter Avalos 	case oPasswordAuthentication:
120618de8d7fSPeter Avalos 		intptr = &options->password_authentication;
120718de8d7fSPeter Avalos 		goto parse_flag;
120818de8d7fSPeter Avalos 
120918de8d7fSPeter Avalos 	case oKbdInteractiveAuthentication:
121018de8d7fSPeter Avalos 		intptr = &options->kbd_interactive_authentication;
121118de8d7fSPeter Avalos 		goto parse_flag;
121218de8d7fSPeter Avalos 
121318de8d7fSPeter Avalos 	case oKbdInteractiveDevices:
121418de8d7fSPeter Avalos 		charptr = &options->kbd_interactive_devices;
121518de8d7fSPeter Avalos 		goto parse_string;
121618de8d7fSPeter Avalos 
121718de8d7fSPeter Avalos 	case oPubkeyAuthentication:
1218ee116499SAntonio Huete Jimenez 		multistate_ptr = multistate_pubkey_auth;
121918de8d7fSPeter Avalos 		intptr = &options->pubkey_authentication;
1220ee116499SAntonio Huete Jimenez 		goto parse_multistate;
122118de8d7fSPeter Avalos 
122218de8d7fSPeter Avalos 	case oHostbasedAuthentication:
122318de8d7fSPeter Avalos 		intptr = &options->hostbased_authentication;
122418de8d7fSPeter Avalos 		goto parse_flag;
122518de8d7fSPeter Avalos 
122618de8d7fSPeter Avalos 	case oGssAuthentication:
122718de8d7fSPeter Avalos 		intptr = &options->gss_authentication;
122818de8d7fSPeter Avalos 		goto parse_flag;
122918de8d7fSPeter Avalos 
123018de8d7fSPeter Avalos 	case oGssDelegateCreds:
123118de8d7fSPeter Avalos 		intptr = &options->gss_deleg_creds;
123218de8d7fSPeter Avalos 		goto parse_flag;
123318de8d7fSPeter Avalos 
123418de8d7fSPeter Avalos 	case oBatchMode:
123518de8d7fSPeter Avalos 		intptr = &options->batch_mode;
123618de8d7fSPeter Avalos 		goto parse_flag;
123718de8d7fSPeter Avalos 
123818de8d7fSPeter Avalos 	case oCheckHostIP:
123918de8d7fSPeter Avalos 		intptr = &options->check_host_ip;
124018de8d7fSPeter Avalos 		goto parse_flag;
124118de8d7fSPeter Avalos 
124218de8d7fSPeter Avalos 	case oVerifyHostKeyDNS:
124318de8d7fSPeter Avalos 		intptr = &options->verify_host_key_dns;
124436e94dc5SPeter Avalos 		multistate_ptr = multistate_yesnoask;
124536e94dc5SPeter Avalos 		goto parse_multistate;
124618de8d7fSPeter Avalos 
124718de8d7fSPeter Avalos 	case oStrictHostKeyChecking:
124818de8d7fSPeter Avalos 		intptr = &options->strict_host_key_checking;
1249ce74bacaSMatthew Dillon 		multistate_ptr = multistate_strict_hostkey;
125036e94dc5SPeter Avalos 		goto parse_multistate;
125118de8d7fSPeter Avalos 
125218de8d7fSPeter Avalos 	case oCompression:
125318de8d7fSPeter Avalos 		intptr = &options->compression;
12540cbfa66cSDaniel Fojt 		multistate_ptr = multistate_compression;
12550cbfa66cSDaniel Fojt 		goto parse_multistate;
125618de8d7fSPeter Avalos 
125718de8d7fSPeter Avalos 	case oTCPKeepAlive:
125818de8d7fSPeter Avalos 		intptr = &options->tcp_keep_alive;
125918de8d7fSPeter Avalos 		goto parse_flag;
126018de8d7fSPeter Avalos 
126118de8d7fSPeter Avalos 	case oNoHostAuthenticationForLocalhost:
126218de8d7fSPeter Avalos 		intptr = &options->no_host_authentication_for_localhost;
126318de8d7fSPeter Avalos 		goto parse_flag;
126418de8d7fSPeter Avalos 
126518de8d7fSPeter Avalos 	case oNumberOfPasswordPrompts:
126618de8d7fSPeter Avalos 		intptr = &options->number_of_password_prompts;
126718de8d7fSPeter Avalos 		goto parse_int;
126818de8d7fSPeter Avalos 
126918de8d7fSPeter Avalos 	case oRekeyLimit:
127050a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
127150a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
127250a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.", filename,
127336e94dc5SPeter Avalos 			    linenum);
127450a69bb5SSascha Wildner 			goto out;
127550a69bb5SSascha Wildner 		}
127636e94dc5SPeter Avalos 		if (strcmp(arg, "default") == 0) {
127736e94dc5SPeter Avalos 			val64 = 0;
127836e94dc5SPeter Avalos 		} else {
127950a69bb5SSascha Wildner 			if (scan_scaled(arg, &val64) == -1) {
128050a69bb5SSascha Wildner 				error("%.200s line %d: Bad number '%s': %s",
128136e94dc5SPeter Avalos 				    filename, linenum, arg, strerror(errno));
128250a69bb5SSascha Wildner 				goto out;
128350a69bb5SSascha Wildner 			}
128450a69bb5SSascha Wildner 			if (val64 != 0 && val64 < 16) {
128550a69bb5SSascha Wildner 				error("%.200s line %d: RekeyLimit too small",
128618de8d7fSPeter Avalos 				    filename, linenum);
128750a69bb5SSascha Wildner 				goto out;
128850a69bb5SSascha Wildner 			}
128936e94dc5SPeter Avalos 		}
129018de8d7fSPeter Avalos 		if (*activep && options->rekey_limit == -1)
1291e9778795SPeter Avalos 			options->rekey_limit = val64;
129250a69bb5SSascha Wildner 		if (ac != 0) { /* optional rekey interval present */
129350a69bb5SSascha Wildner 			if (strcmp(av[0], "none") == 0) {
129450a69bb5SSascha Wildner 				(void)argv_next(&ac, &av);	/* discard */
129536e94dc5SPeter Avalos 				break;
129636e94dc5SPeter Avalos 			}
129736e94dc5SPeter Avalos 			intptr = &options->rekey_interval;
129836e94dc5SPeter Avalos 			goto parse_time;
129936e94dc5SPeter Avalos 		}
130018de8d7fSPeter Avalos 		break;
130118de8d7fSPeter Avalos 
130218de8d7fSPeter Avalos 	case oIdentityFile:
130350a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
130450a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
130550a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
130650a69bb5SSascha Wildner 			    filename, linenum);
130750a69bb5SSascha Wildner 			goto out;
130850a69bb5SSascha Wildner 		}
130918de8d7fSPeter Avalos 		if (*activep) {
131018de8d7fSPeter Avalos 			intptr = &options->num_identity_files;
131150a69bb5SSascha Wildner 			if (*intptr >= SSH_MAX_IDENTITY_FILES) {
131250a69bb5SSascha Wildner 				error("%.200s line %d: Too many identity files "
131350a69bb5SSascha Wildner 				    "specified (max %d).", filename, linenum,
131450a69bb5SSascha Wildner 				    SSH_MAX_IDENTITY_FILES);
131550a69bb5SSascha Wildner 				goto out;
131650a69bb5SSascha Wildner 			}
1317e9778795SPeter Avalos 			add_identity_file(options, NULL,
1318e9778795SPeter Avalos 			    arg, flags & SSHCONF_USERCONF);
1319e9778795SPeter Avalos 		}
1320e9778795SPeter Avalos 		break;
1321e9778795SPeter Avalos 
1322e9778795SPeter Avalos 	case oCertificateFile:
132350a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
132450a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
132550a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
1326e9778795SPeter Avalos 			    filename, linenum);
132750a69bb5SSascha Wildner 			goto out;
132850a69bb5SSascha Wildner 		}
1329e9778795SPeter Avalos 		if (*activep) {
1330e9778795SPeter Avalos 			intptr = &options->num_certificate_files;
1331e9778795SPeter Avalos 			if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
133250a69bb5SSascha Wildner 				error("%.200s line %d: Too many certificate "
1333e9778795SPeter Avalos 				    "files specified (max %d).",
1334e9778795SPeter Avalos 				    filename, linenum,
1335e9778795SPeter Avalos 				    SSH_MAX_CERTIFICATE_FILES);
133650a69bb5SSascha Wildner 				goto out;
1337e9778795SPeter Avalos 			}
1338e9778795SPeter Avalos 			add_certificate_file(options, arg,
1339e9778795SPeter Avalos 			    flags & SSHCONF_USERCONF);
134018de8d7fSPeter Avalos 		}
134118de8d7fSPeter Avalos 		break;
134218de8d7fSPeter Avalos 
134318de8d7fSPeter Avalos 	case oXAuthLocation:
134418de8d7fSPeter Avalos 		charptr=&options->xauth_location;
134518de8d7fSPeter Avalos 		goto parse_string;
134618de8d7fSPeter Avalos 
134718de8d7fSPeter Avalos 	case oUser:
134818de8d7fSPeter Avalos 		charptr = &options->user;
134918de8d7fSPeter Avalos parse_string:
135050a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
135150a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
135250a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
13531c188a7fSPeter Avalos 			    filename, linenum);
135450a69bb5SSascha Wildner 			goto out;
135550a69bb5SSascha Wildner 		}
135618de8d7fSPeter Avalos 		if (*activep && *charptr == NULL)
135718de8d7fSPeter Avalos 			*charptr = xstrdup(arg);
135818de8d7fSPeter Avalos 		break;
135918de8d7fSPeter Avalos 
136018de8d7fSPeter Avalos 	case oGlobalKnownHostsFile:
13611c188a7fSPeter Avalos 		cpptr = (char **)&options->system_hostfiles;
13621c188a7fSPeter Avalos 		uintptr = &options->num_system_hostfiles;
13631c188a7fSPeter Avalos 		max_entries = SSH_MAX_HOSTS_FILES;
13641c188a7fSPeter Avalos parse_char_array:
136550a69bb5SSascha Wildner 		i = 0;
136650a69bb5SSascha Wildner 		value = *uintptr == 0; /* was array empty when we started? */
136750a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
136850a69bb5SSascha Wildner 			if (*arg == '\0') {
136950a69bb5SSascha Wildner 				error("%s line %d: keyword %s empty argument",
137050a69bb5SSascha Wildner 				    filename, linenum, keyword);
137150a69bb5SSascha Wildner 				goto out;
137250a69bb5SSascha Wildner 			}
137350a69bb5SSascha Wildner 			/* Allow "none" only in first position */
137450a69bb5SSascha Wildner 			if (strcasecmp(arg, "none") == 0) {
137550a69bb5SSascha Wildner 				if (i > 0 || ac > 0) {
137650a69bb5SSascha Wildner 					error("%s line %d: keyword %s \"none\" "
137750a69bb5SSascha Wildner 					    "argument must appear alone.",
137850a69bb5SSascha Wildner 					    filename, linenum, keyword);
137950a69bb5SSascha Wildner 					goto out;
138050a69bb5SSascha Wildner 				}
138150a69bb5SSascha Wildner 			}
138250a69bb5SSascha Wildner 			i++;
138350a69bb5SSascha Wildner 			if (*activep && value) {
138450a69bb5SSascha Wildner 				if ((*uintptr) >= max_entries) {
138550a69bb5SSascha Wildner 					error("%s line %d: too many %s "
138650a69bb5SSascha Wildner 					    "entries.", filename, linenum,
138750a69bb5SSascha Wildner 					    keyword);
138850a69bb5SSascha Wildner 					goto out;
138950a69bb5SSascha Wildner 				}
13901c188a7fSPeter Avalos 				cpptr[(*uintptr)++] = xstrdup(arg);
13911c188a7fSPeter Avalos 			}
13921c188a7fSPeter Avalos 		}
139350a69bb5SSascha Wildner 		break;
139418de8d7fSPeter Avalos 
139518de8d7fSPeter Avalos 	case oUserKnownHostsFile:
13961c188a7fSPeter Avalos 		cpptr = (char **)&options->user_hostfiles;
13971c188a7fSPeter Avalos 		uintptr = &options->num_user_hostfiles;
13981c188a7fSPeter Avalos 		max_entries = SSH_MAX_HOSTS_FILES;
13991c188a7fSPeter Avalos 		goto parse_char_array;
140018de8d7fSPeter Avalos 
14010cbfa66cSDaniel Fojt 	case oHostname:
140218de8d7fSPeter Avalos 		charptr = &options->hostname;
140318de8d7fSPeter Avalos 		goto parse_string;
140418de8d7fSPeter Avalos 
1405*ba1276acSMatthew Dillon 	case oTag:
1406*ba1276acSMatthew Dillon 		charptr = &options->tag;
1407*ba1276acSMatthew Dillon 		goto parse_string;
1408*ba1276acSMatthew Dillon 
140918de8d7fSPeter Avalos 	case oHostKeyAlias:
141018de8d7fSPeter Avalos 		charptr = &options->host_key_alias;
141118de8d7fSPeter Avalos 		goto parse_string;
141218de8d7fSPeter Avalos 
141318de8d7fSPeter Avalos 	case oPreferredAuthentications:
141418de8d7fSPeter Avalos 		charptr = &options->preferred_authentications;
141518de8d7fSPeter Avalos 		goto parse_string;
141618de8d7fSPeter Avalos 
141718de8d7fSPeter Avalos 	case oBindAddress:
141818de8d7fSPeter Avalos 		charptr = &options->bind_address;
141918de8d7fSPeter Avalos 		goto parse_string;
142018de8d7fSPeter Avalos 
1421664f4763Szrj 	case oBindInterface:
1422664f4763Szrj 		charptr = &options->bind_interface;
1423664f4763Szrj 		goto parse_string;
1424664f4763Szrj 
1425856ea928SPeter Avalos 	case oPKCS11Provider:
1426856ea928SPeter Avalos 		charptr = &options->pkcs11_provider;
142718de8d7fSPeter Avalos 		goto parse_string;
142818de8d7fSPeter Avalos 
14290cbfa66cSDaniel Fojt 	case oSecurityKeyProvider:
14300cbfa66cSDaniel Fojt 		charptr = &options->sk_provider;
14310cbfa66cSDaniel Fojt 		goto parse_string;
14320cbfa66cSDaniel Fojt 
143350a69bb5SSascha Wildner 	case oKnownHostsCommand:
143450a69bb5SSascha Wildner 		charptr = &options->known_hosts_command;
143550a69bb5SSascha Wildner 		goto parse_command;
143650a69bb5SSascha Wildner 
143718de8d7fSPeter Avalos 	case oProxyCommand:
143818de8d7fSPeter Avalos 		charptr = &options->proxy_command;
1439e9778795SPeter Avalos 		/* Ignore ProxyCommand if ProxyJump already specified */
1440e9778795SPeter Avalos 		if (options->jump_host != NULL)
1441e9778795SPeter Avalos 			charptr = &options->jump_host; /* Skip below */
144218de8d7fSPeter Avalos parse_command:
144350a69bb5SSascha Wildner 		if (str == NULL) {
144450a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
144550a69bb5SSascha Wildner 			    filename, linenum);
144650a69bb5SSascha Wildner 			goto out;
144750a69bb5SSascha Wildner 		}
144850a69bb5SSascha Wildner 		len = strspn(str, WHITESPACE "=");
144918de8d7fSPeter Avalos 		if (*activep && *charptr == NULL)
145050a69bb5SSascha Wildner 			*charptr = xstrdup(str + len);
145150a69bb5SSascha Wildner 		argv_consume(&ac);
145250a69bb5SSascha Wildner 		break;
145318de8d7fSPeter Avalos 
1454e9778795SPeter Avalos 	case oProxyJump:
145550a69bb5SSascha Wildner 		if (str == NULL) {
145650a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
1457e9778795SPeter Avalos 			    filename, linenum);
145850a69bb5SSascha Wildner 			goto out;
1459e9778795SPeter Avalos 		}
146050a69bb5SSascha Wildner 		len = strspn(str, WHITESPACE "=");
146150a69bb5SSascha Wildner 		/* XXX use argv? */
146250a69bb5SSascha Wildner 		if (parse_jump(str + len, options, *activep) == -1) {
146350a69bb5SSascha Wildner 			error("%.200s line %d: Invalid ProxyJump \"%s\"",
146450a69bb5SSascha Wildner 			    filename, linenum, str + len);
146550a69bb5SSascha Wildner 			goto out;
1466e9778795SPeter Avalos 		}
146750a69bb5SSascha Wildner 		argv_consume(&ac);
146850a69bb5SSascha Wildner 		break;
1469e9778795SPeter Avalos 
147018de8d7fSPeter Avalos 	case oPort:
147150a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
147250a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
147350a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
1474664f4763Szrj 			    filename, linenum);
147550a69bb5SSascha Wildner 			goto out;
147650a69bb5SSascha Wildner 		}
1477664f4763Szrj 		value = a2port(arg);
147850a69bb5SSascha Wildner 		if (value <= 0) {
147950a69bb5SSascha Wildner 			error("%.200s line %d: Bad port '%s'.",
1480664f4763Szrj 			    filename, linenum, arg);
148150a69bb5SSascha Wildner 			goto out;
148250a69bb5SSascha Wildner 		}
1483664f4763Szrj 		if (*activep && options->port == -1)
1484664f4763Szrj 			options->port = value;
148518de8d7fSPeter Avalos 		break;
148618de8d7fSPeter Avalos 
148718de8d7fSPeter Avalos 	case oConnectionAttempts:
148818de8d7fSPeter Avalos 		intptr = &options->connection_attempts;
1489664f4763Szrj parse_int:
149050a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
149150a69bb5SSascha Wildner 		if ((errstr = atoi_err(arg, &value)) != NULL) {
149250a69bb5SSascha Wildner 			error("%s line %d: integer value %s.",
1493664f4763Szrj 			    filename, linenum, errstr);
149450a69bb5SSascha Wildner 			goto out;
149550a69bb5SSascha Wildner 		}
1496664f4763Szrj 		if (*activep && *intptr == -1)
1497664f4763Szrj 			*intptr = value;
1498664f4763Szrj 		break;
149918de8d7fSPeter Avalos 
150018de8d7fSPeter Avalos 	case oCiphers:
150150a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
150250a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
150350a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
150450a69bb5SSascha Wildner 			    filename, linenum);
150550a69bb5SSascha Wildner 			goto out;
150650a69bb5SSascha Wildner 		}
15070cbfa66cSDaniel Fojt 		if (*arg != '-' &&
150850a69bb5SSascha Wildner 		    !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
150950a69bb5SSascha Wildner 			error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
151018de8d7fSPeter Avalos 			    filename, linenum, arg ? arg : "<NONE>");
151150a69bb5SSascha Wildner 			goto out;
151250a69bb5SSascha Wildner 		}
151318de8d7fSPeter Avalos 		if (*activep && options->ciphers == NULL)
151418de8d7fSPeter Avalos 			options->ciphers = xstrdup(arg);
151518de8d7fSPeter Avalos 		break;
151618de8d7fSPeter Avalos 
151718de8d7fSPeter Avalos 	case oMacs:
151850a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
151950a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
152050a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
152150a69bb5SSascha Wildner 			    filename, linenum);
152250a69bb5SSascha Wildner 			goto out;
152350a69bb5SSascha Wildner 		}
15240cbfa66cSDaniel Fojt 		if (*arg != '-' &&
152550a69bb5SSascha Wildner 		    !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
152650a69bb5SSascha Wildner 			error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
152718de8d7fSPeter Avalos 			    filename, linenum, arg ? arg : "<NONE>");
152850a69bb5SSascha Wildner 			goto out;
152950a69bb5SSascha Wildner 		}
153018de8d7fSPeter Avalos 		if (*activep && options->macs == NULL)
153118de8d7fSPeter Avalos 			options->macs = xstrdup(arg);
153218de8d7fSPeter Avalos 		break;
153318de8d7fSPeter Avalos 
15349f304aafSPeter Avalos 	case oKexAlgorithms:
153550a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
153650a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
153750a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
15389f304aafSPeter Avalos 			    filename, linenum);
153950a69bb5SSascha Wildner 			goto out;
154050a69bb5SSascha Wildner 		}
1541ce74bacaSMatthew Dillon 		if (*arg != '-' &&
15420cbfa66cSDaniel Fojt 		    !kex_names_valid(*arg == '+' || *arg == '^' ?
154350a69bb5SSascha Wildner 		    arg + 1 : arg)) {
154450a69bb5SSascha Wildner 			error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
15459f304aafSPeter Avalos 			    filename, linenum, arg ? arg : "<NONE>");
154650a69bb5SSascha Wildner 			goto out;
154750a69bb5SSascha Wildner 		}
15489f304aafSPeter Avalos 		if (*activep && options->kex_algorithms == NULL)
15499f304aafSPeter Avalos 			options->kex_algorithms = xstrdup(arg);
15509f304aafSPeter Avalos 		break;
15519f304aafSPeter Avalos 
155218de8d7fSPeter Avalos 	case oHostKeyAlgorithms:
1553e9778795SPeter Avalos 		charptr = &options->hostkeyalgorithms;
1554*ba1276acSMatthew Dillon 		ca_only = 0;
155550a69bb5SSascha Wildner parse_pubkey_algos:
155650a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
155750a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
155850a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
1559e9778795SPeter Avalos 			    filename, linenum);
156050a69bb5SSascha Wildner 			goto out;
156150a69bb5SSascha Wildner 		}
1562ce74bacaSMatthew Dillon 		if (*arg != '-' &&
15630cbfa66cSDaniel Fojt 		    !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
1564*ba1276acSMatthew Dillon 		    arg + 1 : arg, 1, ca_only)) {
156550a69bb5SSascha Wildner 			error("%s line %d: Bad key types '%s'.",
156618de8d7fSPeter Avalos 			    filename, linenum, arg ? arg : "<NONE>");
156750a69bb5SSascha Wildner 			goto out;
156850a69bb5SSascha Wildner 		}
1569e9778795SPeter Avalos 		if (*activep && *charptr == NULL)
1570e9778795SPeter Avalos 			*charptr = xstrdup(arg);
157118de8d7fSPeter Avalos 		break;
157218de8d7fSPeter Avalos 
1573664f4763Szrj 	case oCASignatureAlgorithms:
1574664f4763Szrj 		charptr = &options->ca_sign_algorithms;
1575*ba1276acSMatthew Dillon 		ca_only = 1;
157650a69bb5SSascha Wildner 		goto parse_pubkey_algos;
1577664f4763Szrj 
157818de8d7fSPeter Avalos 	case oLogLevel:
157918de8d7fSPeter Avalos 		log_level_ptr = &options->log_level;
158050a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
158118de8d7fSPeter Avalos 		value = log_level_number(arg);
158250a69bb5SSascha Wildner 		if (value == SYSLOG_LEVEL_NOT_SET) {
158350a69bb5SSascha Wildner 			error("%.200s line %d: unsupported log level '%s'",
158418de8d7fSPeter Avalos 			    filename, linenum, arg ? arg : "<NONE>");
158550a69bb5SSascha Wildner 			goto out;
158650a69bb5SSascha Wildner 		}
158718de8d7fSPeter Avalos 		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
158818de8d7fSPeter Avalos 			*log_level_ptr = (LogLevel) value;
158918de8d7fSPeter Avalos 		break;
159018de8d7fSPeter Avalos 
1591ce74bacaSMatthew Dillon 	case oLogFacility:
1592ce74bacaSMatthew Dillon 		log_facility_ptr = &options->log_facility;
159350a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
1594ce74bacaSMatthew Dillon 		value = log_facility_number(arg);
159550a69bb5SSascha Wildner 		if (value == SYSLOG_FACILITY_NOT_SET) {
159650a69bb5SSascha Wildner 			error("%.200s line %d: unsupported log facility '%s'",
1597ce74bacaSMatthew Dillon 			    filename, linenum, arg ? arg : "<NONE>");
159850a69bb5SSascha Wildner 			goto out;
159950a69bb5SSascha Wildner 		}
1600ce74bacaSMatthew Dillon 		if (*log_facility_ptr == -1)
1601ce74bacaSMatthew Dillon 			*log_facility_ptr = (SyslogFacility) value;
1602ce74bacaSMatthew Dillon 		break;
1603ce74bacaSMatthew Dillon 
160450a69bb5SSascha Wildner 	case oLogVerbose:
160550a69bb5SSascha Wildner 		cppptr = &options->log_verbose;
160650a69bb5SSascha Wildner 		uintptr = &options->num_log_verbose;
160750a69bb5SSascha Wildner 		i = 0;
160850a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
160950a69bb5SSascha Wildner 			if (*arg == '\0') {
161050a69bb5SSascha Wildner 				error("%s line %d: keyword %s empty argument",
161150a69bb5SSascha Wildner 				    filename, linenum, keyword);
161250a69bb5SSascha Wildner 				goto out;
161350a69bb5SSascha Wildner 			}
161450a69bb5SSascha Wildner 			/* Allow "none" only in first position */
161550a69bb5SSascha Wildner 			if (strcasecmp(arg, "none") == 0) {
161650a69bb5SSascha Wildner 				if (i > 0 || ac > 0) {
161750a69bb5SSascha Wildner 					error("%s line %d: keyword %s \"none\" "
161850a69bb5SSascha Wildner 					    "argument must appear alone.",
161950a69bb5SSascha Wildner 					    filename, linenum, keyword);
162050a69bb5SSascha Wildner 					goto out;
162150a69bb5SSascha Wildner 				}
162250a69bb5SSascha Wildner 			}
162350a69bb5SSascha Wildner 			i++;
162450a69bb5SSascha Wildner 			if (*activep && *uintptr == 0) {
162550a69bb5SSascha Wildner 				*cppptr = xrecallocarray(*cppptr, *uintptr,
162650a69bb5SSascha Wildner 				    *uintptr + 1, sizeof(**cppptr));
162750a69bb5SSascha Wildner 				(*cppptr)[(*uintptr)++] = xstrdup(arg);
162850a69bb5SSascha Wildner 			}
162950a69bb5SSascha Wildner 		}
163050a69bb5SSascha Wildner 		break;
163150a69bb5SSascha Wildner 
163218de8d7fSPeter Avalos 	case oLocalForward:
163318de8d7fSPeter Avalos 	case oRemoteForward:
1634cb5eb4f1SPeter Avalos 	case oDynamicForward:
163550a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
163650a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
163750a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
163818de8d7fSPeter Avalos 			    filename, linenum);
163950a69bb5SSascha Wildner 			goto out;
164050a69bb5SSascha Wildner 		}
1641cb5eb4f1SPeter Avalos 
1642ce74bacaSMatthew Dillon 		remotefwd = (opcode == oRemoteForward);
1643ce74bacaSMatthew Dillon 		dynamicfwd = (opcode == oDynamicForward);
1644ce74bacaSMatthew Dillon 
1645ce74bacaSMatthew Dillon 		if (!dynamicfwd) {
164650a69bb5SSascha Wildner 			arg2 = argv_next(&ac, &av);
1647ce74bacaSMatthew Dillon 			if (arg2 == NULL || *arg2 == '\0') {
1648ce74bacaSMatthew Dillon 				if (remotefwd)
1649ce74bacaSMatthew Dillon 					dynamicfwd = 1;
165050a69bb5SSascha Wildner 				else {
165150a69bb5SSascha Wildner 					error("%.200s line %d: Missing target "
1652ce74bacaSMatthew Dillon 					    "argument.", filename, linenum);
165350a69bb5SSascha Wildner 					goto out;
165450a69bb5SSascha Wildner 				}
1655ce74bacaSMatthew Dillon 			} else {
165618de8d7fSPeter Avalos 				/* construct a string for parse_forward */
1657ce74bacaSMatthew Dillon 				snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
1658ce74bacaSMatthew Dillon 				    arg2);
1659cb5eb4f1SPeter Avalos 			}
1660ce74bacaSMatthew Dillon 		}
1661ce74bacaSMatthew Dillon 		if (dynamicfwd)
1662ce74bacaSMatthew Dillon 			strlcpy(fwdarg, arg, sizeof(fwdarg));
166318de8d7fSPeter Avalos 
166450a69bb5SSascha Wildner 		if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
166550a69bb5SSascha Wildner 			error("%.200s line %d: Bad forwarding specification.",
166618de8d7fSPeter Avalos 			    filename, linenum);
166750a69bb5SSascha Wildner 			goto out;
166850a69bb5SSascha Wildner 		}
166918de8d7fSPeter Avalos 
167018de8d7fSPeter Avalos 		if (*activep) {
1671ce74bacaSMatthew Dillon 			if (remotefwd) {
167218de8d7fSPeter Avalos 				add_remote_forward(options, &fwd);
1673ce74bacaSMatthew Dillon 			} else {
1674ce74bacaSMatthew Dillon 				add_local_forward(options, &fwd);
1675ce74bacaSMatthew Dillon 			}
167618de8d7fSPeter Avalos 		}
167718de8d7fSPeter Avalos 		break;
167818de8d7fSPeter Avalos 
167950a69bb5SSascha Wildner 	case oPermitRemoteOpen:
168050a69bb5SSascha Wildner 		uintptr = &options->num_permitted_remote_opens;
168150a69bb5SSascha Wildner 		cppptr = &options->permitted_remote_opens;
1682*ba1276acSMatthew Dillon 		found = *uintptr == 0;
168350a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
168450a69bb5SSascha Wildner 			arg2 = xstrdup(arg);
1685*ba1276acSMatthew Dillon 			/* Allow any/none only in first position */
1686*ba1276acSMatthew Dillon 			if (strcasecmp(arg, "none") == 0 ||
1687*ba1276acSMatthew Dillon 			    strcasecmp(arg, "any") == 0) {
1688*ba1276acSMatthew Dillon 				if (nstrs > 0 || ac > 0) {
1689*ba1276acSMatthew Dillon 					error("%s line %d: keyword %s \"%s\" "
1690*ba1276acSMatthew Dillon 					    "argument must appear alone.",
1691*ba1276acSMatthew Dillon 					    filename, linenum, keyword, arg);
1692*ba1276acSMatthew Dillon 					free(arg2);
1693*ba1276acSMatthew Dillon 					goto out;
1694*ba1276acSMatthew Dillon 				}
1695*ba1276acSMatthew Dillon 			} else {
1696ee116499SAntonio Huete Jimenez 				p = hpdelim(&arg);
1697ee116499SAntonio Huete Jimenez 				if (p == NULL) {
169850a69bb5SSascha Wildner 					fatal("%s line %d: missing host in %s",
169950a69bb5SSascha Wildner 					    filename, linenum,
170050a69bb5SSascha Wildner 					    lookup_opcode_name(opcode));
170150a69bb5SSascha Wildner 				}
170250a69bb5SSascha Wildner 				p = cleanhostname(p);
170350a69bb5SSascha Wildner 				/*
170450a69bb5SSascha Wildner 				 * don't want to use permitopen_port to avoid
170550a69bb5SSascha Wildner 				 * dependency on channels.[ch] here.
170650a69bb5SSascha Wildner 				 */
1707*ba1276acSMatthew Dillon 				if (arg == NULL || (strcmp(arg, "*") != 0 &&
1708*ba1276acSMatthew Dillon 				    a2port(arg) <= 0)) {
1709*ba1276acSMatthew Dillon 					fatal("%s line %d: bad port number "
1710*ba1276acSMatthew Dillon 					    "in %s", filename, linenum,
171150a69bb5SSascha Wildner 					    lookup_opcode_name(opcode));
171250a69bb5SSascha Wildner 				}
1713*ba1276acSMatthew Dillon 			}
171450a69bb5SSascha Wildner 			opt_array_append(filename, linenum,
171550a69bb5SSascha Wildner 			    lookup_opcode_name(opcode),
1716*ba1276acSMatthew Dillon 			    &strs, &nstrs, arg2);
171750a69bb5SSascha Wildner 			free(arg2);
171850a69bb5SSascha Wildner 		}
1719*ba1276acSMatthew Dillon 		if (nstrs == 0)
1720*ba1276acSMatthew Dillon 			fatal("%s line %d: missing %s specification",
1721*ba1276acSMatthew Dillon 			    filename, linenum, lookup_opcode_name(opcode));
1722*ba1276acSMatthew Dillon 		if (found && *activep) {
1723*ba1276acSMatthew Dillon 			*cppptr = strs;
1724*ba1276acSMatthew Dillon 			*uintptr = nstrs;
1725*ba1276acSMatthew Dillon 			strs = NULL; /* transferred */
1726*ba1276acSMatthew Dillon 			nstrs = 0;
1727*ba1276acSMatthew Dillon 		}
172850a69bb5SSascha Wildner 		break;
172950a69bb5SSascha Wildner 
173018de8d7fSPeter Avalos 	case oClearAllForwardings:
173118de8d7fSPeter Avalos 		intptr = &options->clear_forwardings;
173218de8d7fSPeter Avalos 		goto parse_flag;
173318de8d7fSPeter Avalos 
173418de8d7fSPeter Avalos 	case oHost:
173550a69bb5SSascha Wildner 		if (cmdline) {
173650a69bb5SSascha Wildner 			error("Host directive not supported as a command-line "
173736e94dc5SPeter Avalos 			    "option");
173850a69bb5SSascha Wildner 			goto out;
173950a69bb5SSascha Wildner 		}
174018de8d7fSPeter Avalos 		*activep = 0;
17411c188a7fSPeter Avalos 		arg2 = NULL;
174250a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
174350a69bb5SSascha Wildner 			if (*arg == '\0') {
174450a69bb5SSascha Wildner 				error("%s line %d: keyword %s empty argument",
174550a69bb5SSascha Wildner 				    filename, linenum, keyword);
174650a69bb5SSascha Wildner 				goto out;
174750a69bb5SSascha Wildner 			}
174850a69bb5SSascha Wildner 			if ((flags & SSHCONF_NEVERMATCH) != 0) {
174950a69bb5SSascha Wildner 				argv_consume(&ac);
1750e9778795SPeter Avalos 				break;
175150a69bb5SSascha Wildner 			}
17521c188a7fSPeter Avalos 			negated = *arg == '!';
17531c188a7fSPeter Avalos 			if (negated)
17541c188a7fSPeter Avalos 				arg++;
175518de8d7fSPeter Avalos 			if (match_pattern(host, arg)) {
17561c188a7fSPeter Avalos 				if (negated) {
17571c188a7fSPeter Avalos 					debug("%.200s line %d: Skipping Host "
17581c188a7fSPeter Avalos 					    "block because of negated match "
17591c188a7fSPeter Avalos 					    "for %.100s", filename, linenum,
17601c188a7fSPeter Avalos 					    arg);
17611c188a7fSPeter Avalos 					*activep = 0;
176250a69bb5SSascha Wildner 					argv_consume(&ac);
176318de8d7fSPeter Avalos 					break;
176418de8d7fSPeter Avalos 				}
17651c188a7fSPeter Avalos 				if (!*activep)
17661c188a7fSPeter Avalos 					arg2 = arg; /* logged below */
17671c188a7fSPeter Avalos 				*activep = 1;
17681c188a7fSPeter Avalos 			}
17691c188a7fSPeter Avalos 		}
17701c188a7fSPeter Avalos 		if (*activep)
17711c188a7fSPeter Avalos 			debug("%.200s line %d: Applying options for %.100s",
17721c188a7fSPeter Avalos 			    filename, linenum, arg2);
177350a69bb5SSascha Wildner 		break;
177418de8d7fSPeter Avalos 
177536e94dc5SPeter Avalos 	case oMatch:
177650a69bb5SSascha Wildner 		if (cmdline) {
177750a69bb5SSascha Wildner 			error("Host directive not supported as a command-line "
177836e94dc5SPeter Avalos 			    "option");
177950a69bb5SSascha Wildner 			goto out;
178050a69bb5SSascha Wildner 		}
178150a69bb5SSascha Wildner 		value = match_cfg_line(options, &str, pw, host, original_host,
1782664f4763Szrj 		    flags & SSHCONF_FINAL, want_final_pass,
1783664f4763Szrj 		    filename, linenum);
178450a69bb5SSascha Wildner 		if (value < 0) {
178550a69bb5SSascha Wildner 			error("%.200s line %d: Bad Match condition", filename,
178636e94dc5SPeter Avalos 			    linenum);
178750a69bb5SSascha Wildner 			goto out;
178850a69bb5SSascha Wildner 		}
1789e9778795SPeter Avalos 		*activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
179050a69bb5SSascha Wildner 		/*
179150a69bb5SSascha Wildner 		 * If match_cfg_line() didn't consume all its arguments then
179250a69bb5SSascha Wildner 		 * arrange for the extra arguments check below to fail.
179350a69bb5SSascha Wildner 		 */
179450a69bb5SSascha Wildner 
179550a69bb5SSascha Wildner 		if (str == NULL || *str == '\0')
179650a69bb5SSascha Wildner 			argv_consume(&ac);
179736e94dc5SPeter Avalos 		break;
179836e94dc5SPeter Avalos 
179918de8d7fSPeter Avalos 	case oEscapeChar:
180018de8d7fSPeter Avalos 		intptr = &options->escape_char;
180150a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
180250a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
180350a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
180450a69bb5SSascha Wildner 			    filename, linenum);
180550a69bb5SSascha Wildner 			goto out;
180650a69bb5SSascha Wildner 		}
1807e9778795SPeter Avalos 		if (strcmp(arg, "none") == 0)
1808e9778795SPeter Avalos 			value = SSH_ESCAPECHAR_NONE;
1809e9778795SPeter Avalos 		else if (arg[1] == '\0')
1810e9778795SPeter Avalos 			value = (u_char) arg[0];
1811e9778795SPeter Avalos 		else if (arg[0] == '^' && arg[2] == 0 &&
181218de8d7fSPeter Avalos 		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
181318de8d7fSPeter Avalos 			value = (u_char) arg[1] & 31;
181418de8d7fSPeter Avalos 		else {
181550a69bb5SSascha Wildner 			error("%.200s line %d: Bad escape character.",
181618de8d7fSPeter Avalos 			    filename, linenum);
181750a69bb5SSascha Wildner 			goto out;
181818de8d7fSPeter Avalos 		}
181918de8d7fSPeter Avalos 		if (*activep && *intptr == -1)
182018de8d7fSPeter Avalos 			*intptr = value;
182118de8d7fSPeter Avalos 		break;
182218de8d7fSPeter Avalos 
182318de8d7fSPeter Avalos 	case oAddressFamily:
182418de8d7fSPeter Avalos 		intptr = &options->address_family;
182536e94dc5SPeter Avalos 		multistate_ptr = multistate_addressfamily;
182636e94dc5SPeter Avalos 		goto parse_multistate;
182718de8d7fSPeter Avalos 
182818de8d7fSPeter Avalos 	case oEnableSSHKeysign:
182918de8d7fSPeter Avalos 		intptr = &options->enable_ssh_keysign;
183018de8d7fSPeter Avalos 		goto parse_flag;
183118de8d7fSPeter Avalos 
183218de8d7fSPeter Avalos 	case oIdentitiesOnly:
183318de8d7fSPeter Avalos 		intptr = &options->identities_only;
183418de8d7fSPeter Avalos 		goto parse_flag;
183518de8d7fSPeter Avalos 
183618de8d7fSPeter Avalos 	case oServerAliveInterval:
183718de8d7fSPeter Avalos 		intptr = &options->server_alive_interval;
183818de8d7fSPeter Avalos 		goto parse_time;
183918de8d7fSPeter Avalos 
184018de8d7fSPeter Avalos 	case oServerAliveCountMax:
184118de8d7fSPeter Avalos 		intptr = &options->server_alive_count_max;
184218de8d7fSPeter Avalos 		goto parse_int;
184318de8d7fSPeter Avalos 
184418de8d7fSPeter Avalos 	case oSendEnv:
1845*ba1276acSMatthew Dillon 		/* XXX appends to list; doesn't respect first-match-wins */
184650a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
184750a69bb5SSascha Wildner 			if (*arg == '\0' || strchr(arg, '=') != NULL) {
184850a69bb5SSascha Wildner 				error("%s line %d: Invalid environment name.",
184918de8d7fSPeter Avalos 				    filename, linenum);
185050a69bb5SSascha Wildner 				goto out;
185150a69bb5SSascha Wildner 			}
1852*ba1276acSMatthew Dillon 			found = 1;
185318de8d7fSPeter Avalos 			if (!*activep)
185418de8d7fSPeter Avalos 				continue;
1855664f4763Szrj 			if (*arg == '-') {
1856664f4763Szrj 				/* Removing an env var */
1857664f4763Szrj 				rm_env(options, arg, filename, linenum);
1858664f4763Szrj 				continue;
185950a69bb5SSascha Wildner 			}
1860ee116499SAntonio Huete Jimenez 			opt_array_append(filename, linenum,
1861ee116499SAntonio Huete Jimenez 			    lookup_opcode_name(opcode),
1862ee116499SAntonio Huete Jimenez 			    &options->send_env, &options->num_send_env, arg);
1863664f4763Szrj 		}
1864*ba1276acSMatthew Dillon 		if (!found) {
1865*ba1276acSMatthew Dillon 			fatal("%s line %d: no %s specified",
1866*ba1276acSMatthew Dillon 			    filename, linenum, keyword);
1867*ba1276acSMatthew Dillon 		}
1868664f4763Szrj 		break;
1869664f4763Szrj 
1870664f4763Szrj 	case oSetEnv:
1871*ba1276acSMatthew Dillon 		found = options->num_setenv == 0;
187250a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
187350a69bb5SSascha Wildner 			if (strchr(arg, '=') == NULL) {
187450a69bb5SSascha Wildner 				error("%s line %d: Invalid SetEnv.",
1875664f4763Szrj 				    filename, linenum);
187650a69bb5SSascha Wildner 				goto out;
187750a69bb5SSascha Wildner 			}
1878*ba1276acSMatthew Dillon 			if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
1879ee116499SAntonio Huete Jimenez 				debug2("%s line %d: ignoring duplicate env "
1880ee116499SAntonio Huete Jimenez 				    "name \"%.64s\"", filename, linenum, arg);
1881ee116499SAntonio Huete Jimenez 				continue;
188250a69bb5SSascha Wildner 			}
1883ee116499SAntonio Huete Jimenez 			opt_array_append(filename, linenum,
1884ee116499SAntonio Huete Jimenez 			    lookup_opcode_name(opcode),
1885*ba1276acSMatthew Dillon 			    &strs, &nstrs, arg);
1886*ba1276acSMatthew Dillon 		}
1887*ba1276acSMatthew Dillon 		if (nstrs == 0) {
1888*ba1276acSMatthew Dillon 			fatal("%s line %d: no %s specified",
1889*ba1276acSMatthew Dillon 			    filename, linenum, keyword);
1890*ba1276acSMatthew Dillon 		}
1891*ba1276acSMatthew Dillon 		if (found && *activep) {
1892*ba1276acSMatthew Dillon 			options->setenv = strs;
1893*ba1276acSMatthew Dillon 			options->num_setenv = nstrs;
1894*ba1276acSMatthew Dillon 			strs = NULL; /* transferred */
1895*ba1276acSMatthew Dillon 			nstrs = 0;
1896664f4763Szrj 		}
189718de8d7fSPeter Avalos 		break;
189818de8d7fSPeter Avalos 
189918de8d7fSPeter Avalos 	case oControlPath:
190018de8d7fSPeter Avalos 		charptr = &options->control_path;
190118de8d7fSPeter Avalos 		goto parse_string;
190218de8d7fSPeter Avalos 
190318de8d7fSPeter Avalos 	case oControlMaster:
190418de8d7fSPeter Avalos 		intptr = &options->control_master;
190536e94dc5SPeter Avalos 		multistate_ptr = multistate_controlmaster;
190636e94dc5SPeter Avalos 		goto parse_multistate;
190718de8d7fSPeter Avalos 
1908856ea928SPeter Avalos 	case oControlPersist:
1909856ea928SPeter Avalos 		/* no/false/yes/true, or a time spec */
1910856ea928SPeter Avalos 		intptr = &options->control_persist;
191150a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
191250a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
191350a69bb5SSascha Wildner 			error("%.200s line %d: Missing ControlPersist"
1914856ea928SPeter Avalos 			    " argument.", filename, linenum);
191550a69bb5SSascha Wildner 			goto out;
191650a69bb5SSascha Wildner 		}
1917856ea928SPeter Avalos 		value = 0;
1918856ea928SPeter Avalos 		value2 = 0;	/* timeout */
1919856ea928SPeter Avalos 		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
1920856ea928SPeter Avalos 			value = 0;
1921856ea928SPeter Avalos 		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
1922856ea928SPeter Avalos 			value = 1;
1923856ea928SPeter Avalos 		else if ((value2 = convtime(arg)) >= 0)
1924856ea928SPeter Avalos 			value = 1;
192550a69bb5SSascha Wildner 		else {
192650a69bb5SSascha Wildner 			error("%.200s line %d: Bad ControlPersist argument.",
1927856ea928SPeter Avalos 			    filename, linenum);
192850a69bb5SSascha Wildner 			goto out;
192950a69bb5SSascha Wildner 		}
1930856ea928SPeter Avalos 		if (*activep && *intptr == -1) {
1931856ea928SPeter Avalos 			*intptr = value;
1932856ea928SPeter Avalos 			options->control_persist_timeout = value2;
1933856ea928SPeter Avalos 		}
1934856ea928SPeter Avalos 		break;
1935856ea928SPeter Avalos 
193618de8d7fSPeter Avalos 	case oHashKnownHosts:
193718de8d7fSPeter Avalos 		intptr = &options->hash_known_hosts;
193818de8d7fSPeter Avalos 		goto parse_flag;
193918de8d7fSPeter Avalos 
194018de8d7fSPeter Avalos 	case oTunnel:
194118de8d7fSPeter Avalos 		intptr = &options->tun_open;
194236e94dc5SPeter Avalos 		multistate_ptr = multistate_tunnel;
194336e94dc5SPeter Avalos 		goto parse_multistate;
194418de8d7fSPeter Avalos 
194518de8d7fSPeter Avalos 	case oTunnelDevice:
194650a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
194750a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
194850a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
194950a69bb5SSascha Wildner 			    filename, linenum);
195050a69bb5SSascha Wildner 			goto out;
195150a69bb5SSascha Wildner 		}
195218de8d7fSPeter Avalos 		value = a2tun(arg, &value2);
195350a69bb5SSascha Wildner 		if (value == SSH_TUNID_ERR) {
195450a69bb5SSascha Wildner 			error("%.200s line %d: Bad tun device.",
195550a69bb5SSascha Wildner 			    filename, linenum);
195650a69bb5SSascha Wildner 			goto out;
195750a69bb5SSascha Wildner 		}
195850a69bb5SSascha Wildner 		if (*activep && options->tun_local == -1) {
195918de8d7fSPeter Avalos 			options->tun_local = value;
196018de8d7fSPeter Avalos 			options->tun_remote = value2;
196118de8d7fSPeter Avalos 		}
196218de8d7fSPeter Avalos 		break;
196318de8d7fSPeter Avalos 
196418de8d7fSPeter Avalos 	case oLocalCommand:
196518de8d7fSPeter Avalos 		charptr = &options->local_command;
196618de8d7fSPeter Avalos 		goto parse_command;
196718de8d7fSPeter Avalos 
196818de8d7fSPeter Avalos 	case oPermitLocalCommand:
196918de8d7fSPeter Avalos 		intptr = &options->permit_local_command;
197018de8d7fSPeter Avalos 		goto parse_flag;
197118de8d7fSPeter Avalos 
1972ce74bacaSMatthew Dillon 	case oRemoteCommand:
1973ce74bacaSMatthew Dillon 		charptr = &options->remote_command;
1974ce74bacaSMatthew Dillon 		goto parse_command;
1975ce74bacaSMatthew Dillon 
197618de8d7fSPeter Avalos 	case oVisualHostKey:
197718de8d7fSPeter Avalos 		intptr = &options->visual_host_key;
197818de8d7fSPeter Avalos 		goto parse_flag;
197918de8d7fSPeter Avalos 
1980e9778795SPeter Avalos 	case oInclude:
198150a69bb5SSascha Wildner 		if (cmdline) {
198250a69bb5SSascha Wildner 			error("Include directive not supported as a "
1983e9778795SPeter Avalos 			    "command-line option");
198450a69bb5SSascha Wildner 			goto out;
198550a69bb5SSascha Wildner 		}
1986e9778795SPeter Avalos 		value = 0;
198750a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
198850a69bb5SSascha Wildner 			if (*arg == '\0') {
198950a69bb5SSascha Wildner 				error("%s line %d: keyword %s empty argument",
199050a69bb5SSascha Wildner 				    filename, linenum, keyword);
199150a69bb5SSascha Wildner 				goto out;
199250a69bb5SSascha Wildner 			}
1993e9778795SPeter Avalos 			/*
1994e9778795SPeter Avalos 			 * Ensure all paths are anchored. User configuration
1995e9778795SPeter Avalos 			 * files may begin with '~/' but system configurations
1996e9778795SPeter Avalos 			 * must not. If the path is relative, then treat it
1997e9778795SPeter Avalos 			 * as living in ~/.ssh for user configurations or
1998e9778795SPeter Avalos 			 * /etc/ssh for system ones.
1999e9778795SPeter Avalos 			 */
200050a69bb5SSascha Wildner 			if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) {
200150a69bb5SSascha Wildner 				error("%.200s line %d: bad include path %s.",
2002e9778795SPeter Avalos 				    filename, linenum, arg);
200350a69bb5SSascha Wildner 				goto out;
200450a69bb5SSascha Wildner 			}
2005664f4763Szrj 			if (!path_absolute(arg) && *arg != '~') {
2006e9778795SPeter Avalos 				xasprintf(&arg2, "%s/%s",
2007e9778795SPeter Avalos 				    (flags & SSHCONF_USERCONF) ?
2008e9778795SPeter Avalos 				    "~/" _PATH_SSH_USER_DIR : SSHDIR, arg);
2009e9778795SPeter Avalos 			} else
2010e9778795SPeter Avalos 				arg2 = xstrdup(arg);
2011e9778795SPeter Avalos 			memset(&gl, 0, sizeof(gl));
2012e9778795SPeter Avalos 			r = glob(arg2, GLOB_TILDE, NULL, &gl);
2013e9778795SPeter Avalos 			if (r == GLOB_NOMATCH) {
2014e9778795SPeter Avalos 				debug("%.200s line %d: include %s matched no "
2015e9778795SPeter Avalos 				    "files",filename, linenum, arg2);
2016ce74bacaSMatthew Dillon 				free(arg2);
2017e9778795SPeter Avalos 				continue;
201850a69bb5SSascha Wildner 			} else if (r != 0) {
201950a69bb5SSascha Wildner 				error("%.200s line %d: glob failed for %s.",
2020e9778795SPeter Avalos 				    filename, linenum, arg2);
202150a69bb5SSascha Wildner 				goto out;
202250a69bb5SSascha Wildner 			}
2023e9778795SPeter Avalos 			free(arg2);
2024e9778795SPeter Avalos 			oactive = *activep;
20250cbfa66cSDaniel Fojt 			for (i = 0; i < gl.gl_pathc; i++) {
2026e9778795SPeter Avalos 				debug3("%.200s line %d: Including file %s "
2027e9778795SPeter Avalos 				    "depth %d%s", filename, linenum,
2028e9778795SPeter Avalos 				    gl.gl_pathv[i], depth,
2029e9778795SPeter Avalos 				    oactive ? "" : " (parse only)");
2030e9778795SPeter Avalos 				r = read_config_file_depth(gl.gl_pathv[i],
2031e9778795SPeter Avalos 				    pw, host, original_host, options,
2032e9778795SPeter Avalos 				    flags | SSHCONF_CHECKPERM |
2033e9778795SPeter Avalos 				    (oactive ? 0 : SSHCONF_NEVERMATCH),
2034664f4763Szrj 				    activep, want_final_pass, depth + 1);
2035ce74bacaSMatthew Dillon 				if (r != 1 && errno != ENOENT) {
203650a69bb5SSascha Wildner 					error("Can't open user config file "
2037ce74bacaSMatthew Dillon 					    "%.100s: %.100s", gl.gl_pathv[i],
2038ce74bacaSMatthew Dillon 					    strerror(errno));
203950a69bb5SSascha Wildner 					globfree(&gl);
204050a69bb5SSascha Wildner 					goto out;
2041ce74bacaSMatthew Dillon 				}
2042e9778795SPeter Avalos 				/*
2043e9778795SPeter Avalos 				 * don't let Match in includes clobber the
2044e9778795SPeter Avalos 				 * containing file's Match state.
2045e9778795SPeter Avalos 				 */
2046e9778795SPeter Avalos 				*activep = oactive;
2047e9778795SPeter Avalos 				if (r != 1)
2048e9778795SPeter Avalos 					value = -1;
2049e9778795SPeter Avalos 			}
2050e9778795SPeter Avalos 			globfree(&gl);
2051e9778795SPeter Avalos 		}
2052e9778795SPeter Avalos 		if (value != 0)
205350a69bb5SSascha Wildner 			ret = value;
2054e9778795SPeter Avalos 		break;
2055e9778795SPeter Avalos 
20569f304aafSPeter Avalos 	case oIPQoS:
205750a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
205850a69bb5SSascha Wildner 		if ((value = parse_ipqos(arg)) == -1) {
205950a69bb5SSascha Wildner 			error("%s line %d: Bad IPQoS value: %s",
20609f304aafSPeter Avalos 			    filename, linenum, arg);
206150a69bb5SSascha Wildner 			goto out;
206250a69bb5SSascha Wildner 		}
206350a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
20649f304aafSPeter Avalos 		if (arg == NULL)
20659f304aafSPeter Avalos 			value2 = value;
206650a69bb5SSascha Wildner 		else if ((value2 = parse_ipqos(arg)) == -1) {
206750a69bb5SSascha Wildner 			error("%s line %d: Bad IPQoS value: %s",
20689f304aafSPeter Avalos 			    filename, linenum, arg);
206950a69bb5SSascha Wildner 			goto out;
207050a69bb5SSascha Wildner 		}
207150a69bb5SSascha Wildner 		if (*activep && options->ip_qos_interactive == -1) {
20729f304aafSPeter Avalos 			options->ip_qos_interactive = value;
20739f304aafSPeter Avalos 			options->ip_qos_bulk = value2;
20749f304aafSPeter Avalos 		}
20759f304aafSPeter Avalos 		break;
20769f304aafSPeter Avalos 
20771c188a7fSPeter Avalos 	case oRequestTTY:
207836e94dc5SPeter Avalos 		intptr = &options->request_tty;
207936e94dc5SPeter Avalos 		multistate_ptr = multistate_requesttty;
208036e94dc5SPeter Avalos 		goto parse_multistate;
208136e94dc5SPeter Avalos 
208250a69bb5SSascha Wildner 	case oSessionType:
208350a69bb5SSascha Wildner 		intptr = &options->session_type;
208450a69bb5SSascha Wildner 		multistate_ptr = multistate_sessiontype;
208550a69bb5SSascha Wildner 		goto parse_multistate;
208650a69bb5SSascha Wildner 
208750a69bb5SSascha Wildner 	case oStdinNull:
208850a69bb5SSascha Wildner 		intptr = &options->stdin_null;
208950a69bb5SSascha Wildner 		goto parse_flag;
209050a69bb5SSascha Wildner 
209150a69bb5SSascha Wildner 	case oForkAfterAuthentication:
209250a69bb5SSascha Wildner 		intptr = &options->fork_after_authentication;
209350a69bb5SSascha Wildner 		goto parse_flag;
209450a69bb5SSascha Wildner 
209536e94dc5SPeter Avalos 	case oIgnoreUnknown:
209636e94dc5SPeter Avalos 		charptr = &options->ignored_unknown;
209736e94dc5SPeter Avalos 		goto parse_string;
209836e94dc5SPeter Avalos 
209936e94dc5SPeter Avalos 	case oProxyUseFdpass:
210036e94dc5SPeter Avalos 		intptr = &options->proxy_use_fdpass;
210136e94dc5SPeter Avalos 		goto parse_flag;
210236e94dc5SPeter Avalos 
210336e94dc5SPeter Avalos 	case oCanonicalDomains:
2104*ba1276acSMatthew Dillon 		found = options->num_canonical_domains == 0;
210550a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
210650a69bb5SSascha Wildner 			/* Allow "none" only in first position */
210750a69bb5SSascha Wildner 			if (strcasecmp(arg, "none") == 0) {
2108*ba1276acSMatthew Dillon 				if (nstrs > 0 || ac > 0) {
210950a69bb5SSascha Wildner 					error("%s line %d: keyword %s \"none\" "
211050a69bb5SSascha Wildner 					    "argument must appear alone.",
211150a69bb5SSascha Wildner 					    filename, linenum, keyword);
211250a69bb5SSascha Wildner 					goto out;
211350a69bb5SSascha Wildner 				}
211450a69bb5SSascha Wildner 			}
2115664f4763Szrj 			if (!valid_domain(arg, 1, &errstr)) {
211650a69bb5SSascha Wildner 				error("%s line %d: %s", filename, linenum,
2117664f4763Szrj 				    errstr);
211850a69bb5SSascha Wildner 				goto out;
2119664f4763Szrj 			}
2120*ba1276acSMatthew Dillon 			opt_array_append(filename, linenum, keyword,
2121*ba1276acSMatthew Dillon 			    &strs, &nstrs, arg);
212250a69bb5SSascha Wildner 		}
2123*ba1276acSMatthew Dillon 		if (nstrs == 0) {
2124*ba1276acSMatthew Dillon 			fatal("%s line %d: no %s specified",
2125*ba1276acSMatthew Dillon 			    filename, linenum, keyword);
2126*ba1276acSMatthew Dillon 		}
2127*ba1276acSMatthew Dillon 		if (found && *activep) {
2128*ba1276acSMatthew Dillon 			options->canonical_domains = strs;
2129*ba1276acSMatthew Dillon 			options->num_canonical_domains = nstrs;
2130*ba1276acSMatthew Dillon 			strs = NULL; /* transferred */
2131*ba1276acSMatthew Dillon 			nstrs = 0;
213236e94dc5SPeter Avalos 		}
213336e94dc5SPeter Avalos 		break;
213436e94dc5SPeter Avalos 
213536e94dc5SPeter Avalos 	case oCanonicalizePermittedCNAMEs:
2136*ba1276acSMatthew Dillon 		found = options->num_permitted_cnames == 0;
213750a69bb5SSascha Wildner 		while ((arg = argv_next(&ac, &av)) != NULL) {
213850a69bb5SSascha Wildner 			/*
213950a69bb5SSascha Wildner 			 * Either 'none' (only in first position), '*' for
214050a69bb5SSascha Wildner 			 * everything or 'list:list'
214150a69bb5SSascha Wildner 			 */
214250a69bb5SSascha Wildner 			if (strcasecmp(arg, "none") == 0) {
2143*ba1276acSMatthew Dillon 				if (ncnames > 0 || ac > 0) {
214450a69bb5SSascha Wildner 					error("%s line %d: keyword %s \"none\" "
214550a69bb5SSascha Wildner 					    "argument must appear alone.",
214650a69bb5SSascha Wildner 					    filename, linenum, keyword);
214750a69bb5SSascha Wildner 					goto out;
214850a69bb5SSascha Wildner 				}
214950a69bb5SSascha Wildner 				arg2 = "";
215050a69bb5SSascha Wildner 			} else if (strcmp(arg, "*") == 0) {
215136e94dc5SPeter Avalos 				arg2 = arg;
215250a69bb5SSascha Wildner 			} else {
215336e94dc5SPeter Avalos 				lowercase(arg);
215436e94dc5SPeter Avalos 				if ((arg2 = strchr(arg, ':')) == NULL ||
215536e94dc5SPeter Avalos 				    arg2[1] == '\0') {
215650a69bb5SSascha Wildner 					error("%s line %d: "
215736e94dc5SPeter Avalos 					    "Invalid permitted CNAME \"%s\"",
215836e94dc5SPeter Avalos 					    filename, linenum, arg);
215950a69bb5SSascha Wildner 					goto out;
216036e94dc5SPeter Avalos 				}
216136e94dc5SPeter Avalos 				*arg2 = '\0';
216236e94dc5SPeter Avalos 				arg2++;
216336e94dc5SPeter Avalos 			}
2164*ba1276acSMatthew Dillon 			cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
2165*ba1276acSMatthew Dillon 			    sizeof(*cnames));
2166*ba1276acSMatthew Dillon 			cnames[ncnames].source_list = xstrdup(arg);
2167*ba1276acSMatthew Dillon 			cnames[ncnames].target_list = xstrdup(arg2);
2168*ba1276acSMatthew Dillon 			ncnames++;
216950a69bb5SSascha Wildner 		}
2170*ba1276acSMatthew Dillon 		if (ncnames == 0) {
2171*ba1276acSMatthew Dillon 			fatal("%s line %d: no %s specified",
2172*ba1276acSMatthew Dillon 			    filename, linenum, keyword);
217336e94dc5SPeter Avalos 		}
2174*ba1276acSMatthew Dillon 		if (found && *activep) {
2175*ba1276acSMatthew Dillon 			options->permitted_cnames = cnames;
2176*ba1276acSMatthew Dillon 			options->num_permitted_cnames = ncnames;
2177*ba1276acSMatthew Dillon 			cnames = NULL; /* transferred */
2178*ba1276acSMatthew Dillon 			ncnames = 0;
2179*ba1276acSMatthew Dillon 		}
2180*ba1276acSMatthew Dillon 		/* un-transferred cnames is cleaned up before exit */
218136e94dc5SPeter Avalos 		break;
218236e94dc5SPeter Avalos 
218336e94dc5SPeter Avalos 	case oCanonicalizeHostname:
218436e94dc5SPeter Avalos 		intptr = &options->canonicalize_hostname;
218536e94dc5SPeter Avalos 		multistate_ptr = multistate_canonicalizehostname;
218636e94dc5SPeter Avalos 		goto parse_multistate;
218736e94dc5SPeter Avalos 
218836e94dc5SPeter Avalos 	case oCanonicalizeMaxDots:
218936e94dc5SPeter Avalos 		intptr = &options->canonicalize_max_dots;
219036e94dc5SPeter Avalos 		goto parse_int;
219136e94dc5SPeter Avalos 
219236e94dc5SPeter Avalos 	case oCanonicalizeFallbackLocal:
219336e94dc5SPeter Avalos 		intptr = &options->canonicalize_fallback_local;
219436e94dc5SPeter Avalos 		goto parse_flag;
219536e94dc5SPeter Avalos 
219636e94dc5SPeter Avalos 	case oStreamLocalBindMask:
219750a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
219850a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
219950a69bb5SSascha Wildner 			error("%.200s line %d: Missing StreamLocalBindMask "
220050a69bb5SSascha Wildner 			    "argument.", filename, linenum);
220150a69bb5SSascha Wildner 			goto out;
220250a69bb5SSascha Wildner 		}
220336e94dc5SPeter Avalos 		/* Parse mode in octal format */
220436e94dc5SPeter Avalos 		value = strtol(arg, &endofnumber, 8);
220550a69bb5SSascha Wildner 		if (arg == endofnumber || value < 0 || value > 0777) {
220650a69bb5SSascha Wildner 			error("%.200s line %d: Bad mask.", filename, linenum);
220750a69bb5SSascha Wildner 			goto out;
220850a69bb5SSascha Wildner 		}
220936e94dc5SPeter Avalos 		options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
22101c188a7fSPeter Avalos 		break;
22111c188a7fSPeter Avalos 
221236e94dc5SPeter Avalos 	case oStreamLocalBindUnlink:
221336e94dc5SPeter Avalos 		intptr = &options->fwd_opts.streamlocal_bind_unlink;
221436e94dc5SPeter Avalos 		goto parse_flag;
221536e94dc5SPeter Avalos 
2216e9778795SPeter Avalos 	case oRevokedHostKeys:
2217e9778795SPeter Avalos 		charptr = &options->revoked_host_keys;
2218e9778795SPeter Avalos 		goto parse_string;
2219e9778795SPeter Avalos 
2220e9778795SPeter Avalos 	case oFingerprintHash:
2221e9778795SPeter Avalos 		intptr = &options->fingerprint_hash;
222250a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
222350a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
222450a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
2225e9778795SPeter Avalos 			    filename, linenum);
222650a69bb5SSascha Wildner 			goto out;
222750a69bb5SSascha Wildner 		}
222850a69bb5SSascha Wildner 		if ((value = ssh_digest_alg_by_name(arg)) == -1) {
222950a69bb5SSascha Wildner 			error("%.200s line %d: Invalid hash algorithm \"%s\".",
2230e9778795SPeter Avalos 			    filename, linenum, arg);
223150a69bb5SSascha Wildner 			goto out;
223250a69bb5SSascha Wildner 		}
2233e9778795SPeter Avalos 		if (*activep && *intptr == -1)
2234e9778795SPeter Avalos 			*intptr = value;
2235e9778795SPeter Avalos 		break;
2236e9778795SPeter Avalos 
2237e9778795SPeter Avalos 	case oUpdateHostkeys:
2238e9778795SPeter Avalos 		intptr = &options->update_hostkeys;
2239e9778795SPeter Avalos 		multistate_ptr = multistate_yesnoask;
2240e9778795SPeter Avalos 		goto parse_multistate;
2241e9778795SPeter Avalos 
224250a69bb5SSascha Wildner 	case oHostbasedAcceptedAlgorithms:
224350a69bb5SSascha Wildner 		charptr = &options->hostbased_accepted_algos;
2244*ba1276acSMatthew Dillon 		ca_only = 0;
224550a69bb5SSascha Wildner 		goto parse_pubkey_algos;
2246e9778795SPeter Avalos 
224750a69bb5SSascha Wildner 	case oPubkeyAcceptedAlgorithms:
224850a69bb5SSascha Wildner 		charptr = &options->pubkey_accepted_algos;
2249*ba1276acSMatthew Dillon 		ca_only = 0;
225050a69bb5SSascha Wildner 		goto parse_pubkey_algos;
2251e9778795SPeter Avalos 
2252e9778795SPeter Avalos 	case oAddKeysToAgent:
225350a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
225450a69bb5SSascha Wildner 		arg2 = argv_next(&ac, &av);
225550a69bb5SSascha Wildner 		value = parse_multistate_value(arg, filename, linenum,
225650a69bb5SSascha Wildner 		    multistate_yesnoaskconfirm);
225750a69bb5SSascha Wildner 		value2 = 0; /* unlimited lifespan by default */
225850a69bb5SSascha Wildner 		if (value == 3 && arg2 != NULL) {
225950a69bb5SSascha Wildner 			/* allow "AddKeysToAgent confirm 5m" */
2260*ba1276acSMatthew Dillon 			if ((value2 = convtime(arg2)) == -1) {
226150a69bb5SSascha Wildner 				error("%s line %d: invalid time value.",
226250a69bb5SSascha Wildner 				    filename, linenum);
226350a69bb5SSascha Wildner 				goto out;
226450a69bb5SSascha Wildner 			}
226550a69bb5SSascha Wildner 		} else if (value == -1 && arg2 == NULL) {
2266*ba1276acSMatthew Dillon 			if ((value2 = convtime(arg)) == -1) {
226750a69bb5SSascha Wildner 				error("%s line %d: unsupported option",
226850a69bb5SSascha Wildner 				    filename, linenum);
226950a69bb5SSascha Wildner 				goto out;
227050a69bb5SSascha Wildner 			}
227150a69bb5SSascha Wildner 			value = 1; /* yes */
227250a69bb5SSascha Wildner 		} else if (value == -1 || arg2 != NULL) {
227350a69bb5SSascha Wildner 			error("%s line %d: unsupported option",
227450a69bb5SSascha Wildner 			    filename, linenum);
227550a69bb5SSascha Wildner 			goto out;
227650a69bb5SSascha Wildner 		}
227750a69bb5SSascha Wildner 		if (*activep && options->add_keys_to_agent == -1) {
227850a69bb5SSascha Wildner 			options->add_keys_to_agent = value;
227950a69bb5SSascha Wildner 			options->add_keys_to_agent_lifespan = value2;
228050a69bb5SSascha Wildner 		}
228150a69bb5SSascha Wildner 		break;
2282e9778795SPeter Avalos 
2283e9778795SPeter Avalos 	case oIdentityAgent:
2284e9778795SPeter Avalos 		charptr = &options->identity_agent;
228550a69bb5SSascha Wildner 		arg = argv_next(&ac, &av);
228650a69bb5SSascha Wildner 		if (!arg || *arg == '\0') {
228750a69bb5SSascha Wildner 			error("%.200s line %d: Missing argument.",
2288664f4763Szrj 			    filename, linenum);
228950a69bb5SSascha Wildner 			goto out;
229050a69bb5SSascha Wildner 		}
22910cbfa66cSDaniel Fojt   parse_agent_path:
2292664f4763Szrj 		/* Extra validation if the string represents an env var. */
229350a69bb5SSascha Wildner 		if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
229450a69bb5SSascha Wildner 			error("%.200s line %d: Invalid environment expansion "
229550a69bb5SSascha Wildner 			    "%s.", filename, linenum, arg);
229650a69bb5SSascha Wildner 			goto out;
229750a69bb5SSascha Wildner 		}
229850a69bb5SSascha Wildner 		free(arg2);
229950a69bb5SSascha Wildner 		/* check for legacy environment format */
230050a69bb5SSascha Wildner 		if (arg[0] == '$' && arg[1] != '{' &&
230150a69bb5SSascha Wildner 		    !valid_env_name(arg + 1)) {
230250a69bb5SSascha Wildner 			error("%.200s line %d: Invalid environment name %s.",
2303664f4763Szrj 			    filename, linenum, arg);
230450a69bb5SSascha Wildner 			goto out;
2305664f4763Szrj 		}
2306664f4763Szrj 		if (*activep && *charptr == NULL)
2307664f4763Szrj 			*charptr = xstrdup(arg);
2308664f4763Szrj 		break;
2309e9778795SPeter Avalos 
2310*ba1276acSMatthew Dillon 	case oEnableEscapeCommandline:
2311*ba1276acSMatthew Dillon 		intptr = &options->enable_escape_commandline;
2312*ba1276acSMatthew Dillon 		goto parse_flag;
2313*ba1276acSMatthew Dillon 
2314ee116499SAntonio Huete Jimenez 	case oRequiredRSASize:
2315ee116499SAntonio Huete Jimenez 		intptr = &options->required_rsa_size;
2316ee116499SAntonio Huete Jimenez 		goto parse_int;
2317ee116499SAntonio Huete Jimenez 
2318*ba1276acSMatthew Dillon 	case oObscureKeystrokeTiming:
2319*ba1276acSMatthew Dillon 		value = -1;
2320*ba1276acSMatthew Dillon 		while ((arg = argv_next(&ac, &av)) != NULL) {
2321*ba1276acSMatthew Dillon 			if (value != -1) {
2322*ba1276acSMatthew Dillon 				error("%s line %d: invalid arguments",
2323*ba1276acSMatthew Dillon 				    filename, linenum);
2324*ba1276acSMatthew Dillon 				goto out;
2325*ba1276acSMatthew Dillon 			}
2326*ba1276acSMatthew Dillon 			if (strcmp(arg, "yes") == 0 ||
2327*ba1276acSMatthew Dillon 			    strcmp(arg, "true") == 0)
2328*ba1276acSMatthew Dillon 				value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2329*ba1276acSMatthew Dillon 			else if (strcmp(arg, "no") == 0 ||
2330*ba1276acSMatthew Dillon 			    strcmp(arg, "false") == 0)
2331*ba1276acSMatthew Dillon 				value = 0;
2332*ba1276acSMatthew Dillon 			else if (strncmp(arg, "interval:", 9) == 0) {
2333*ba1276acSMatthew Dillon 				if ((errstr = atoi_err(arg + 9,
2334*ba1276acSMatthew Dillon 				    &value)) != NULL) {
2335*ba1276acSMatthew Dillon 					error("%s line %d: integer value %s.",
2336*ba1276acSMatthew Dillon 					    filename, linenum, errstr);
2337*ba1276acSMatthew Dillon 					goto out;
2338*ba1276acSMatthew Dillon 				}
2339*ba1276acSMatthew Dillon 				if (value <= 0 || value > 1000) {
2340*ba1276acSMatthew Dillon 					error("%s line %d: value out of range.",
2341*ba1276acSMatthew Dillon 					    filename, linenum);
2342*ba1276acSMatthew Dillon 					goto out;
2343*ba1276acSMatthew Dillon 				}
2344*ba1276acSMatthew Dillon 			} else {
2345*ba1276acSMatthew Dillon 				error("%s line %d: unsupported argument \"%s\"",
2346*ba1276acSMatthew Dillon 				    filename, linenum, arg);
2347*ba1276acSMatthew Dillon 				goto out;
2348*ba1276acSMatthew Dillon 			}
2349*ba1276acSMatthew Dillon 		}
2350*ba1276acSMatthew Dillon 		if (value == -1) {
2351*ba1276acSMatthew Dillon 			error("%s line %d: missing argument",
2352*ba1276acSMatthew Dillon 			    filename, linenum);
2353*ba1276acSMatthew Dillon 			goto out;
2354*ba1276acSMatthew Dillon 		}
2355*ba1276acSMatthew Dillon 		intptr = &options->obscure_keystroke_timing_interval;
2356*ba1276acSMatthew Dillon 		if (*activep && *intptr == -1)
2357*ba1276acSMatthew Dillon 			*intptr = value;
2358*ba1276acSMatthew Dillon 		break;
2359*ba1276acSMatthew Dillon 
2360*ba1276acSMatthew Dillon 	case oChannelTimeout:
2361*ba1276acSMatthew Dillon 		found = options->num_channel_timeouts == 0;
2362*ba1276acSMatthew Dillon 		while ((arg = argv_next(&ac, &av)) != NULL) {
2363*ba1276acSMatthew Dillon 			/* Allow "none" only in first position */
2364*ba1276acSMatthew Dillon 			if (strcasecmp(arg, "none") == 0) {
2365*ba1276acSMatthew Dillon 				if (nstrs > 0 || ac > 0) {
2366*ba1276acSMatthew Dillon 					error("%s line %d: keyword %s \"none\" "
2367*ba1276acSMatthew Dillon 					    "argument must appear alone.",
2368*ba1276acSMatthew Dillon 					    filename, linenum, keyword);
2369*ba1276acSMatthew Dillon 					goto out;
2370*ba1276acSMatthew Dillon 				}
2371*ba1276acSMatthew Dillon 			} else if (parse_pattern_interval(arg,
2372*ba1276acSMatthew Dillon 			    NULL, NULL) != 0) {
2373*ba1276acSMatthew Dillon 				fatal("%s line %d: invalid channel timeout %s",
2374*ba1276acSMatthew Dillon 				    filename, linenum, arg);
2375*ba1276acSMatthew Dillon 			}
2376*ba1276acSMatthew Dillon 			opt_array_append(filename, linenum, keyword,
2377*ba1276acSMatthew Dillon 			    &strs, &nstrs, arg);
2378*ba1276acSMatthew Dillon 		}
2379*ba1276acSMatthew Dillon 		if (nstrs == 0) {
2380*ba1276acSMatthew Dillon 			fatal("%s line %d: no %s specified",
2381*ba1276acSMatthew Dillon 			    filename, linenum, keyword);
2382*ba1276acSMatthew Dillon 		}
2383*ba1276acSMatthew Dillon 		if (found && *activep) {
2384*ba1276acSMatthew Dillon 			options->channel_timeouts = strs;
2385*ba1276acSMatthew Dillon 			options->num_channel_timeouts = nstrs;
2386*ba1276acSMatthew Dillon 			strs = NULL; /* transferred */
2387*ba1276acSMatthew Dillon 			nstrs = 0;
2388*ba1276acSMatthew Dillon 		}
2389*ba1276acSMatthew Dillon 		break;
2390*ba1276acSMatthew Dillon 
239118de8d7fSPeter Avalos 	case oDeprecated:
239218de8d7fSPeter Avalos 		debug("%s line %d: Deprecated option \"%s\"",
239318de8d7fSPeter Avalos 		    filename, linenum, keyword);
239450a69bb5SSascha Wildner 		argv_consume(&ac);
239550a69bb5SSascha Wildner 		break;
239618de8d7fSPeter Avalos 
239718de8d7fSPeter Avalos 	case oUnsupported:
239818de8d7fSPeter Avalos 		error("%s line %d: Unsupported option \"%s\"",
239918de8d7fSPeter Avalos 		    filename, linenum, keyword);
240050a69bb5SSascha Wildner 		argv_consume(&ac);
240150a69bb5SSascha Wildner 		break;
240218de8d7fSPeter Avalos 
240318de8d7fSPeter Avalos 	default:
240450a69bb5SSascha Wildner 		error("%s line %d: Unimplemented opcode %d",
240550a69bb5SSascha Wildner 		    filename, linenum, opcode);
240650a69bb5SSascha Wildner 		goto out;
240718de8d7fSPeter Avalos 	}
240818de8d7fSPeter Avalos 
240918de8d7fSPeter Avalos 	/* Check that there is no garbage at end of line. */
241050a69bb5SSascha Wildner 	if (ac > 0) {
241150a69bb5SSascha Wildner 		error("%.200s line %d: keyword %s extra arguments "
241250a69bb5SSascha Wildner 		    "at end of line", filename, linenum, keyword);
241350a69bb5SSascha Wildner 		goto out;
241418de8d7fSPeter Avalos 	}
241550a69bb5SSascha Wildner 
241650a69bb5SSascha Wildner 	/* success */
241750a69bb5SSascha Wildner 	ret = 0;
241850a69bb5SSascha Wildner  out:
2419*ba1276acSMatthew Dillon 	free_canon_cnames(cnames, ncnames);
2420*ba1276acSMatthew Dillon 	opt_array_free2(strs, NULL, nstrs);
242150a69bb5SSascha Wildner 	argv_free(oav, oac);
242250a69bb5SSascha Wildner 	return ret;
242318de8d7fSPeter Avalos }
242418de8d7fSPeter Avalos 
242518de8d7fSPeter Avalos /*
242618de8d7fSPeter Avalos  * Reads the config file and modifies the options accordingly.  Options
242718de8d7fSPeter Avalos  * should already be initialized before this call.  This never returns if
242818de8d7fSPeter Avalos  * there is an error.  If the file does not exist, this returns 0.
242918de8d7fSPeter Avalos  */
243018de8d7fSPeter Avalos int
read_config_file(const char * filename,struct passwd * pw,const char * host,const char * original_host,Options * options,int flags,int * want_final_pass)243136e94dc5SPeter Avalos read_config_file(const char *filename, struct passwd *pw, const char *host,
2432664f4763Szrj     const char *original_host, Options *options, int flags,
2433664f4763Szrj     int *want_final_pass)
2434e9778795SPeter Avalos {
2435e9778795SPeter Avalos 	int active = 1;
2436e9778795SPeter Avalos 
2437e9778795SPeter Avalos 	return read_config_file_depth(filename, pw, host, original_host,
2438664f4763Szrj 	    options, flags, &active, want_final_pass, 0);
2439e9778795SPeter Avalos }
2440e9778795SPeter Avalos 
2441e9778795SPeter Avalos #define READCONF_MAX_DEPTH	16
2442e9778795SPeter Avalos static int
read_config_file_depth(const char * filename,struct passwd * pw,const char * host,const char * original_host,Options * options,int flags,int * activep,int * want_final_pass,int depth)2443e9778795SPeter Avalos read_config_file_depth(const char *filename, struct passwd *pw,
2444e9778795SPeter Avalos     const char *host, const char *original_host, Options *options,
2445664f4763Szrj     int flags, int *activep, int *want_final_pass, int depth)
244618de8d7fSPeter Avalos {
244718de8d7fSPeter Avalos 	FILE *f;
2448664f4763Szrj 	char *line = NULL;
2449664f4763Szrj 	size_t linesize = 0;
2450e9778795SPeter Avalos 	int linenum;
245118de8d7fSPeter Avalos 	int bad_options = 0;
245218de8d7fSPeter Avalos 
2453e9778795SPeter Avalos 	if (depth < 0 || depth > READCONF_MAX_DEPTH)
2454e9778795SPeter Avalos 		fatal("Too many recursive configuration includes");
2455e9778795SPeter Avalos 
245618de8d7fSPeter Avalos 	if ((f = fopen(filename, "r")) == NULL)
245718de8d7fSPeter Avalos 		return 0;
245818de8d7fSPeter Avalos 
245936e94dc5SPeter Avalos 	if (flags & SSHCONF_CHECKPERM) {
246018de8d7fSPeter Avalos 		struct stat sb;
246118de8d7fSPeter Avalos 
246218de8d7fSPeter Avalos 		if (fstat(fileno(f), &sb) == -1)
246318de8d7fSPeter Avalos 			fatal("fstat %s: %s", filename, strerror(errno));
246418de8d7fSPeter Avalos 		if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
246518de8d7fSPeter Avalos 		    (sb.st_mode & 022) != 0))
246618de8d7fSPeter Avalos 			fatal("Bad owner or permissions on %s", filename);
246718de8d7fSPeter Avalos 	}
246818de8d7fSPeter Avalos 
246918de8d7fSPeter Avalos 	debug("Reading configuration data %.200s", filename);
247018de8d7fSPeter Avalos 
247118de8d7fSPeter Avalos 	/*
247218de8d7fSPeter Avalos 	 * Mark that we are now processing the options.  This flag is turned
247318de8d7fSPeter Avalos 	 * on/off by Host specifications.
247418de8d7fSPeter Avalos 	 */
247518de8d7fSPeter Avalos 	linenum = 0;
2476664f4763Szrj 	while (getline(&line, &linesize, f) != -1) {
247718de8d7fSPeter Avalos 		/* Update line number counter. */
247818de8d7fSPeter Avalos 		linenum++;
247950a69bb5SSascha Wildner 		/*
248050a69bb5SSascha Wildner 		 * Trim out comments and strip whitespace.
248150a69bb5SSascha Wildner 		 * NB - preserve newlines, they are needed to reproduce
248250a69bb5SSascha Wildner 		 * line numbers later for error messages.
248350a69bb5SSascha Wildner 		 */
2484e9778795SPeter Avalos 		if (process_config_line_depth(options, pw, host, original_host,
2485664f4763Szrj 		    line, filename, linenum, activep, flags, want_final_pass,
2486664f4763Szrj 		    depth) != 0)
248718de8d7fSPeter Avalos 			bad_options++;
248818de8d7fSPeter Avalos 	}
2489664f4763Szrj 	free(line);
249018de8d7fSPeter Avalos 	fclose(f);
249118de8d7fSPeter Avalos 	if (bad_options > 0)
249218de8d7fSPeter Avalos 		fatal("%s: terminating, %d bad configuration options",
249318de8d7fSPeter Avalos 		    filename, bad_options);
249418de8d7fSPeter Avalos 	return 1;
249518de8d7fSPeter Avalos }
249618de8d7fSPeter Avalos 
249736e94dc5SPeter Avalos /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
249836e94dc5SPeter Avalos int
option_clear_or_none(const char * o)249936e94dc5SPeter Avalos option_clear_or_none(const char *o)
250036e94dc5SPeter Avalos {
250136e94dc5SPeter Avalos 	return o == NULL || strcasecmp(o, "none") == 0;
250236e94dc5SPeter Avalos }
250336e94dc5SPeter Avalos 
250418de8d7fSPeter Avalos /*
250550a69bb5SSascha Wildner  * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
250650a69bb5SSascha Wildner  * Allowed to be called on non-final configuration.
250750a69bb5SSascha Wildner  */
250850a69bb5SSascha Wildner int
config_has_permitted_cnames(Options * options)250950a69bb5SSascha Wildner config_has_permitted_cnames(Options *options)
251050a69bb5SSascha Wildner {
251150a69bb5SSascha Wildner 	if (options->num_permitted_cnames == 1 &&
251250a69bb5SSascha Wildner 	    strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 &&
251350a69bb5SSascha Wildner 	    strcmp(options->permitted_cnames[0].target_list, "") == 0)
251450a69bb5SSascha Wildner 		return 0;
251550a69bb5SSascha Wildner 	return options->num_permitted_cnames > 0;
251650a69bb5SSascha Wildner }
251750a69bb5SSascha Wildner 
251850a69bb5SSascha Wildner /*
251918de8d7fSPeter Avalos  * Initializes options to special values that indicate that they have not yet
252018de8d7fSPeter Avalos  * been set.  Read_config_file will only set options with this value. Options
252118de8d7fSPeter Avalos  * are processed in the following order: command line, user config file,
252218de8d7fSPeter Avalos  * system config file.  Last, fill_default_options is called.
252318de8d7fSPeter Avalos  */
252418de8d7fSPeter Avalos 
252518de8d7fSPeter Avalos void
initialize_options(Options * options)252618de8d7fSPeter Avalos initialize_options(Options * options)
252718de8d7fSPeter Avalos {
252818de8d7fSPeter Avalos 	memset(options, 'X', sizeof(*options));
2529*ba1276acSMatthew Dillon 	options->host_arg = NULL;
253018de8d7fSPeter Avalos 	options->forward_agent = -1;
25310cbfa66cSDaniel Fojt 	options->forward_agent_sock_path = NULL;
253218de8d7fSPeter Avalos 	options->forward_x11 = -1;
253318de8d7fSPeter Avalos 	options->forward_x11_trusted = -1;
2534856ea928SPeter Avalos 	options->forward_x11_timeout = -1;
2535e9778795SPeter Avalos 	options->stdio_forward_host = NULL;
2536e9778795SPeter Avalos 	options->stdio_forward_port = 0;
2537e9778795SPeter Avalos 	options->clear_forwardings = -1;
253818de8d7fSPeter Avalos 	options->exit_on_forward_failure = -1;
253918de8d7fSPeter Avalos 	options->xauth_location = NULL;
254036e94dc5SPeter Avalos 	options->fwd_opts.gateway_ports = -1;
254136e94dc5SPeter Avalos 	options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
254236e94dc5SPeter Avalos 	options->fwd_opts.streamlocal_bind_unlink = -1;
254318de8d7fSPeter Avalos 	options->pubkey_authentication = -1;
254418de8d7fSPeter Avalos 	options->gss_authentication = -1;
254518de8d7fSPeter Avalos 	options->gss_deleg_creds = -1;
254618de8d7fSPeter Avalos 	options->password_authentication = -1;
254718de8d7fSPeter Avalos 	options->kbd_interactive_authentication = -1;
254818de8d7fSPeter Avalos 	options->kbd_interactive_devices = NULL;
254918de8d7fSPeter Avalos 	options->hostbased_authentication = -1;
255018de8d7fSPeter Avalos 	options->batch_mode = -1;
255118de8d7fSPeter Avalos 	options->check_host_ip = -1;
255218de8d7fSPeter Avalos 	options->strict_host_key_checking = -1;
255318de8d7fSPeter Avalos 	options->compression = -1;
255418de8d7fSPeter Avalos 	options->tcp_keep_alive = -1;
255518de8d7fSPeter Avalos 	options->port = -1;
255618de8d7fSPeter Avalos 	options->address_family = -1;
255718de8d7fSPeter Avalos 	options->connection_attempts = -1;
255818de8d7fSPeter Avalos 	options->connection_timeout = -1;
255918de8d7fSPeter Avalos 	options->number_of_password_prompts = -1;
256018de8d7fSPeter Avalos 	options->ciphers = NULL;
256118de8d7fSPeter Avalos 	options->macs = NULL;
25629f304aafSPeter Avalos 	options->kex_algorithms = NULL;
256318de8d7fSPeter Avalos 	options->hostkeyalgorithms = NULL;
2564664f4763Szrj 	options->ca_sign_algorithms = NULL;
256518de8d7fSPeter Avalos 	options->num_identity_files = 0;
256650a69bb5SSascha Wildner 	memset(options->identity_keys, 0, sizeof(options->identity_keys));
2567e9778795SPeter Avalos 	options->num_certificate_files = 0;
256850a69bb5SSascha Wildner 	memset(options->certificates, 0, sizeof(options->certificates));
256918de8d7fSPeter Avalos 	options->hostname = NULL;
257018de8d7fSPeter Avalos 	options->host_key_alias = NULL;
257118de8d7fSPeter Avalos 	options->proxy_command = NULL;
2572e9778795SPeter Avalos 	options->jump_user = NULL;
2573e9778795SPeter Avalos 	options->jump_host = NULL;
2574e9778795SPeter Avalos 	options->jump_port = -1;
2575e9778795SPeter Avalos 	options->jump_extra = NULL;
257618de8d7fSPeter Avalos 	options->user = NULL;
257718de8d7fSPeter Avalos 	options->escape_char = -1;
25781c188a7fSPeter Avalos 	options->num_system_hostfiles = 0;
25791c188a7fSPeter Avalos 	options->num_user_hostfiles = 0;
2580856ea928SPeter Avalos 	options->local_forwards = NULL;
258118de8d7fSPeter Avalos 	options->num_local_forwards = 0;
2582856ea928SPeter Avalos 	options->remote_forwards = NULL;
258318de8d7fSPeter Avalos 	options->num_remote_forwards = 0;
258450a69bb5SSascha Wildner 	options->permitted_remote_opens = NULL;
258550a69bb5SSascha Wildner 	options->num_permitted_remote_opens = 0;
2586ce74bacaSMatthew Dillon 	options->log_facility = SYSLOG_FACILITY_NOT_SET;
258718de8d7fSPeter Avalos 	options->log_level = SYSLOG_LEVEL_NOT_SET;
258850a69bb5SSascha Wildner 	options->num_log_verbose = 0;
258950a69bb5SSascha Wildner 	options->log_verbose = NULL;
259018de8d7fSPeter Avalos 	options->preferred_authentications = NULL;
259118de8d7fSPeter Avalos 	options->bind_address = NULL;
2592664f4763Szrj 	options->bind_interface = NULL;
2593856ea928SPeter Avalos 	options->pkcs11_provider = NULL;
25940cbfa66cSDaniel Fojt 	options->sk_provider = NULL;
259518de8d7fSPeter Avalos 	options->enable_ssh_keysign = - 1;
259618de8d7fSPeter Avalos 	options->no_host_authentication_for_localhost = - 1;
259718de8d7fSPeter Avalos 	options->identities_only = - 1;
259818de8d7fSPeter Avalos 	options->rekey_limit = - 1;
259936e94dc5SPeter Avalos 	options->rekey_interval = -1;
260018de8d7fSPeter Avalos 	options->verify_host_key_dns = -1;
260118de8d7fSPeter Avalos 	options->server_alive_interval = -1;
260218de8d7fSPeter Avalos 	options->server_alive_count_max = -1;
2603664f4763Szrj 	options->send_env = NULL;
260418de8d7fSPeter Avalos 	options->num_send_env = 0;
2605664f4763Szrj 	options->setenv = NULL;
2606664f4763Szrj 	options->num_setenv = 0;
260718de8d7fSPeter Avalos 	options->control_path = NULL;
260818de8d7fSPeter Avalos 	options->control_master = -1;
2609856ea928SPeter Avalos 	options->control_persist = -1;
2610856ea928SPeter Avalos 	options->control_persist_timeout = 0;
261118de8d7fSPeter Avalos 	options->hash_known_hosts = -1;
261218de8d7fSPeter Avalos 	options->tun_open = -1;
261318de8d7fSPeter Avalos 	options->tun_local = -1;
261418de8d7fSPeter Avalos 	options->tun_remote = -1;
261518de8d7fSPeter Avalos 	options->local_command = NULL;
261618de8d7fSPeter Avalos 	options->permit_local_command = -1;
2617ce74bacaSMatthew Dillon 	options->remote_command = NULL;
2618e9778795SPeter Avalos 	options->add_keys_to_agent = -1;
261950a69bb5SSascha Wildner 	options->add_keys_to_agent_lifespan = -1;
2620e9778795SPeter Avalos 	options->identity_agent = NULL;
262118de8d7fSPeter Avalos 	options->visual_host_key = -1;
26229f304aafSPeter Avalos 	options->ip_qos_interactive = -1;
26239f304aafSPeter Avalos 	options->ip_qos_bulk = -1;
26241c188a7fSPeter Avalos 	options->request_tty = -1;
262550a69bb5SSascha Wildner 	options->session_type = -1;
262650a69bb5SSascha Wildner 	options->stdin_null = -1;
262750a69bb5SSascha Wildner 	options->fork_after_authentication = -1;
262836e94dc5SPeter Avalos 	options->proxy_use_fdpass = -1;
262936e94dc5SPeter Avalos 	options->ignored_unknown = NULL;
263036e94dc5SPeter Avalos 	options->num_canonical_domains = 0;
263136e94dc5SPeter Avalos 	options->num_permitted_cnames = 0;
263236e94dc5SPeter Avalos 	options->canonicalize_max_dots = -1;
263336e94dc5SPeter Avalos 	options->canonicalize_fallback_local = -1;
263436e94dc5SPeter Avalos 	options->canonicalize_hostname = -1;
2635e9778795SPeter Avalos 	options->revoked_host_keys = NULL;
2636e9778795SPeter Avalos 	options->fingerprint_hash = -1;
2637e9778795SPeter Avalos 	options->update_hostkeys = -1;
263850a69bb5SSascha Wildner 	options->hostbased_accepted_algos = NULL;
263950a69bb5SSascha Wildner 	options->pubkey_accepted_algos = NULL;
264050a69bb5SSascha Wildner 	options->known_hosts_command = NULL;
2641ee116499SAntonio Huete Jimenez 	options->required_rsa_size = -1;
2642*ba1276acSMatthew Dillon 	options->enable_escape_commandline = -1;
2643*ba1276acSMatthew Dillon 	options->obscure_keystroke_timing_interval = -1;
2644*ba1276acSMatthew Dillon 	options->tag = NULL;
2645*ba1276acSMatthew Dillon 	options->channel_timeouts = NULL;
2646*ba1276acSMatthew Dillon 	options->num_channel_timeouts = 0;
264736e94dc5SPeter Avalos }
264836e94dc5SPeter Avalos 
264936e94dc5SPeter Avalos /*
265036e94dc5SPeter Avalos  * A petite version of fill_default_options() that just fills the options
265136e94dc5SPeter Avalos  * needed for hostname canonicalization to proceed.
265236e94dc5SPeter Avalos  */
265336e94dc5SPeter Avalos void
fill_default_options_for_canonicalization(Options * options)265436e94dc5SPeter Avalos fill_default_options_for_canonicalization(Options *options)
265536e94dc5SPeter Avalos {
265636e94dc5SPeter Avalos 	if (options->canonicalize_max_dots == -1)
265736e94dc5SPeter Avalos 		options->canonicalize_max_dots = 1;
265836e94dc5SPeter Avalos 	if (options->canonicalize_fallback_local == -1)
265936e94dc5SPeter Avalos 		options->canonicalize_fallback_local = 1;
266036e94dc5SPeter Avalos 	if (options->canonicalize_hostname == -1)
266136e94dc5SPeter Avalos 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
266218de8d7fSPeter Avalos }
266318de8d7fSPeter Avalos 
266418de8d7fSPeter Avalos /*
266518de8d7fSPeter Avalos  * Called after processing other sources of option data, this fills those
266618de8d7fSPeter Avalos  * options for which no value has been specified with their default values.
266718de8d7fSPeter Avalos  */
266850a69bb5SSascha Wildner int
fill_default_options(Options * options)266918de8d7fSPeter Avalos fill_default_options(Options * options)
267018de8d7fSPeter Avalos {
2671664f4763Szrj 	char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig;
26720cbfa66cSDaniel Fojt 	char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig;
267350a69bb5SSascha Wildner 	int ret = 0, r;
2674664f4763Szrj 
267518de8d7fSPeter Avalos 	if (options->forward_agent == -1)
267618de8d7fSPeter Avalos 		options->forward_agent = 0;
267718de8d7fSPeter Avalos 	if (options->forward_x11 == -1)
267818de8d7fSPeter Avalos 		options->forward_x11 = 0;
267918de8d7fSPeter Avalos 	if (options->forward_x11_trusted == -1)
268018de8d7fSPeter Avalos 		options->forward_x11_trusted = 0;
2681856ea928SPeter Avalos 	if (options->forward_x11_timeout == -1)
2682856ea928SPeter Avalos 		options->forward_x11_timeout = 1200;
2683e9778795SPeter Avalos 	/*
2684e9778795SPeter Avalos 	 * stdio forwarding (-W) changes the default for these but we defer
2685e9778795SPeter Avalos 	 * setting the values so they can be overridden.
2686e9778795SPeter Avalos 	 */
268718de8d7fSPeter Avalos 	if (options->exit_on_forward_failure == -1)
2688e9778795SPeter Avalos 		options->exit_on_forward_failure =
2689e9778795SPeter Avalos 		    options->stdio_forward_host != NULL ? 1 : 0;
2690e9778795SPeter Avalos 	if (options->clear_forwardings == -1)
2691e9778795SPeter Avalos 		options->clear_forwardings =
2692e9778795SPeter Avalos 		    options->stdio_forward_host != NULL ? 1 : 0;
2693e9778795SPeter Avalos 	if (options->clear_forwardings == 1)
2694e9778795SPeter Avalos 		clear_forwardings(options);
2695e9778795SPeter Avalos 
269618de8d7fSPeter Avalos 	if (options->xauth_location == NULL)
269750a69bb5SSascha Wildner 		options->xauth_location = xstrdup(_PATH_XAUTH);
269836e94dc5SPeter Avalos 	if (options->fwd_opts.gateway_ports == -1)
269936e94dc5SPeter Avalos 		options->fwd_opts.gateway_ports = 0;
270036e94dc5SPeter Avalos 	if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
270136e94dc5SPeter Avalos 		options->fwd_opts.streamlocal_bind_mask = 0177;
270236e94dc5SPeter Avalos 	if (options->fwd_opts.streamlocal_bind_unlink == -1)
270336e94dc5SPeter Avalos 		options->fwd_opts.streamlocal_bind_unlink = 0;
270418de8d7fSPeter Avalos 	if (options->pubkey_authentication == -1)
2705ee116499SAntonio Huete Jimenez 		options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
270618de8d7fSPeter Avalos 	if (options->gss_authentication == -1)
270718de8d7fSPeter Avalos 		options->gss_authentication = 0;
270818de8d7fSPeter Avalos 	if (options->gss_deleg_creds == -1)
270918de8d7fSPeter Avalos 		options->gss_deleg_creds = 0;
271018de8d7fSPeter Avalos 	if (options->password_authentication == -1)
271118de8d7fSPeter Avalos 		options->password_authentication = 1;
271218de8d7fSPeter Avalos 	if (options->kbd_interactive_authentication == -1)
271318de8d7fSPeter Avalos 		options->kbd_interactive_authentication = 1;
271418de8d7fSPeter Avalos 	if (options->hostbased_authentication == -1)
271518de8d7fSPeter Avalos 		options->hostbased_authentication = 0;
271618de8d7fSPeter Avalos 	if (options->batch_mode == -1)
271718de8d7fSPeter Avalos 		options->batch_mode = 0;
271818de8d7fSPeter Avalos 	if (options->check_host_ip == -1)
271950a69bb5SSascha Wildner 		options->check_host_ip = 0;
272018de8d7fSPeter Avalos 	if (options->strict_host_key_checking == -1)
2721ce74bacaSMatthew Dillon 		options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK;
272218de8d7fSPeter Avalos 	if (options->compression == -1)
272318de8d7fSPeter Avalos 		options->compression = 0;
272418de8d7fSPeter Avalos 	if (options->tcp_keep_alive == -1)
272518de8d7fSPeter Avalos 		options->tcp_keep_alive = 1;
272618de8d7fSPeter Avalos 	if (options->port == -1)
272718de8d7fSPeter Avalos 		options->port = 0;	/* Filled in ssh_connect. */
272818de8d7fSPeter Avalos 	if (options->address_family == -1)
272918de8d7fSPeter Avalos 		options->address_family = AF_UNSPEC;
273018de8d7fSPeter Avalos 	if (options->connection_attempts == -1)
273118de8d7fSPeter Avalos 		options->connection_attempts = 1;
273218de8d7fSPeter Avalos 	if (options->number_of_password_prompts == -1)
273318de8d7fSPeter Avalos 		options->number_of_password_prompts = 3;
273418de8d7fSPeter Avalos 	/* options->hostkeyalgorithms, default set in myproposals.h */
273550a69bb5SSascha Wildner 	if (options->add_keys_to_agent == -1) {
2736e9778795SPeter Avalos 		options->add_keys_to_agent = 0;
273750a69bb5SSascha Wildner 		options->add_keys_to_agent_lifespan = 0;
273850a69bb5SSascha Wildner 	}
273918de8d7fSPeter Avalos 	if (options->num_identity_files == 0) {
2740ce74bacaSMatthew Dillon 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0);
27419f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
2742ce74bacaSMatthew Dillon 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
27430cbfa66cSDaniel Fojt 		add_identity_file(options, "~/",
27440cbfa66cSDaniel Fojt 		    _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
27459f304aafSPeter Avalos #endif
274636e94dc5SPeter Avalos 		add_identity_file(options, "~/",
274736e94dc5SPeter Avalos 		    _PATH_SSH_CLIENT_ID_ED25519, 0);
27480cbfa66cSDaniel Fojt 		add_identity_file(options, "~/",
27490cbfa66cSDaniel Fojt 		    _PATH_SSH_CLIENT_ID_ED25519_SK, 0);
2750664f4763Szrj 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0);
2751*ba1276acSMatthew Dillon #ifdef WITH_DSA
2752ee116499SAntonio Huete Jimenez 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0);
2753*ba1276acSMatthew Dillon #endif
275418de8d7fSPeter Avalos 	}
275518de8d7fSPeter Avalos 	if (options->escape_char == -1)
275618de8d7fSPeter Avalos 		options->escape_char = '~';
27571c188a7fSPeter Avalos 	if (options->num_system_hostfiles == 0) {
27581c188a7fSPeter Avalos 		options->system_hostfiles[options->num_system_hostfiles++] =
27591c188a7fSPeter Avalos 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
27601c188a7fSPeter Avalos 		options->system_hostfiles[options->num_system_hostfiles++] =
27611c188a7fSPeter Avalos 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
27621c188a7fSPeter Avalos 	}
276350a69bb5SSascha Wildner 	if (options->update_hostkeys == -1) {
276450a69bb5SSascha Wildner 		if (options->verify_host_key_dns <= 0 &&
276550a69bb5SSascha Wildner 		    (options->num_user_hostfiles == 0 ||
276650a69bb5SSascha Wildner 		    (options->num_user_hostfiles == 1 && strcmp(options->
276750a69bb5SSascha Wildner 		    user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0)))
276850a69bb5SSascha Wildner 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES;
276950a69bb5SSascha Wildner 		else
27700cbfa66cSDaniel Fojt 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO;
277150a69bb5SSascha Wildner 	}
27721c188a7fSPeter Avalos 	if (options->num_user_hostfiles == 0) {
27731c188a7fSPeter Avalos 		options->user_hostfiles[options->num_user_hostfiles++] =
27741c188a7fSPeter Avalos 		    xstrdup(_PATH_SSH_USER_HOSTFILE);
27751c188a7fSPeter Avalos 		options->user_hostfiles[options->num_user_hostfiles++] =
27761c188a7fSPeter Avalos 		    xstrdup(_PATH_SSH_USER_HOSTFILE2);
27771c188a7fSPeter Avalos 	}
277818de8d7fSPeter Avalos 	if (options->log_level == SYSLOG_LEVEL_NOT_SET)
277918de8d7fSPeter Avalos 		options->log_level = SYSLOG_LEVEL_INFO;
2780ce74bacaSMatthew Dillon 	if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
2781ce74bacaSMatthew Dillon 		options->log_facility = SYSLOG_FACILITY_USER;
278218de8d7fSPeter Avalos 	if (options->no_host_authentication_for_localhost == - 1)
278318de8d7fSPeter Avalos 		options->no_host_authentication_for_localhost = 0;
278418de8d7fSPeter Avalos 	if (options->identities_only == -1)
278518de8d7fSPeter Avalos 		options->identities_only = 0;
278618de8d7fSPeter Avalos 	if (options->enable_ssh_keysign == -1)
278718de8d7fSPeter Avalos 		options->enable_ssh_keysign = 0;
278818de8d7fSPeter Avalos 	if (options->rekey_limit == -1)
278918de8d7fSPeter Avalos 		options->rekey_limit = 0;
279036e94dc5SPeter Avalos 	if (options->rekey_interval == -1)
279136e94dc5SPeter Avalos 		options->rekey_interval = 0;
279218de8d7fSPeter Avalos 	if (options->verify_host_key_dns == -1)
279318de8d7fSPeter Avalos 		options->verify_host_key_dns = 0;
279418de8d7fSPeter Avalos 	if (options->server_alive_interval == -1)
279518de8d7fSPeter Avalos 		options->server_alive_interval = 0;
279618de8d7fSPeter Avalos 	if (options->server_alive_count_max == -1)
279718de8d7fSPeter Avalos 		options->server_alive_count_max = 3;
279818de8d7fSPeter Avalos 	if (options->control_master == -1)
279918de8d7fSPeter Avalos 		options->control_master = 0;
2800856ea928SPeter Avalos 	if (options->control_persist == -1) {
2801856ea928SPeter Avalos 		options->control_persist = 0;
2802856ea928SPeter Avalos 		options->control_persist_timeout = 0;
2803856ea928SPeter Avalos 	}
280418de8d7fSPeter Avalos 	if (options->hash_known_hosts == -1)
280518de8d7fSPeter Avalos 		options->hash_known_hosts = 0;
280618de8d7fSPeter Avalos 	if (options->tun_open == -1)
280718de8d7fSPeter Avalos 		options->tun_open = SSH_TUNMODE_NO;
280818de8d7fSPeter Avalos 	if (options->tun_local == -1)
280918de8d7fSPeter Avalos 		options->tun_local = SSH_TUNID_ANY;
281018de8d7fSPeter Avalos 	if (options->tun_remote == -1)
281118de8d7fSPeter Avalos 		options->tun_remote = SSH_TUNID_ANY;
281218de8d7fSPeter Avalos 	if (options->permit_local_command == -1)
281318de8d7fSPeter Avalos 		options->permit_local_command = 0;
281418de8d7fSPeter Avalos 	if (options->visual_host_key == -1)
281518de8d7fSPeter Avalos 		options->visual_host_key = 0;
28169f304aafSPeter Avalos 	if (options->ip_qos_interactive == -1)
2817664f4763Szrj 		options->ip_qos_interactive = IPTOS_DSCP_AF21;
28189f304aafSPeter Avalos 	if (options->ip_qos_bulk == -1)
2819664f4763Szrj 		options->ip_qos_bulk = IPTOS_DSCP_CS1;
28201c188a7fSPeter Avalos 	if (options->request_tty == -1)
28211c188a7fSPeter Avalos 		options->request_tty = REQUEST_TTY_AUTO;
282250a69bb5SSascha Wildner 	if (options->session_type == -1)
282350a69bb5SSascha Wildner 		options->session_type = SESSION_TYPE_DEFAULT;
282450a69bb5SSascha Wildner 	if (options->stdin_null == -1)
282550a69bb5SSascha Wildner 		options->stdin_null = 0;
282650a69bb5SSascha Wildner 	if (options->fork_after_authentication == -1)
282750a69bb5SSascha Wildner 		options->fork_after_authentication = 0;
282836e94dc5SPeter Avalos 	if (options->proxy_use_fdpass == -1)
282936e94dc5SPeter Avalos 		options->proxy_use_fdpass = 0;
283036e94dc5SPeter Avalos 	if (options->canonicalize_max_dots == -1)
283136e94dc5SPeter Avalos 		options->canonicalize_max_dots = 1;
283236e94dc5SPeter Avalos 	if (options->canonicalize_fallback_local == -1)
283336e94dc5SPeter Avalos 		options->canonicalize_fallback_local = 1;
283436e94dc5SPeter Avalos 	if (options->canonicalize_hostname == -1)
283536e94dc5SPeter Avalos 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
2836e9778795SPeter Avalos 	if (options->fingerprint_hash == -1)
2837e9778795SPeter Avalos 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
28380cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL
28390cbfa66cSDaniel Fojt 	if (options->sk_provider == NULL)
28400cbfa66cSDaniel Fojt 		options->sk_provider = xstrdup("internal");
28410cbfa66cSDaniel Fojt #else
28420cbfa66cSDaniel Fojt 	if (options->sk_provider == NULL)
28430cbfa66cSDaniel Fojt 		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
28440cbfa66cSDaniel Fojt #endif
2845ee116499SAntonio Huete Jimenez 	if (options->required_rsa_size == -1)
2846ee116499SAntonio Huete Jimenez 		options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
2847*ba1276acSMatthew Dillon 	if (options->enable_escape_commandline == -1)
2848*ba1276acSMatthew Dillon 		options->enable_escape_commandline = 0;
2849*ba1276acSMatthew Dillon 	if (options->obscure_keystroke_timing_interval == -1) {
2850*ba1276acSMatthew Dillon 		options->obscure_keystroke_timing_interval =
2851*ba1276acSMatthew Dillon 		    SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2852*ba1276acSMatthew Dillon 	}
2853664f4763Szrj 
2854664f4763Szrj 	/* Expand KEX name lists */
2855664f4763Szrj 	all_cipher = cipher_alg_list(',', 0);
2856664f4763Szrj 	all_mac = mac_alg_list(',');
2857664f4763Szrj 	all_kex = kex_alg_list(',');
2858664f4763Szrj 	all_key = sshkey_alg_list(0, 0, 1, ',');
2859664f4763Szrj 	all_sig = sshkey_alg_list(0, 1, 1, ',');
28600cbfa66cSDaniel Fojt 	/* remove unsupported algos from default lists */
286150a69bb5SSascha Wildner 	def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher);
286250a69bb5SSascha Wildner 	def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac);
286350a69bb5SSascha Wildner 	def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex);
286450a69bb5SSascha Wildner 	def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
286550a69bb5SSascha Wildner 	def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig);
2866664f4763Szrj #define ASSEMBLE(what, defaults, all) \
2867664f4763Szrj 	do { \
2868664f4763Szrj 		if ((r = kex_assemble_names(&options->what, \
286950a69bb5SSascha Wildner 		    defaults, all)) != 0) { \
287050a69bb5SSascha Wildner 			error_fr(r, "%s", #what); \
287150a69bb5SSascha Wildner 			goto fail; \
287250a69bb5SSascha Wildner 		} \
2873664f4763Szrj 	} while (0)
28740cbfa66cSDaniel Fojt 	ASSEMBLE(ciphers, def_cipher, all_cipher);
28750cbfa66cSDaniel Fojt 	ASSEMBLE(macs, def_mac, all_mac);
28760cbfa66cSDaniel Fojt 	ASSEMBLE(kex_algorithms, def_kex, all_kex);
287750a69bb5SSascha Wildner 	ASSEMBLE(hostbased_accepted_algos, def_key, all_key);
287850a69bb5SSascha Wildner 	ASSEMBLE(pubkey_accepted_algos, def_key, all_key);
28790cbfa66cSDaniel Fojt 	ASSEMBLE(ca_sign_algorithms, def_sig, all_sig);
2880664f4763Szrj #undef ASSEMBLE
2881e9778795SPeter Avalos 
288236e94dc5SPeter Avalos #define CLEAR_ON_NONE(v) \
288336e94dc5SPeter Avalos 	do { \
288436e94dc5SPeter Avalos 		if (option_clear_or_none(v)) { \
288536e94dc5SPeter Avalos 			free(v); \
288636e94dc5SPeter Avalos 			v = NULL; \
288736e94dc5SPeter Avalos 		} \
288836e94dc5SPeter Avalos 	} while(0)
2889*ba1276acSMatthew Dillon #define CLEAR_ON_NONE_ARRAY(v, nv, none) \
2890*ba1276acSMatthew Dillon 	do { \
2891*ba1276acSMatthew Dillon 		if (options->nv == 1 && \
2892*ba1276acSMatthew Dillon 		    strcasecmp(options->v[0], none) == 0) { \
2893*ba1276acSMatthew Dillon 			free(options->v[0]); \
2894*ba1276acSMatthew Dillon 			free(options->v); \
2895*ba1276acSMatthew Dillon 			options->v = NULL; \
2896*ba1276acSMatthew Dillon 			options->nv = 0; \
2897*ba1276acSMatthew Dillon 		} \
2898*ba1276acSMatthew Dillon 	} while (0)
289936e94dc5SPeter Avalos 	CLEAR_ON_NONE(options->local_command);
2900ce74bacaSMatthew Dillon 	CLEAR_ON_NONE(options->remote_command);
290136e94dc5SPeter Avalos 	CLEAR_ON_NONE(options->proxy_command);
290236e94dc5SPeter Avalos 	CLEAR_ON_NONE(options->control_path);
2903e9778795SPeter Avalos 	CLEAR_ON_NONE(options->revoked_host_keys);
2904664f4763Szrj 	CLEAR_ON_NONE(options->pkcs11_provider);
29050cbfa66cSDaniel Fojt 	CLEAR_ON_NONE(options->sk_provider);
290650a69bb5SSascha Wildner 	CLEAR_ON_NONE(options->known_hosts_command);
2907*ba1276acSMatthew Dillon 	CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
2908*ba1276acSMatthew Dillon #undef CLEAR_ON_NONE
2909*ba1276acSMatthew Dillon #undef CLEAR_ON_NONE_ARRAY
2910664f4763Szrj 	if (options->jump_host != NULL &&
2911664f4763Szrj 	    strcmp(options->jump_host, "none") == 0 &&
2912664f4763Szrj 	    options->jump_port == 0 && options->jump_user == NULL) {
2913664f4763Szrj 		free(options->jump_host);
2914664f4763Szrj 		options->jump_host = NULL;
2915664f4763Szrj 	}
291650a69bb5SSascha Wildner 	if (options->num_permitted_cnames == 1 &&
291750a69bb5SSascha Wildner 	    !config_has_permitted_cnames(options)) {
291850a69bb5SSascha Wildner 		/* clean up CanonicalizePermittedCNAMEs=none */
291950a69bb5SSascha Wildner 		free(options->permitted_cnames[0].source_list);
292050a69bb5SSascha Wildner 		free(options->permitted_cnames[0].target_list);
292150a69bb5SSascha Wildner 		memset(options->permitted_cnames, '\0',
292250a69bb5SSascha Wildner 		    sizeof(*options->permitted_cnames));
292350a69bb5SSascha Wildner 		options->num_permitted_cnames = 0;
292450a69bb5SSascha Wildner 	}
2925e9778795SPeter Avalos 	/* options->identity_agent distinguishes NULL from 'none' */
292618de8d7fSPeter Avalos 	/* options->user will be set in the main program if appropriate */
292718de8d7fSPeter Avalos 	/* options->hostname will be set in the main program if appropriate */
292818de8d7fSPeter Avalos 	/* options->host_key_alias should not be set by default */
292918de8d7fSPeter Avalos 	/* options->preferred_authentications will be set in ssh */
293050a69bb5SSascha Wildner 
293150a69bb5SSascha Wildner 	/* success */
293250a69bb5SSascha Wildner 	ret = 0;
293350a69bb5SSascha Wildner  fail:
293450a69bb5SSascha Wildner 	free(all_cipher);
293550a69bb5SSascha Wildner 	free(all_mac);
293650a69bb5SSascha Wildner 	free(all_kex);
293750a69bb5SSascha Wildner 	free(all_key);
293850a69bb5SSascha Wildner 	free(all_sig);
293950a69bb5SSascha Wildner 	free(def_cipher);
294050a69bb5SSascha Wildner 	free(def_mac);
294150a69bb5SSascha Wildner 	free(def_kex);
294250a69bb5SSascha Wildner 	free(def_key);
294350a69bb5SSascha Wildner 	free(def_sig);
294450a69bb5SSascha Wildner 	return ret;
294550a69bb5SSascha Wildner }
294650a69bb5SSascha Wildner 
294750a69bb5SSascha Wildner void
free_options(Options * o)294850a69bb5SSascha Wildner free_options(Options *o)
294950a69bb5SSascha Wildner {
295050a69bb5SSascha Wildner 	int i;
295150a69bb5SSascha Wildner 
295250a69bb5SSascha Wildner 	if (o == NULL)
295350a69bb5SSascha Wildner 		return;
295450a69bb5SSascha Wildner 
295550a69bb5SSascha Wildner #define FREE_ARRAY(type, n, a) \
295650a69bb5SSascha Wildner 	do { \
295750a69bb5SSascha Wildner 		type _i; \
295850a69bb5SSascha Wildner 		for (_i = 0; _i < (n); _i++) \
295950a69bb5SSascha Wildner 			free((a)[_i]); \
296050a69bb5SSascha Wildner 	} while (0)
296150a69bb5SSascha Wildner 
296250a69bb5SSascha Wildner 	free(o->forward_agent_sock_path);
296350a69bb5SSascha Wildner 	free(o->xauth_location);
296450a69bb5SSascha Wildner 	FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
296550a69bb5SSascha Wildner 	free(o->log_verbose);
296650a69bb5SSascha Wildner 	free(o->ciphers);
296750a69bb5SSascha Wildner 	free(o->macs);
296850a69bb5SSascha Wildner 	free(o->hostkeyalgorithms);
296950a69bb5SSascha Wildner 	free(o->kex_algorithms);
297050a69bb5SSascha Wildner 	free(o->ca_sign_algorithms);
297150a69bb5SSascha Wildner 	free(o->hostname);
297250a69bb5SSascha Wildner 	free(o->host_key_alias);
297350a69bb5SSascha Wildner 	free(o->proxy_command);
297450a69bb5SSascha Wildner 	free(o->user);
297550a69bb5SSascha Wildner 	FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles);
297650a69bb5SSascha Wildner 	FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles);
297750a69bb5SSascha Wildner 	free(o->preferred_authentications);
297850a69bb5SSascha Wildner 	free(o->bind_address);
297950a69bb5SSascha Wildner 	free(o->bind_interface);
298050a69bb5SSascha Wildner 	free(o->pkcs11_provider);
298150a69bb5SSascha Wildner 	free(o->sk_provider);
298250a69bb5SSascha Wildner 	for (i = 0; i < o->num_identity_files; i++) {
298350a69bb5SSascha Wildner 		free(o->identity_files[i]);
298450a69bb5SSascha Wildner 		sshkey_free(o->identity_keys[i]);
298550a69bb5SSascha Wildner 	}
298650a69bb5SSascha Wildner 	for (i = 0; i < o->num_certificate_files; i++) {
298750a69bb5SSascha Wildner 		free(o->certificate_files[i]);
298850a69bb5SSascha Wildner 		sshkey_free(o->certificates[i]);
298950a69bb5SSascha Wildner 	}
299050a69bb5SSascha Wildner 	free(o->identity_agent);
299150a69bb5SSascha Wildner 	for (i = 0; i < o->num_local_forwards; i++) {
299250a69bb5SSascha Wildner 		free(o->local_forwards[i].listen_host);
299350a69bb5SSascha Wildner 		free(o->local_forwards[i].listen_path);
299450a69bb5SSascha Wildner 		free(o->local_forwards[i].connect_host);
299550a69bb5SSascha Wildner 		free(o->local_forwards[i].connect_path);
299650a69bb5SSascha Wildner 	}
299750a69bb5SSascha Wildner 	free(o->local_forwards);
299850a69bb5SSascha Wildner 	for (i = 0; i < o->num_remote_forwards; i++) {
299950a69bb5SSascha Wildner 		free(o->remote_forwards[i].listen_host);
300050a69bb5SSascha Wildner 		free(o->remote_forwards[i].listen_path);
300150a69bb5SSascha Wildner 		free(o->remote_forwards[i].connect_host);
300250a69bb5SSascha Wildner 		free(o->remote_forwards[i].connect_path);
300350a69bb5SSascha Wildner 	}
300450a69bb5SSascha Wildner 	free(o->remote_forwards);
300550a69bb5SSascha Wildner 	free(o->stdio_forward_host);
3006ee116499SAntonio Huete Jimenez 	FREE_ARRAY(u_int, o->num_send_env, o->send_env);
300750a69bb5SSascha Wildner 	free(o->send_env);
3008ee116499SAntonio Huete Jimenez 	FREE_ARRAY(u_int, o->num_setenv, o->setenv);
300950a69bb5SSascha Wildner 	free(o->setenv);
301050a69bb5SSascha Wildner 	free(o->control_path);
301150a69bb5SSascha Wildner 	free(o->local_command);
301250a69bb5SSascha Wildner 	free(o->remote_command);
301350a69bb5SSascha Wildner 	FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains);
301450a69bb5SSascha Wildner 	for (i = 0; i < o->num_permitted_cnames; i++) {
301550a69bb5SSascha Wildner 		free(o->permitted_cnames[i].source_list);
301650a69bb5SSascha Wildner 		free(o->permitted_cnames[i].target_list);
301750a69bb5SSascha Wildner 	}
301850a69bb5SSascha Wildner 	free(o->revoked_host_keys);
301950a69bb5SSascha Wildner 	free(o->hostbased_accepted_algos);
302050a69bb5SSascha Wildner 	free(o->pubkey_accepted_algos);
302150a69bb5SSascha Wildner 	free(o->jump_user);
302250a69bb5SSascha Wildner 	free(o->jump_host);
302350a69bb5SSascha Wildner 	free(o->jump_extra);
302450a69bb5SSascha Wildner 	free(o->ignored_unknown);
302550a69bb5SSascha Wildner 	explicit_bzero(o, sizeof(*o));
302650a69bb5SSascha Wildner #undef FREE_ARRAY
302718de8d7fSPeter Avalos }
302818de8d7fSPeter Avalos 
302936e94dc5SPeter Avalos struct fwdarg {
303036e94dc5SPeter Avalos 	char *arg;
303136e94dc5SPeter Avalos 	int ispath;
303236e94dc5SPeter Avalos };
303336e94dc5SPeter Avalos 
303436e94dc5SPeter Avalos /*
303536e94dc5SPeter Avalos  * parse_fwd_field
303636e94dc5SPeter Avalos  * parses the next field in a port forwarding specification.
303736e94dc5SPeter Avalos  * sets fwd to the parsed field and advances p past the colon
303836e94dc5SPeter Avalos  * or sets it to NULL at end of string.
303936e94dc5SPeter Avalos  * returns 0 on success, else non-zero.
304036e94dc5SPeter Avalos  */
304136e94dc5SPeter Avalos static int
parse_fwd_field(char ** p,struct fwdarg * fwd)304236e94dc5SPeter Avalos parse_fwd_field(char **p, struct fwdarg *fwd)
304336e94dc5SPeter Avalos {
304436e94dc5SPeter Avalos 	char *ep, *cp = *p;
304536e94dc5SPeter Avalos 	int ispath = 0;
304636e94dc5SPeter Avalos 
304736e94dc5SPeter Avalos 	if (*cp == '\0') {
304836e94dc5SPeter Avalos 		*p = NULL;
304936e94dc5SPeter Avalos 		return -1;	/* end of string */
305036e94dc5SPeter Avalos 	}
305136e94dc5SPeter Avalos 
305236e94dc5SPeter Avalos 	/*
305336e94dc5SPeter Avalos 	 * A field escaped with square brackets is used literally.
305436e94dc5SPeter Avalos 	 * XXX - allow ']' to be escaped via backslash?
305536e94dc5SPeter Avalos 	 */
305636e94dc5SPeter Avalos 	if (*cp == '[') {
305736e94dc5SPeter Avalos 		/* find matching ']' */
305836e94dc5SPeter Avalos 		for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) {
305936e94dc5SPeter Avalos 			if (*ep == '/')
306036e94dc5SPeter Avalos 				ispath = 1;
306136e94dc5SPeter Avalos 		}
306236e94dc5SPeter Avalos 		/* no matching ']' or not at end of field. */
306336e94dc5SPeter Avalos 		if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0'))
306436e94dc5SPeter Avalos 			return -1;
306536e94dc5SPeter Avalos 		/* NUL terminate the field and advance p past the colon */
306636e94dc5SPeter Avalos 		*ep++ = '\0';
306736e94dc5SPeter Avalos 		if (*ep != '\0')
306836e94dc5SPeter Avalos 			*ep++ = '\0';
306936e94dc5SPeter Avalos 		fwd->arg = cp + 1;
307036e94dc5SPeter Avalos 		fwd->ispath = ispath;
307136e94dc5SPeter Avalos 		*p = ep;
307236e94dc5SPeter Avalos 		return 0;
307336e94dc5SPeter Avalos 	}
307436e94dc5SPeter Avalos 
307536e94dc5SPeter Avalos 	for (cp = *p; *cp != '\0'; cp++) {
307636e94dc5SPeter Avalos 		switch (*cp) {
307736e94dc5SPeter Avalos 		case '\\':
307836e94dc5SPeter Avalos 			memmove(cp, cp + 1, strlen(cp + 1) + 1);
3079e9778795SPeter Avalos 			if (*cp == '\0')
3080e9778795SPeter Avalos 				return -1;
308136e94dc5SPeter Avalos 			break;
308236e94dc5SPeter Avalos 		case '/':
308336e94dc5SPeter Avalos 			ispath = 1;
308436e94dc5SPeter Avalos 			break;
308536e94dc5SPeter Avalos 		case ':':
308636e94dc5SPeter Avalos 			*cp++ = '\0';
308736e94dc5SPeter Avalos 			goto done;
308836e94dc5SPeter Avalos 		}
308936e94dc5SPeter Avalos 	}
309036e94dc5SPeter Avalos done:
309136e94dc5SPeter Avalos 	fwd->arg = *p;
309236e94dc5SPeter Avalos 	fwd->ispath = ispath;
309336e94dc5SPeter Avalos 	*p = cp;
309436e94dc5SPeter Avalos 	return 0;
309536e94dc5SPeter Avalos }
309636e94dc5SPeter Avalos 
309718de8d7fSPeter Avalos /*
309818de8d7fSPeter Avalos  * parse_forward
309918de8d7fSPeter Avalos  * parses a string containing a port forwarding specification of the form:
3100cb5eb4f1SPeter Avalos  *   dynamicfwd == 0
310136e94dc5SPeter Avalos  *	[listenhost:]listenport|listenpath:connecthost:connectport|connectpath
310236e94dc5SPeter Avalos  *	listenpath:connectpath
3103cb5eb4f1SPeter Avalos  *   dynamicfwd == 1
3104cb5eb4f1SPeter Avalos  *	[listenhost:]listenport
310518de8d7fSPeter Avalos  * returns number of arguments parsed or zero on error
310618de8d7fSPeter Avalos  */
310718de8d7fSPeter Avalos int
parse_forward(struct Forward * fwd,const char * fwdspec,int dynamicfwd,int remotefwd)310836e94dc5SPeter Avalos parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
310918de8d7fSPeter Avalos {
311036e94dc5SPeter Avalos 	struct fwdarg fwdargs[4];
311136e94dc5SPeter Avalos 	char *p, *cp;
311250a69bb5SSascha Wildner 	int i, err;
311318de8d7fSPeter Avalos 
311436e94dc5SPeter Avalos 	memset(fwd, 0, sizeof(*fwd));
311536e94dc5SPeter Avalos 	memset(fwdargs, 0, sizeof(fwdargs));
311618de8d7fSPeter Avalos 
311750a69bb5SSascha Wildner 	/*
311850a69bb5SSascha Wildner 	 * We expand environment variables before checking if we think they're
311950a69bb5SSascha Wildner 	 * paths so that if ${VAR} expands to a fully qualified path it is
312050a69bb5SSascha Wildner 	 * treated as a path.
312150a69bb5SSascha Wildner 	 */
312250a69bb5SSascha Wildner 	cp = p = dollar_expand(&err, fwdspec);
312350a69bb5SSascha Wildner 	if (p == NULL || err)
312450a69bb5SSascha Wildner 		return 0;
312518de8d7fSPeter Avalos 
312618de8d7fSPeter Avalos 	/* skip leading spaces */
312736e94dc5SPeter Avalos 	while (isspace((u_char)*cp))
312818de8d7fSPeter Avalos 		cp++;
312918de8d7fSPeter Avalos 
313036e94dc5SPeter Avalos 	for (i = 0; i < 4; ++i) {
313136e94dc5SPeter Avalos 		if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
313218de8d7fSPeter Avalos 			break;
313336e94dc5SPeter Avalos 	}
313418de8d7fSPeter Avalos 
3135cb5eb4f1SPeter Avalos 	/* Check for trailing garbage */
313636e94dc5SPeter Avalos 	if (cp != NULL && *cp != '\0') {
313718de8d7fSPeter Avalos 		i = 0;	/* failure */
313836e94dc5SPeter Avalos 	}
313918de8d7fSPeter Avalos 
314018de8d7fSPeter Avalos 	switch (i) {
3141cb5eb4f1SPeter Avalos 	case 1:
314236e94dc5SPeter Avalos 		if (fwdargs[0].ispath) {
314336e94dc5SPeter Avalos 			fwd->listen_path = xstrdup(fwdargs[0].arg);
314436e94dc5SPeter Avalos 			fwd->listen_port = PORT_STREAMLOCAL;
314536e94dc5SPeter Avalos 		} else {
3146cb5eb4f1SPeter Avalos 			fwd->listen_host = NULL;
314736e94dc5SPeter Avalos 			fwd->listen_port = a2port(fwdargs[0].arg);
314836e94dc5SPeter Avalos 		}
3149cb5eb4f1SPeter Avalos 		fwd->connect_host = xstrdup("socks");
3150cb5eb4f1SPeter Avalos 		break;
3151cb5eb4f1SPeter Avalos 
3152cb5eb4f1SPeter Avalos 	case 2:
315336e94dc5SPeter Avalos 		if (fwdargs[0].ispath && fwdargs[1].ispath) {
315436e94dc5SPeter Avalos 			fwd->listen_path = xstrdup(fwdargs[0].arg);
315536e94dc5SPeter Avalos 			fwd->listen_port = PORT_STREAMLOCAL;
315636e94dc5SPeter Avalos 			fwd->connect_path = xstrdup(fwdargs[1].arg);
315736e94dc5SPeter Avalos 			fwd->connect_port = PORT_STREAMLOCAL;
315836e94dc5SPeter Avalos 		} else if (fwdargs[1].ispath) {
315936e94dc5SPeter Avalos 			fwd->listen_host = NULL;
316036e94dc5SPeter Avalos 			fwd->listen_port = a2port(fwdargs[0].arg);
316136e94dc5SPeter Avalos 			fwd->connect_path = xstrdup(fwdargs[1].arg);
316236e94dc5SPeter Avalos 			fwd->connect_port = PORT_STREAMLOCAL;
316336e94dc5SPeter Avalos 		} else {
316436e94dc5SPeter Avalos 			fwd->listen_host = xstrdup(fwdargs[0].arg);
316536e94dc5SPeter Avalos 			fwd->listen_port = a2port(fwdargs[1].arg);
3166cb5eb4f1SPeter Avalos 			fwd->connect_host = xstrdup("socks");
316736e94dc5SPeter Avalos 		}
3168cb5eb4f1SPeter Avalos 		break;
3169cb5eb4f1SPeter Avalos 
317018de8d7fSPeter Avalos 	case 3:
317136e94dc5SPeter Avalos 		if (fwdargs[0].ispath) {
317236e94dc5SPeter Avalos 			fwd->listen_path = xstrdup(fwdargs[0].arg);
317336e94dc5SPeter Avalos 			fwd->listen_port = PORT_STREAMLOCAL;
317436e94dc5SPeter Avalos 			fwd->connect_host = xstrdup(fwdargs[1].arg);
317536e94dc5SPeter Avalos 			fwd->connect_port = a2port(fwdargs[2].arg);
317636e94dc5SPeter Avalos 		} else if (fwdargs[2].ispath) {
317736e94dc5SPeter Avalos 			fwd->listen_host = xstrdup(fwdargs[0].arg);
317836e94dc5SPeter Avalos 			fwd->listen_port = a2port(fwdargs[1].arg);
317936e94dc5SPeter Avalos 			fwd->connect_path = xstrdup(fwdargs[2].arg);
318036e94dc5SPeter Avalos 			fwd->connect_port = PORT_STREAMLOCAL;
318136e94dc5SPeter Avalos 		} else {
318218de8d7fSPeter Avalos 			fwd->listen_host = NULL;
318336e94dc5SPeter Avalos 			fwd->listen_port = a2port(fwdargs[0].arg);
318436e94dc5SPeter Avalos 			fwd->connect_host = xstrdup(fwdargs[1].arg);
318536e94dc5SPeter Avalos 			fwd->connect_port = a2port(fwdargs[2].arg);
318636e94dc5SPeter Avalos 		}
318718de8d7fSPeter Avalos 		break;
318818de8d7fSPeter Avalos 
318918de8d7fSPeter Avalos 	case 4:
319036e94dc5SPeter Avalos 		fwd->listen_host = xstrdup(fwdargs[0].arg);
319136e94dc5SPeter Avalos 		fwd->listen_port = a2port(fwdargs[1].arg);
319236e94dc5SPeter Avalos 		fwd->connect_host = xstrdup(fwdargs[2].arg);
319336e94dc5SPeter Avalos 		fwd->connect_port = a2port(fwdargs[3].arg);
319418de8d7fSPeter Avalos 		break;
319518de8d7fSPeter Avalos 	default:
319618de8d7fSPeter Avalos 		i = 0; /* failure */
319718de8d7fSPeter Avalos 	}
319818de8d7fSPeter Avalos 
319936e94dc5SPeter Avalos 	free(p);
320018de8d7fSPeter Avalos 
3201cb5eb4f1SPeter Avalos 	if (dynamicfwd) {
3202cb5eb4f1SPeter Avalos 		if (!(i == 1 || i == 2))
3203cb5eb4f1SPeter Avalos 			goto fail_free;
3204cb5eb4f1SPeter Avalos 	} else {
320536e94dc5SPeter Avalos 		if (!(i == 3 || i == 4)) {
320636e94dc5SPeter Avalos 			if (fwd->connect_path == NULL &&
320736e94dc5SPeter Avalos 			    fwd->listen_path == NULL)
3208cb5eb4f1SPeter Avalos 				goto fail_free;
320936e94dc5SPeter Avalos 		}
321036e94dc5SPeter Avalos 		if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3211cb5eb4f1SPeter Avalos 			goto fail_free;
3212cb5eb4f1SPeter Avalos 	}
3213cb5eb4f1SPeter Avalos 
321436e94dc5SPeter Avalos 	if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
321536e94dc5SPeter Avalos 	    (!remotefwd && fwd->listen_port == 0))
321618de8d7fSPeter Avalos 		goto fail_free;
321718de8d7fSPeter Avalos 	if (fwd->connect_host != NULL &&
321818de8d7fSPeter Avalos 	    strlen(fwd->connect_host) >= NI_MAXHOST)
321918de8d7fSPeter Avalos 		goto fail_free;
322050a69bb5SSascha Wildner 	/*
322150a69bb5SSascha Wildner 	 * XXX - if connecting to a remote socket, max sun len may not
322250a69bb5SSascha Wildner 	 * match this host
322350a69bb5SSascha Wildner 	 */
322436e94dc5SPeter Avalos 	if (fwd->connect_path != NULL &&
322536e94dc5SPeter Avalos 	    strlen(fwd->connect_path) >= PATH_MAX_SUN)
322636e94dc5SPeter Avalos 		goto fail_free;
3227cb5eb4f1SPeter Avalos 	if (fwd->listen_host != NULL &&
3228cb5eb4f1SPeter Avalos 	    strlen(fwd->listen_host) >= NI_MAXHOST)
3229cb5eb4f1SPeter Avalos 		goto fail_free;
323036e94dc5SPeter Avalos 	if (fwd->listen_path != NULL &&
323136e94dc5SPeter Avalos 	    strlen(fwd->listen_path) >= PATH_MAX_SUN)
323236e94dc5SPeter Avalos 		goto fail_free;
323318de8d7fSPeter Avalos 
323418de8d7fSPeter Avalos 	return (i);
323518de8d7fSPeter Avalos 
323618de8d7fSPeter Avalos  fail_free:
323736e94dc5SPeter Avalos 	free(fwd->connect_host);
3238cb5eb4f1SPeter Avalos 	fwd->connect_host = NULL;
323936e94dc5SPeter Avalos 	free(fwd->connect_path);
324036e94dc5SPeter Avalos 	fwd->connect_path = NULL;
324136e94dc5SPeter Avalos 	free(fwd->listen_host);
3242cb5eb4f1SPeter Avalos 	fwd->listen_host = NULL;
324336e94dc5SPeter Avalos 	free(fwd->listen_path);
324436e94dc5SPeter Avalos 	fwd->listen_path = NULL;
324518de8d7fSPeter Avalos 	return (0);
324618de8d7fSPeter Avalos }
3247e9778795SPeter Avalos 
3248e9778795SPeter Avalos int
parse_jump(const char * s,Options * o,int active)3249e9778795SPeter Avalos parse_jump(const char *s, Options *o, int active)
3250e9778795SPeter Avalos {
3251e9778795SPeter Avalos 	char *orig, *sdup, *cp;
3252e9778795SPeter Avalos 	char *host = NULL, *user = NULL;
325350a69bb5SSascha Wildner 	int r, ret = -1, port = -1, first;
3254e9778795SPeter Avalos 
3255e9778795SPeter Avalos 	active &= o->proxy_command == NULL && o->jump_host == NULL;
3256e9778795SPeter Avalos 
3257e9778795SPeter Avalos 	orig = sdup = xstrdup(s);
325850a69bb5SSascha Wildner 
325950a69bb5SSascha Wildner 	/* Remove comment and trailing whitespace */
326050a69bb5SSascha Wildner 	if ((cp = strchr(orig, '#')) != NULL)
326150a69bb5SSascha Wildner 		*cp = '\0';
326250a69bb5SSascha Wildner 	rtrim(orig);
326350a69bb5SSascha Wildner 
3264e9778795SPeter Avalos 	first = active;
3265e9778795SPeter Avalos 	do {
3266664f4763Szrj 		if (strcasecmp(s, "none") == 0)
3267664f4763Szrj 			break;
3268e9778795SPeter Avalos 		if ((cp = strrchr(sdup, ',')) == NULL)
3269e9778795SPeter Avalos 			cp = sdup; /* last */
3270e9778795SPeter Avalos 		else
3271e9778795SPeter Avalos 			*cp++ = '\0';
3272e9778795SPeter Avalos 
3273e9778795SPeter Avalos 		if (first) {
3274e9778795SPeter Avalos 			/* First argument and configuration is active */
327550a69bb5SSascha Wildner 			r = parse_ssh_uri(cp, &user, &host, &port);
327650a69bb5SSascha Wildner 			if (r == -1 || (r == 1 &&
327750a69bb5SSascha Wildner 			    parse_user_host_port(cp, &user, &host, &port) != 0))
3278e9778795SPeter Avalos 				goto out;
3279e9778795SPeter Avalos 		} else {
3280e9778795SPeter Avalos 			/* Subsequent argument or inactive configuration */
328150a69bb5SSascha Wildner 			r = parse_ssh_uri(cp, NULL, NULL, NULL);
328250a69bb5SSascha Wildner 			if (r == -1 || (r == 1 &&
328350a69bb5SSascha Wildner 			    parse_user_host_port(cp, NULL, NULL, NULL) != 0))
3284e9778795SPeter Avalos 				goto out;
3285e9778795SPeter Avalos 		}
3286e9778795SPeter Avalos 		first = 0; /* only check syntax for subsequent hosts */
3287e9778795SPeter Avalos 	} while (cp != sdup);
3288e9778795SPeter Avalos 	/* success */
3289e9778795SPeter Avalos 	if (active) {
3290664f4763Szrj 		if (strcasecmp(s, "none") == 0) {
3291664f4763Szrj 			o->jump_host = xstrdup("none");
3292664f4763Szrj 			o->jump_port = 0;
3293664f4763Szrj 		} else {
3294e9778795SPeter Avalos 			o->jump_user = user;
3295e9778795SPeter Avalos 			o->jump_host = host;
3296e9778795SPeter Avalos 			o->jump_port = port;
3297e9778795SPeter Avalos 			o->proxy_command = xstrdup("none");
3298e9778795SPeter Avalos 			user = host = NULL;
3299e9778795SPeter Avalos 			if ((cp = strrchr(s, ',')) != NULL && cp != s) {
3300e9778795SPeter Avalos 				o->jump_extra = xstrdup(s);
3301e9778795SPeter Avalos 				o->jump_extra[cp - s] = '\0';
3302e9778795SPeter Avalos 			}
3303e9778795SPeter Avalos 		}
3304664f4763Szrj 	}
3305e9778795SPeter Avalos 	ret = 0;
3306e9778795SPeter Avalos  out:
3307e9778795SPeter Avalos 	free(orig);
3308e9778795SPeter Avalos 	free(user);
3309e9778795SPeter Avalos 	free(host);
3310e9778795SPeter Avalos 	return ret;
3311e9778795SPeter Avalos }
3312e9778795SPeter Avalos 
3313664f4763Szrj int
parse_ssh_uri(const char * uri,char ** userp,char ** hostp,int * portp)3314664f4763Szrj parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
3315664f4763Szrj {
331650a69bb5SSascha Wildner 	char *user = NULL, *host = NULL, *path = NULL;
331750a69bb5SSascha Wildner 	int r, port;
3318664f4763Szrj 
331950a69bb5SSascha Wildner 	r = parse_uri("ssh", uri, &user, &host, &port, &path);
3320664f4763Szrj 	if (r == 0 && path != NULL)
3321664f4763Szrj 		r = -1;		/* path not allowed */
332250a69bb5SSascha Wildner 	if (r == 0) {
332350a69bb5SSascha Wildner 		if (userp != NULL) {
332450a69bb5SSascha Wildner 			*userp = user;
332550a69bb5SSascha Wildner 			user = NULL;
332650a69bb5SSascha Wildner 		}
332750a69bb5SSascha Wildner 		if (hostp != NULL) {
332850a69bb5SSascha Wildner 			*hostp = host;
332950a69bb5SSascha Wildner 			host = NULL;
333050a69bb5SSascha Wildner 		}
333150a69bb5SSascha Wildner 		if (portp != NULL)
333250a69bb5SSascha Wildner 			*portp = port;
333350a69bb5SSascha Wildner 	}
333450a69bb5SSascha Wildner 	free(user);
333550a69bb5SSascha Wildner 	free(host);
333650a69bb5SSascha Wildner 	free(path);
3337664f4763Szrj 	return r;
3338664f4763Szrj }
3339664f4763Szrj 
3340*ba1276acSMatthew Dillon /* XXX the following is a near-verbatim copy from servconf.c; refactor */
3341e9778795SPeter Avalos static const char *
fmt_multistate_int(int val,const struct multistate * m)3342e9778795SPeter Avalos fmt_multistate_int(int val, const struct multistate *m)
3343e9778795SPeter Avalos {
3344e9778795SPeter Avalos 	u_int i;
3345e9778795SPeter Avalos 
3346e9778795SPeter Avalos 	for (i = 0; m[i].key != NULL; i++) {
3347e9778795SPeter Avalos 		if (m[i].value == val)
3348e9778795SPeter Avalos 			return m[i].key;
3349e9778795SPeter Avalos 	}
3350e9778795SPeter Avalos 	return "UNKNOWN";
3351e9778795SPeter Avalos }
3352e9778795SPeter Avalos 
3353e9778795SPeter Avalos static const char *
fmt_intarg(OpCodes code,int val)3354e9778795SPeter Avalos fmt_intarg(OpCodes code, int val)
3355e9778795SPeter Avalos {
3356e9778795SPeter Avalos 	if (val == -1)
3357e9778795SPeter Avalos 		return "unset";
3358e9778795SPeter Avalos 	switch (code) {
3359e9778795SPeter Avalos 	case oAddressFamily:
3360e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_addressfamily);
3361e9778795SPeter Avalos 	case oVerifyHostKeyDNS:
3362e9778795SPeter Avalos 	case oUpdateHostkeys:
3363e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_yesnoask);
3364ce74bacaSMatthew Dillon 	case oStrictHostKeyChecking:
3365ce74bacaSMatthew Dillon 		return fmt_multistate_int(val, multistate_strict_hostkey);
3366e9778795SPeter Avalos 	case oControlMaster:
3367e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_controlmaster);
3368e9778795SPeter Avalos 	case oTunnel:
3369e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_tunnel);
3370e9778795SPeter Avalos 	case oRequestTTY:
3371e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_requesttty);
337250a69bb5SSascha Wildner 	case oSessionType:
337350a69bb5SSascha Wildner 		return fmt_multistate_int(val, multistate_sessiontype);
3374e9778795SPeter Avalos 	case oCanonicalizeHostname:
3375e9778795SPeter Avalos 		return fmt_multistate_int(val, multistate_canonicalizehostname);
3376664f4763Szrj 	case oAddKeysToAgent:
3377664f4763Szrj 		return fmt_multistate_int(val, multistate_yesnoaskconfirm);
3378ee116499SAntonio Huete Jimenez 	case oPubkeyAuthentication:
3379ee116499SAntonio Huete Jimenez 		return fmt_multistate_int(val, multistate_pubkey_auth);
3380e9778795SPeter Avalos 	case oFingerprintHash:
3381e9778795SPeter Avalos 		return ssh_digest_alg_name(val);
3382e9778795SPeter Avalos 	default:
3383e9778795SPeter Avalos 		switch (val) {
3384e9778795SPeter Avalos 		case 0:
3385e9778795SPeter Avalos 			return "no";
3386e9778795SPeter Avalos 		case 1:
3387e9778795SPeter Avalos 			return "yes";
3388e9778795SPeter Avalos 		default:
3389e9778795SPeter Avalos 			return "UNKNOWN";
3390e9778795SPeter Avalos 		}
3391e9778795SPeter Avalos 	}
3392e9778795SPeter Avalos }
3393e9778795SPeter Avalos 
3394e9778795SPeter Avalos static const char *
lookup_opcode_name(OpCodes code)3395e9778795SPeter Avalos lookup_opcode_name(OpCodes code)
3396e9778795SPeter Avalos {
3397e9778795SPeter Avalos 	u_int i;
3398e9778795SPeter Avalos 
3399e9778795SPeter Avalos 	for (i = 0; keywords[i].name != NULL; i++)
3400e9778795SPeter Avalos 		if (keywords[i].opcode == code)
3401e9778795SPeter Avalos 			return(keywords[i].name);
3402e9778795SPeter Avalos 	return "UNKNOWN";
3403e9778795SPeter Avalos }
3404e9778795SPeter Avalos 
3405e9778795SPeter Avalos static void
dump_cfg_int(OpCodes code,int val)3406e9778795SPeter Avalos dump_cfg_int(OpCodes code, int val)
3407e9778795SPeter Avalos {
3408*ba1276acSMatthew Dillon 	if (code == oObscureKeystrokeTiming) {
3409*ba1276acSMatthew Dillon 		if (val == 0) {
3410*ba1276acSMatthew Dillon 			printf("%s no\n", lookup_opcode_name(code));
3411*ba1276acSMatthew Dillon 			return;
3412*ba1276acSMatthew Dillon 		} else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) {
3413*ba1276acSMatthew Dillon 			printf("%s yes\n", lookup_opcode_name(code));
3414*ba1276acSMatthew Dillon 			return;
3415*ba1276acSMatthew Dillon 		}
3416*ba1276acSMatthew Dillon 		/* FALLTHROUGH */
3417*ba1276acSMatthew Dillon 	}
3418e9778795SPeter Avalos 	printf("%s %d\n", lookup_opcode_name(code), val);
3419e9778795SPeter Avalos }
3420e9778795SPeter Avalos 
3421e9778795SPeter Avalos static void
dump_cfg_fmtint(OpCodes code,int val)3422e9778795SPeter Avalos dump_cfg_fmtint(OpCodes code, int val)
3423e9778795SPeter Avalos {
3424e9778795SPeter Avalos 	printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
3425e9778795SPeter Avalos }
3426e9778795SPeter Avalos 
3427e9778795SPeter Avalos static void
dump_cfg_string(OpCodes code,const char * val)3428e9778795SPeter Avalos dump_cfg_string(OpCodes code, const char *val)
3429e9778795SPeter Avalos {
3430e9778795SPeter Avalos 	if (val == NULL)
3431e9778795SPeter Avalos 		return;
3432e9778795SPeter Avalos 	printf("%s %s\n", lookup_opcode_name(code), val);
3433e9778795SPeter Avalos }
3434e9778795SPeter Avalos 
3435e9778795SPeter Avalos static void
dump_cfg_strarray(OpCodes code,u_int count,char ** vals)3436e9778795SPeter Avalos dump_cfg_strarray(OpCodes code, u_int count, char **vals)
3437e9778795SPeter Avalos {
3438e9778795SPeter Avalos 	u_int i;
3439e9778795SPeter Avalos 
3440e9778795SPeter Avalos 	for (i = 0; i < count; i++)
3441e9778795SPeter Avalos 		printf("%s %s\n", lookup_opcode_name(code), vals[i]);
3442e9778795SPeter Avalos }
3443e9778795SPeter Avalos 
3444e9778795SPeter Avalos static void
dump_cfg_strarray_oneline(OpCodes code,u_int count,char ** vals)3445e9778795SPeter Avalos dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
3446e9778795SPeter Avalos {
3447e9778795SPeter Avalos 	u_int i;
3448e9778795SPeter Avalos 
3449e9778795SPeter Avalos 	printf("%s", lookup_opcode_name(code));
345050a69bb5SSascha Wildner 	if (count == 0)
345150a69bb5SSascha Wildner 		printf(" none");
3452e9778795SPeter Avalos 	for (i = 0; i < count; i++)
3453e9778795SPeter Avalos 		printf(" %s",  vals[i]);
3454e9778795SPeter Avalos 	printf("\n");
3455e9778795SPeter Avalos }
3456e9778795SPeter Avalos 
3457e9778795SPeter Avalos static void
dump_cfg_forwards(OpCodes code,u_int count,const struct Forward * fwds)3458e9778795SPeter Avalos dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
3459e9778795SPeter Avalos {
3460e9778795SPeter Avalos 	const struct Forward *fwd;
3461e9778795SPeter Avalos 	u_int i;
3462e9778795SPeter Avalos 
3463e9778795SPeter Avalos 	/* oDynamicForward */
3464e9778795SPeter Avalos 	for (i = 0; i < count; i++) {
3465e9778795SPeter Avalos 		fwd = &fwds[i];
3466ce74bacaSMatthew Dillon 		if (code == oDynamicForward && fwd->connect_host != NULL &&
3467e9778795SPeter Avalos 		    strcmp(fwd->connect_host, "socks") != 0)
3468e9778795SPeter Avalos 			continue;
3469ce74bacaSMatthew Dillon 		if (code == oLocalForward && fwd->connect_host != NULL &&
3470e9778795SPeter Avalos 		    strcmp(fwd->connect_host, "socks") == 0)
3471e9778795SPeter Avalos 			continue;
3472e9778795SPeter Avalos 		printf("%s", lookup_opcode_name(code));
3473e9778795SPeter Avalos 		if (fwd->listen_port == PORT_STREAMLOCAL)
3474e9778795SPeter Avalos 			printf(" %s", fwd->listen_path);
3475e9778795SPeter Avalos 		else if (fwd->listen_host == NULL)
3476e9778795SPeter Avalos 			printf(" %d", fwd->listen_port);
3477e9778795SPeter Avalos 		else {
3478e9778795SPeter Avalos 			printf(" [%s]:%d",
3479e9778795SPeter Avalos 			    fwd->listen_host, fwd->listen_port);
3480e9778795SPeter Avalos 		}
3481e9778795SPeter Avalos 		if (code != oDynamicForward) {
3482e9778795SPeter Avalos 			if (fwd->connect_port == PORT_STREAMLOCAL)
3483e9778795SPeter Avalos 				printf(" %s", fwd->connect_path);
3484e9778795SPeter Avalos 			else if (fwd->connect_host == NULL)
3485e9778795SPeter Avalos 				printf(" %d", fwd->connect_port);
3486e9778795SPeter Avalos 			else {
3487e9778795SPeter Avalos 				printf(" [%s]:%d",
3488e9778795SPeter Avalos 				    fwd->connect_host, fwd->connect_port);
3489e9778795SPeter Avalos 			}
3490e9778795SPeter Avalos 		}
3491e9778795SPeter Avalos 		printf("\n");
3492e9778795SPeter Avalos 	}
3493e9778795SPeter Avalos }
3494e9778795SPeter Avalos 
3495e9778795SPeter Avalos void
dump_client_config(Options * o,const char * host)3496e9778795SPeter Avalos dump_client_config(Options *o, const char *host)
3497e9778795SPeter Avalos {
34980cbfa66cSDaniel Fojt 	int i, r;
3499664f4763Szrj 	char buf[8], *all_key;
3500e9778795SPeter Avalos 
35010cbfa66cSDaniel Fojt 	/*
35020cbfa66cSDaniel Fojt 	 * Expand HostKeyAlgorithms name lists. This isn't handled in
35030cbfa66cSDaniel Fojt 	 * fill_default_options() like the other algorithm lists because
35040cbfa66cSDaniel Fojt 	 * the host key algorithms are by default dynamically chosen based
35050cbfa66cSDaniel Fojt 	 * on the host's keys found in known_hosts.
35060cbfa66cSDaniel Fojt 	 */
3507664f4763Szrj 	all_key = sshkey_alg_list(0, 0, 1, ',');
35080cbfa66cSDaniel Fojt 	if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(),
35090cbfa66cSDaniel Fojt 	    all_key)) != 0)
351050a69bb5SSascha Wildner 		fatal_fr(r, "expand HostKeyAlgorithms");
3511664f4763Szrj 	free(all_key);
3512e9778795SPeter Avalos 
3513e9778795SPeter Avalos 	/* Most interesting options first: user, host, port */
3514*ba1276acSMatthew Dillon 	dump_cfg_string(oHost, o->host_arg);
3515e9778795SPeter Avalos 	dump_cfg_string(oUser, o->user);
35160cbfa66cSDaniel Fojt 	dump_cfg_string(oHostname, host);
3517e9778795SPeter Avalos 	dump_cfg_int(oPort, o->port);
3518e9778795SPeter Avalos 
3519e9778795SPeter Avalos 	/* Flag options */
3520e9778795SPeter Avalos 	dump_cfg_fmtint(oAddressFamily, o->address_family);
3521e9778795SPeter Avalos 	dump_cfg_fmtint(oBatchMode, o->batch_mode);
3522e9778795SPeter Avalos 	dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
3523e9778795SPeter Avalos 	dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
3524e9778795SPeter Avalos 	dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
3525e9778795SPeter Avalos 	dump_cfg_fmtint(oCompression, o->compression);
3526e9778795SPeter Avalos 	dump_cfg_fmtint(oControlMaster, o->control_master);
3527e9778795SPeter Avalos 	dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
3528e9778795SPeter Avalos 	dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings);
3529e9778795SPeter Avalos 	dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
3530e9778795SPeter Avalos 	dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash);
3531e9778795SPeter Avalos 	dump_cfg_fmtint(oForwardX11, o->forward_x11);
3532e9778795SPeter Avalos 	dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
3533e9778795SPeter Avalos 	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3534e9778795SPeter Avalos #ifdef GSSAPI
3535e9778795SPeter Avalos 	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3536e9778795SPeter Avalos 	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3537e9778795SPeter Avalos #endif /* GSSAPI */
3538e9778795SPeter Avalos 	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3539e9778795SPeter Avalos 	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3540e9778795SPeter Avalos 	dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
3541e9778795SPeter Avalos 	dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
3542e9778795SPeter Avalos 	dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
3543e9778795SPeter Avalos 	dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
3544e9778795SPeter Avalos 	dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
3545e9778795SPeter Avalos 	dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
3546e9778795SPeter Avalos 	dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
3547e9778795SPeter Avalos 	dump_cfg_fmtint(oRequestTTY, o->request_tty);
354850a69bb5SSascha Wildner 	dump_cfg_fmtint(oSessionType, o->session_type);
354950a69bb5SSascha Wildner 	dump_cfg_fmtint(oStdinNull, o->stdin_null);
355050a69bb5SSascha Wildner 	dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication);
3551e9778795SPeter Avalos 	dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
3552e9778795SPeter Avalos 	dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
3553e9778795SPeter Avalos 	dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
3554e9778795SPeter Avalos 	dump_cfg_fmtint(oTunnel, o->tun_open);
3555e9778795SPeter Avalos 	dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
3556e9778795SPeter Avalos 	dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
3557e9778795SPeter Avalos 	dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
3558*ba1276acSMatthew Dillon 	dump_cfg_fmtint(oEnableEscapeCommandline, o->enable_escape_commandline);
3559e9778795SPeter Avalos 
3560e9778795SPeter Avalos 	/* Integer options */
3561e9778795SPeter Avalos 	dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
3562e9778795SPeter Avalos 	dump_cfg_int(oConnectionAttempts, o->connection_attempts);
3563e9778795SPeter Avalos 	dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
3564e9778795SPeter Avalos 	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
3565e9778795SPeter Avalos 	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
3566e9778795SPeter Avalos 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
3567ee116499SAntonio Huete Jimenez 	dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
3568*ba1276acSMatthew Dillon 	dump_cfg_int(oObscureKeystrokeTiming,
3569*ba1276acSMatthew Dillon 	    o->obscure_keystroke_timing_interval);
3570e9778795SPeter Avalos 
3571e9778795SPeter Avalos 	/* String options */
3572e9778795SPeter Avalos 	dump_cfg_string(oBindAddress, o->bind_address);
3573664f4763Szrj 	dump_cfg_string(oBindInterface, o->bind_interface);
35740cbfa66cSDaniel Fojt 	dump_cfg_string(oCiphers, o->ciphers);
3575e9778795SPeter Avalos 	dump_cfg_string(oControlPath, o->control_path);
3576e9778795SPeter Avalos 	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
3577e9778795SPeter Avalos 	dump_cfg_string(oHostKeyAlias, o->host_key_alias);
357850a69bb5SSascha Wildner 	dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos);
3579e9778795SPeter Avalos 	dump_cfg_string(oIdentityAgent, o->identity_agent);
3580664f4763Szrj 	dump_cfg_string(oIgnoreUnknown, o->ignored_unknown);
3581e9778795SPeter Avalos 	dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
35820cbfa66cSDaniel Fojt 	dump_cfg_string(oKexAlgorithms, o->kex_algorithms);
35830cbfa66cSDaniel Fojt 	dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms);
3584e9778795SPeter Avalos 	dump_cfg_string(oLocalCommand, o->local_command);
3585ce74bacaSMatthew Dillon 	dump_cfg_string(oRemoteCommand, o->remote_command);
3586e9778795SPeter Avalos 	dump_cfg_string(oLogLevel, log_level_name(o->log_level));
35870cbfa66cSDaniel Fojt 	dump_cfg_string(oMacs, o->macs);
3588ce74bacaSMatthew Dillon #ifdef ENABLE_PKCS11
3589e9778795SPeter Avalos 	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
3590ce74bacaSMatthew Dillon #endif
35910cbfa66cSDaniel Fojt 	dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
3592e9778795SPeter Avalos 	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
359350a69bb5SSascha Wildner 	dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
3594e9778795SPeter Avalos 	dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
3595e9778795SPeter Avalos 	dump_cfg_string(oXAuthLocation, o->xauth_location);
359650a69bb5SSascha Wildner 	dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
3597*ba1276acSMatthew Dillon 	dump_cfg_string(oTag, o->tag);
3598e9778795SPeter Avalos 
3599e9778795SPeter Avalos 	/* Forwards */
3600e9778795SPeter Avalos 	dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
3601e9778795SPeter Avalos 	dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
3602e9778795SPeter Avalos 	dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
3603e9778795SPeter Avalos 
3604e9778795SPeter Avalos 	/* String array options */
3605e9778795SPeter Avalos 	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
3606e9778795SPeter Avalos 	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
3607664f4763Szrj 	dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
3608e9778795SPeter Avalos 	dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
3609e9778795SPeter Avalos 	dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
3610e9778795SPeter Avalos 	dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
3611664f4763Szrj 	dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
361250a69bb5SSascha Wildner 	dump_cfg_strarray_oneline(oLogVerbose,
361350a69bb5SSascha Wildner 	    o->num_log_verbose, o->log_verbose);
3614*ba1276acSMatthew Dillon 	dump_cfg_strarray_oneline(oChannelTimeout,
3615*ba1276acSMatthew Dillon 	    o->num_channel_timeouts, o->channel_timeouts);
3616e9778795SPeter Avalos 
3617e9778795SPeter Avalos 	/* Special cases */
3618e9778795SPeter Avalos 
361950a69bb5SSascha Wildner 	/* PermitRemoteOpen */
362050a69bb5SSascha Wildner 	if (o->num_permitted_remote_opens == 0)
362150a69bb5SSascha Wildner 		printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen));
362250a69bb5SSascha Wildner 	else
362350a69bb5SSascha Wildner 		dump_cfg_strarray_oneline(oPermitRemoteOpen,
362450a69bb5SSascha Wildner 		    o->num_permitted_remote_opens, o->permitted_remote_opens);
362550a69bb5SSascha Wildner 
362650a69bb5SSascha Wildner 	/* AddKeysToAgent */
362750a69bb5SSascha Wildner 	if (o->add_keys_to_agent_lifespan <= 0)
362850a69bb5SSascha Wildner 		dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent);
362950a69bb5SSascha Wildner 	else {
363050a69bb5SSascha Wildner 		printf("addkeystoagent%s %d\n",
363150a69bb5SSascha Wildner 		    o->add_keys_to_agent == 3 ? " confirm" : "",
363250a69bb5SSascha Wildner 		    o->add_keys_to_agent_lifespan);
363350a69bb5SSascha Wildner 	}
363450a69bb5SSascha Wildner 
36350cbfa66cSDaniel Fojt 	/* oForwardAgent */
36360cbfa66cSDaniel Fojt 	if (o->forward_agent_sock_path == NULL)
36370cbfa66cSDaniel Fojt 		dump_cfg_fmtint(oForwardAgent, o->forward_agent);
36380cbfa66cSDaniel Fojt 	else
36390cbfa66cSDaniel Fojt 		dump_cfg_string(oForwardAgent, o->forward_agent_sock_path);
36400cbfa66cSDaniel Fojt 
3641e9778795SPeter Avalos 	/* oConnectTimeout */
3642e9778795SPeter Avalos 	if (o->connection_timeout == -1)
3643e9778795SPeter Avalos 		printf("connecttimeout none\n");
3644e9778795SPeter Avalos 	else
3645e9778795SPeter Avalos 		dump_cfg_int(oConnectTimeout, o->connection_timeout);
3646e9778795SPeter Avalos 
3647e9778795SPeter Avalos 	/* oTunnelDevice */
3648e9778795SPeter Avalos 	printf("tunneldevice");
3649e9778795SPeter Avalos 	if (o->tun_local == SSH_TUNID_ANY)
3650e9778795SPeter Avalos 		printf(" any");
3651e9778795SPeter Avalos 	else
3652e9778795SPeter Avalos 		printf(" %d", o->tun_local);
3653e9778795SPeter Avalos 	if (o->tun_remote == SSH_TUNID_ANY)
3654e9778795SPeter Avalos 		printf(":any");
3655e9778795SPeter Avalos 	else
3656e9778795SPeter Avalos 		printf(":%d", o->tun_remote);
3657e9778795SPeter Avalos 	printf("\n");
3658e9778795SPeter Avalos 
3659e9778795SPeter Avalos 	/* oCanonicalizePermittedCNAMEs */
3660e9778795SPeter Avalos 	printf("canonicalizePermittedcnames");
366150a69bb5SSascha Wildner 	if (o->num_permitted_cnames == 0)
366250a69bb5SSascha Wildner 		printf(" none");
3663e9778795SPeter Avalos 	for (i = 0; i < o->num_permitted_cnames; i++) {
3664e9778795SPeter Avalos 		printf(" %s:%s", o->permitted_cnames[i].source_list,
3665e9778795SPeter Avalos 		    o->permitted_cnames[i].target_list);
3666e9778795SPeter Avalos 	}
3667e9778795SPeter Avalos 	printf("\n");
3668e9778795SPeter Avalos 
3669e9778795SPeter Avalos 	/* oControlPersist */
3670e9778795SPeter Avalos 	if (o->control_persist == 0 || o->control_persist_timeout == 0)
3671e9778795SPeter Avalos 		dump_cfg_fmtint(oControlPersist, o->control_persist);
3672e9778795SPeter Avalos 	else
3673e9778795SPeter Avalos 		dump_cfg_int(oControlPersist, o->control_persist_timeout);
3674e9778795SPeter Avalos 
3675e9778795SPeter Avalos 	/* oEscapeChar */
3676e9778795SPeter Avalos 	if (o->escape_char == SSH_ESCAPECHAR_NONE)
3677e9778795SPeter Avalos 		printf("escapechar none\n");
3678e9778795SPeter Avalos 	else {
3679e9778795SPeter Avalos 		vis(buf, o->escape_char, VIS_WHITE, 0);
3680e9778795SPeter Avalos 		printf("escapechar %s\n", buf);
3681e9778795SPeter Avalos 	}
3682e9778795SPeter Avalos 
3683e9778795SPeter Avalos 	/* oIPQoS */
3684e9778795SPeter Avalos 	printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
3685e9778795SPeter Avalos 	printf("%s\n", iptos2str(o->ip_qos_bulk));
3686e9778795SPeter Avalos 
3687e9778795SPeter Avalos 	/* oRekeyLimit */
3688e9778795SPeter Avalos 	printf("rekeylimit %llu %d\n",
3689e9778795SPeter Avalos 	    (unsigned long long)o->rekey_limit, o->rekey_interval);
3690e9778795SPeter Avalos 
3691e9778795SPeter Avalos 	/* oStreamLocalBindMask */
3692e9778795SPeter Avalos 	printf("streamlocalbindmask 0%o\n",
3693e9778795SPeter Avalos 	    o->fwd_opts.streamlocal_bind_mask);
3694e9778795SPeter Avalos 
3695664f4763Szrj 	/* oLogFacility */
3696664f4763Szrj 	printf("syslogfacility %s\n", log_facility_name(o->log_facility));
3697664f4763Szrj 
3698e9778795SPeter Avalos 	/* oProxyCommand / oProxyJump */
3699e9778795SPeter Avalos 	if (o->jump_host == NULL)
3700e9778795SPeter Avalos 		dump_cfg_string(oProxyCommand, o->proxy_command);
3701e9778795SPeter Avalos 	else {
3702e9778795SPeter Avalos 		/* Check for numeric addresses */
3703e9778795SPeter Avalos 		i = strchr(o->jump_host, ':') != NULL ||
3704e9778795SPeter Avalos 		    strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
3705e9778795SPeter Avalos 		snprintf(buf, sizeof(buf), "%d", o->jump_port);
3706e9778795SPeter Avalos 		printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
3707e9778795SPeter Avalos 		    /* optional additional jump spec */
3708e9778795SPeter Avalos 		    o->jump_extra == NULL ? "" : o->jump_extra,
3709e9778795SPeter Avalos 		    o->jump_extra == NULL ? "" : ",",
3710e9778795SPeter Avalos 		    /* optional user */
3711e9778795SPeter Avalos 		    o->jump_user == NULL ? "" : o->jump_user,
3712e9778795SPeter Avalos 		    o->jump_user == NULL ? "" : "@",
3713e9778795SPeter Avalos 		    /* opening [ if hostname is numeric */
3714e9778795SPeter Avalos 		    i ? "[" : "",
3715e9778795SPeter Avalos 		    /* mandatory hostname */
3716e9778795SPeter Avalos 		    o->jump_host,
3717e9778795SPeter Avalos 		    /* closing ] if hostname is numeric */
3718e9778795SPeter Avalos 		    i ? "]" : "",
3719e9778795SPeter Avalos 		    /* optional port number */
3720e9778795SPeter Avalos 		    o->jump_port <= 0 ? "" : ":",
3721e9778795SPeter Avalos 		    o->jump_port <= 0 ? "" : buf);
3722e9778795SPeter Avalos 	}
3723e9778795SPeter Avalos }
3724