xref: /freebsd/crypto/openssh/readconf.c (revision 0ae642c7dd0c2cfd965a22bf73876cd26cceadd2)
1*0ae642c7SEd Maste /* $OpenBSD: readconf.c,v 1.392 2024/09/26 23:55:08 djm Exp $ */
2511b41d2SMark Murray /*
3511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5511b41d2SMark Murray  *                    All rights reserved
6511b41d2SMark Murray  * Functions for reading the configuration files.
7511b41d2SMark Murray  *
8c2d3a559SKris Kennaway  * As far as I am concerned, the code I have written for this software
9c2d3a559SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
10c2d3a559SKris Kennaway  * software must be clearly marked as such, and if the derived work is
11c2d3a559SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
12c2d3a559SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
13511b41d2SMark Murray  */
14511b41d2SMark Murray 
15511b41d2SMark Murray #include "includes.h"
16511b41d2SMark Murray 
17333ee039SDag-Erling Smørgrav #include <sys/types.h>
18333ee039SDag-Erling Smørgrav #include <sys/stat.h>
19333ee039SDag-Erling Smørgrav #include <sys/socket.h>
20f7167e0eSDag-Erling Smørgrav #include <sys/wait.h>
21a0ee8cc6SDag-Erling Smørgrav #include <sys/un.h>
22333ee039SDag-Erling Smørgrav 
23535af610SEd Maste #include <net/if.h>
24333ee039SDag-Erling Smørgrav #include <netinet/in.h>
254a421b63SDag-Erling Smørgrav #include <netinet/in_systm.h>
264a421b63SDag-Erling Smørgrav #include <netinet/ip.h>
27b83788ffSDag-Erling Smørgrav #include <arpa/inet.h>
28333ee039SDag-Erling Smørgrav 
29333ee039SDag-Erling Smørgrav #include <ctype.h>
30333ee039SDag-Erling Smørgrav #include <errno.h>
31f7167e0eSDag-Erling Smørgrav #include <fcntl.h>
32535af610SEd Maste #ifdef HAVE_IFADDRS_H
33535af610SEd Maste # include <ifaddrs.h>
34535af610SEd Maste #endif
35bc5531deSDag-Erling Smørgrav #include <limits.h>
36333ee039SDag-Erling Smørgrav #include <netdb.h>
37f7167e0eSDag-Erling Smørgrav #ifdef HAVE_PATHS_H
38f7167e0eSDag-Erling Smørgrav # include <paths.h>
39f7167e0eSDag-Erling Smørgrav #endif
40f7167e0eSDag-Erling Smørgrav #include <pwd.h>
41333ee039SDag-Erling Smørgrav #include <signal.h>
42333ee039SDag-Erling Smørgrav #include <stdio.h>
43333ee039SDag-Erling Smørgrav #include <string.h>
4419261079SEd Maste #include <stdarg.h>
45333ee039SDag-Erling Smørgrav #include <unistd.h>
46076ad2f8SDag-Erling Smørgrav #ifdef USE_SYSTEM_GLOB
47076ad2f8SDag-Erling Smørgrav # include <glob.h>
48076ad2f8SDag-Erling Smørgrav #else
49076ad2f8SDag-Erling Smørgrav # include "openbsd-compat/glob.h"
50076ad2f8SDag-Erling Smørgrav #endif
51e4a9863fSDag-Erling Smørgrav #ifdef HAVE_UTIL_H
52e4a9863fSDag-Erling Smørgrav #include <util.h>
53e4a9863fSDag-Erling Smørgrav #endif
54bc5531deSDag-Erling Smørgrav #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
55bc5531deSDag-Erling Smørgrav # include <vis.h>
56bc5531deSDag-Erling Smørgrav #endif
57333ee039SDag-Erling Smørgrav 
58511b41d2SMark Murray #include "xmalloc.h"
59333ee039SDag-Erling Smørgrav #include "ssh.h"
60190cef3dSDag-Erling Smørgrav #include "ssherr.h"
61ca3176e7SBrian Feldman #include "cipher.h"
62ca3176e7SBrian Feldman #include "pathnames.h"
63ca3176e7SBrian Feldman #include "log.h"
64bc5531deSDag-Erling Smørgrav #include "sshkey.h"
65a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
66ca3176e7SBrian Feldman #include "readconf.h"
67ca3176e7SBrian Feldman #include "match.h"
68ca3176e7SBrian Feldman #include "kex.h"
69ca3176e7SBrian Feldman #include "mac.h"
70f7167e0eSDag-Erling Smørgrav #include "uidswap.h"
71bc5531deSDag-Erling Smørgrav #include "myproposal.h"
72bc5531deSDag-Erling Smørgrav #include "digest.h"
73511b41d2SMark Murray 
74511b41d2SMark Murray /* Format of the configuration file:
75511b41d2SMark Murray 
76511b41d2SMark Murray    # Configuration data is parsed as follows:
77511b41d2SMark Murray    #  1. command line options
78511b41d2SMark Murray    #  2. user-specific file
79511b41d2SMark Murray    #  3. system-wide file
80511b41d2SMark Murray    # Any configuration value is only changed the first time it is set.
81511b41d2SMark Murray    # Thus, host-specific definitions should be at the beginning of the
82511b41d2SMark Murray    # configuration file, and defaults at the end.
83511b41d2SMark Murray 
84511b41d2SMark Murray    # Host-specific declarations.  These may override anything above.  A single
85511b41d2SMark Murray    # host may match multiple declarations; these are processed in the order
86511b41d2SMark Murray    # that they are given in.
87511b41d2SMark Murray 
88511b41d2SMark Murray    Host *.ngs.fi ngs.fi
8980628bacSDag-Erling Smørgrav      User foo
90511b41d2SMark Murray 
91511b41d2SMark Murray    Host fake.com
9219261079SEd Maste      Hostname another.host.name.real.org
93511b41d2SMark Murray      User blaah
94511b41d2SMark Murray      Port 34289
95511b41d2SMark Murray      ForwardX11 no
96511b41d2SMark Murray      ForwardAgent no
97511b41d2SMark Murray 
98511b41d2SMark Murray    Host books.com
99511b41d2SMark Murray      RemoteForward 9999 shadows.cs.hut.fi:9999
100d93a896eSDag-Erling Smørgrav      Ciphers 3des-cbc
101511b41d2SMark Murray 
102511b41d2SMark Murray    Host fascist.blob.com
103511b41d2SMark Murray      Port 23123
104511b41d2SMark Murray      User tylonen
105511b41d2SMark Murray      PasswordAuthentication no
106511b41d2SMark Murray 
107511b41d2SMark Murray    Host puukko.hut.fi
108511b41d2SMark Murray      User t35124p
109511b41d2SMark Murray      ProxyCommand ssh-proxy %h %p
110511b41d2SMark Murray 
111511b41d2SMark Murray    Host *.fr
11280628bacSDag-Erling Smørgrav      PublicKeyAuthentication no
113511b41d2SMark Murray 
114511b41d2SMark Murray    Host *.su
115d93a896eSDag-Erling Smørgrav      Ciphers aes128-ctr
116511b41d2SMark Murray      PasswordAuthentication no
117511b41d2SMark Murray 
118b74df5b2SDag-Erling Smørgrav    Host vpn.fake.com
119b74df5b2SDag-Erling Smørgrav      Tunnel yes
120b74df5b2SDag-Erling Smørgrav      TunnelDevice 3
121b74df5b2SDag-Erling Smørgrav 
122511b41d2SMark Murray    # Defaults for various options
123511b41d2SMark Murray    Host *
124511b41d2SMark Murray      ForwardAgent no
125ca3176e7SBrian Feldman      ForwardX11 no
126511b41d2SMark Murray      PasswordAuthentication yes
127511b41d2SMark Murray      StrictHostKeyChecking yes
1281ec0d754SDag-Erling Smørgrav      TcpKeepAlive no
129511b41d2SMark Murray      IdentityFile ~/.ssh/identity
130511b41d2SMark Murray      Port 22
131511b41d2SMark Murray      EscapeChar ~
132511b41d2SMark Murray 
133511b41d2SMark Murray */
134511b41d2SMark Murray 
135076ad2f8SDag-Erling Smørgrav static int read_config_file_depth(const char *filename, struct passwd *pw,
136076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, Options *options,
13719261079SEd Maste     int flags, int *activep, int *want_final_pass, int depth);
138076ad2f8SDag-Erling Smørgrav static int process_config_line_depth(Options *options, struct passwd *pw,
139076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, char *line,
14019261079SEd Maste     const char *filename, int linenum, int *activep, int flags,
14119261079SEd Maste     int *want_final_pass, int depth);
142076ad2f8SDag-Erling Smørgrav 
143511b41d2SMark Murray /* Keyword tokens. */
144511b41d2SMark Murray 
145511b41d2SMark Murray typedef enum {
146511b41d2SMark Murray 	oBadOption,
147535af610SEd Maste 	oHost, oMatch, oInclude, oTag,
148e2f6069cSDag-Erling Smørgrav 	oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
149e2f6069cSDag-Erling Smørgrav 	oGatewayPorts, oExitOnForwardFailure,
15019261079SEd Maste 	oPasswordAuthentication,
15119261079SEd Maste 	oXAuthLocation,
15219261079SEd Maste 	oIdentityFile, oHostname, oPort, oRemoteForward, oLocalForward,
15319261079SEd Maste 	oPermitRemoteOpen,
154076ad2f8SDag-Erling Smørgrav 	oCertificateFile, oAddKeysToAgent, oIdentityAgent,
15519261079SEd Maste 	oUser, oEscapeChar, oProxyCommand,
156511b41d2SMark Murray 	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
157511b41d2SMark Murray 	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
15819261079SEd Maste 	oTCPKeepAlive, oNumberOfPasswordPrompts,
15919261079SEd Maste 	oLogFacility, oLogLevel, oLogVerbose, oCiphers, oMacs,
160bc5531deSDag-Erling Smørgrav 	oPubkeyAuthentication,
161ca3176e7SBrian Feldman 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
162ca3176e7SBrian Feldman 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
16347dd1d1bSDag-Erling Smørgrav 	oHostKeyAlgorithms, oBindAddress, oBindInterface, oPKCS11Provider,
1649e2cbe04SDag-Erling Smørgrav 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
165cf2b5f3bSDag-Erling Smørgrav 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
166cf2b5f3bSDag-Erling Smørgrav 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
1675962c0e9SDag-Erling Smørgrav 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
168190cef3dSDag-Erling Smørgrav 	oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
169e2f6069cSDag-Erling Smørgrav 	oHashKnownHosts,
1704f52dfbbSDag-Erling Smørgrav 	oTunnel, oTunnelDevice,
1714f52dfbbSDag-Erling Smørgrav 	oLocalCommand, oPermitLocalCommand, oRemoteCommand,
172acc1a9efSDag-Erling Smørgrav 	oVisualHostKey,
17319261079SEd Maste 	oKexAlgorithms, oIPQoS, oRequestTTY, oSessionType, oStdinNull,
17419261079SEd Maste 	oForkAfterAuthentication, oIgnoreUnknown, oProxyUseFdpass,
175f7167e0eSDag-Erling Smørgrav 	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
176f7167e0eSDag-Erling Smørgrav 	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
177bc5531deSDag-Erling Smørgrav 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
17819261079SEd Maste 	oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
17919261079SEd Maste 	oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
18038a52bd3SEd Maste 	oSecurityKeyProvider, oKnownHostsCommand, oRequiredRSASize,
181069ac184SEd Maste 	oEnableEscapeCommandline, oObscureKeystrokeTiming, oChannelTimeout,
1824f52dfbbSDag-Erling Smørgrav 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
183511b41d2SMark Murray } OpCodes;
184511b41d2SMark Murray 
185511b41d2SMark Murray /* Textual representations of the tokens. */
186511b41d2SMark Murray 
187511b41d2SMark Murray static struct {
188511b41d2SMark Murray 	const char *name;
189511b41d2SMark Murray 	OpCodes opcode;
190511b41d2SMark Murray } keywords[] = {
191d93a896eSDag-Erling Smørgrav 	/* Deprecated options */
1924f52dfbbSDag-Erling Smørgrav 	{ "protocol", oIgnore }, /* NB. silently ignored */
1934f52dfbbSDag-Erling Smørgrav 	{ "cipher", oDeprecated },
194d93a896eSDag-Erling Smørgrav 	{ "fallbacktorsh", oDeprecated },
195d93a896eSDag-Erling Smørgrav 	{ "globalknownhostsfile2", oDeprecated },
196d93a896eSDag-Erling Smørgrav 	{ "rhostsauthentication", oDeprecated },
197d93a896eSDag-Erling Smørgrav 	{ "userknownhostsfile2", oDeprecated },
198d93a896eSDag-Erling Smørgrav 	{ "useroaming", oDeprecated },
199d93a896eSDag-Erling Smørgrav 	{ "usersh", oDeprecated },
200190cef3dSDag-Erling Smørgrav 	{ "useprivilegedport", oDeprecated },
201d93a896eSDag-Erling Smørgrav 
202d93a896eSDag-Erling Smørgrav 	/* Unsupported options */
203d93a896eSDag-Erling Smørgrav 	{ "afstokenpassing", oUnsupported },
204d93a896eSDag-Erling Smørgrav 	{ "kerberosauthentication", oUnsupported },
205d93a896eSDag-Erling Smørgrav 	{ "kerberostgtpassing", oUnsupported },
20619261079SEd Maste 	{ "rsaauthentication", oUnsupported },
20719261079SEd Maste 	{ "rhostsrsaauthentication", oUnsupported },
20819261079SEd Maste 	{ "compressionlevel", oUnsupported },
209d93a896eSDag-Erling Smørgrav 
210d93a896eSDag-Erling Smørgrav 	/* Sometimes-unsupported options */
211d93a896eSDag-Erling Smørgrav #if defined(GSSAPI)
212d93a896eSDag-Erling Smørgrav 	{ "gssapiauthentication", oGssAuthentication },
213d93a896eSDag-Erling Smørgrav 	{ "gssapidelegatecredentials", oGssDelegateCreds },
214d93a896eSDag-Erling Smørgrav # else
215d93a896eSDag-Erling Smørgrav 	{ "gssapiauthentication", oUnsupported },
216d93a896eSDag-Erling Smørgrav 	{ "gssapidelegatecredentials", oUnsupported },
217d93a896eSDag-Erling Smørgrav #endif
218d93a896eSDag-Erling Smørgrav #ifdef ENABLE_PKCS11
219d93a896eSDag-Erling Smørgrav 	{ "pkcs11provider", oPKCS11Provider },
22019261079SEd Maste 	{ "smartcarddevice", oPKCS11Provider },
221d93a896eSDag-Erling Smørgrav # else
222d93a896eSDag-Erling Smørgrav 	{ "smartcarddevice", oUnsupported },
223d93a896eSDag-Erling Smørgrav 	{ "pkcs11provider", oUnsupported },
224d93a896eSDag-Erling Smørgrav #endif
225d93a896eSDag-Erling Smørgrav 
226511b41d2SMark Murray 	{ "forwardagent", oForwardAgent },
227511b41d2SMark Murray 	{ "forwardx11", oForwardX11 },
2281ec0d754SDag-Erling Smørgrav 	{ "forwardx11trusted", oForwardX11Trusted },
229e2f6069cSDag-Erling Smørgrav 	{ "forwardx11timeout", oForwardX11Timeout },
230333ee039SDag-Erling Smørgrav 	{ "exitonforwardfailure", oExitOnForwardFailure },
231c2d3a559SKris Kennaway 	{ "xauthlocation", oXAuthLocation },
232511b41d2SMark Murray 	{ "gatewayports", oGatewayPorts },
233511b41d2SMark Murray 	{ "passwordauthentication", oPasswordAuthentication },
23409958426SBrian Feldman 	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
23509958426SBrian Feldman 	{ "kbdinteractivedevices", oKbdInteractiveDevices },
23619261079SEd Maste 	{ "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */
23719261079SEd Maste 	{ "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */
23819261079SEd Maste 	{ "tisauthentication", oKbdInteractiveAuthentication },  /* alias */
239ca3176e7SBrian Feldman 	{ "pubkeyauthentication", oPubkeyAuthentication },
240ca3176e7SBrian Feldman 	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
241ca3176e7SBrian Feldman 	{ "hostbasedauthentication", oHostbasedAuthentication },
242511b41d2SMark Murray 	{ "identityfile", oIdentityFile },
243cce7d346SDag-Erling Smørgrav 	{ "identityfile2", oIdentityFile },			/* obsolete */
2445962c0e9SDag-Erling Smørgrav 	{ "identitiesonly", oIdentitiesOnly },
245acc1a9efSDag-Erling Smørgrav 	{ "certificatefile", oCertificateFile },
246acc1a9efSDag-Erling Smørgrav 	{ "addkeystoagent", oAddKeysToAgent },
247076ad2f8SDag-Erling Smørgrav 	{ "identityagent", oIdentityAgent },
24819261079SEd Maste 	{ "hostname", oHostname },
249ca3176e7SBrian Feldman 	{ "hostkeyalias", oHostKeyAlias },
250511b41d2SMark Murray 	{ "proxycommand", oProxyCommand },
251511b41d2SMark Murray 	{ "port", oPort },
252e8aafc91SKris Kennaway 	{ "ciphers", oCiphers },
253ca3176e7SBrian Feldman 	{ "macs", oMacs },
254511b41d2SMark Murray 	{ "remoteforward", oRemoteForward },
255511b41d2SMark Murray 	{ "localforward", oLocalForward },
25619261079SEd Maste 	{ "permitremoteopen", oPermitRemoteOpen },
257511b41d2SMark Murray 	{ "user", oUser },
258511b41d2SMark Murray 	{ "host", oHost },
259f7167e0eSDag-Erling Smørgrav 	{ "match", oMatch },
260535af610SEd Maste 	{ "tag", oTag },
261511b41d2SMark Murray 	{ "escapechar", oEscapeChar },
262511b41d2SMark Murray 	{ "globalknownhostsfile", oGlobalKnownHostsFile },
263cce7d346SDag-Erling Smørgrav 	{ "userknownhostsfile", oUserKnownHostsFile },
264511b41d2SMark Murray 	{ "connectionattempts", oConnectionAttempts },
265511b41d2SMark Murray 	{ "batchmode", oBatchMode },
266511b41d2SMark Murray 	{ "checkhostip", oCheckHostIP },
267511b41d2SMark Murray 	{ "stricthostkeychecking", oStrictHostKeyChecking },
268511b41d2SMark Murray 	{ "compression", oCompression },
2691ec0d754SDag-Erling Smørgrav 	{ "tcpkeepalive", oTCPKeepAlive },
2701ec0d754SDag-Erling Smørgrav 	{ "keepalive", oTCPKeepAlive },				/* obsolete */
271511b41d2SMark Murray 	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
2724f52dfbbSDag-Erling Smørgrav 	{ "syslogfacility", oLogFacility },
273511b41d2SMark Murray 	{ "loglevel", oLogLevel },
27419261079SEd Maste 	{ "logverbose", oLogVerbose },
275ca3176e7SBrian Feldman 	{ "dynamicforward", oDynamicForward },
276ca3176e7SBrian Feldman 	{ "preferredauthentications", oPreferredAuthentications },
277ca3176e7SBrian Feldman 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
2782f513db7SEd Maste 	{ "casignaturealgorithms", oCASignatureAlgorithms },
279af12a3e7SDag-Erling Smørgrav 	{ "bindaddress", oBindAddress },
28047dd1d1bSDag-Erling Smørgrav 	{ "bindinterface", oBindInterface },
281af12a3e7SDag-Erling Smørgrav 	{ "clearallforwardings", oClearAllForwardings },
282e73e9afaSDag-Erling Smørgrav 	{ "enablesshkeysign", oEnableSSHKeysign },
283cf2b5f3bSDag-Erling Smørgrav 	{ "verifyhostkeydns", oVerifyHostKeyDNS },
284af12a3e7SDag-Erling Smørgrav 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
285cf2b5f3bSDag-Erling Smørgrav 	{ "rekeylimit", oRekeyLimit },
286cf2b5f3bSDag-Erling Smørgrav 	{ "connecttimeout", oConnectTimeout },
287cf2b5f3bSDag-Erling Smørgrav 	{ "addressfamily", oAddressFamily },
2881ec0d754SDag-Erling Smørgrav 	{ "serveraliveinterval", oServerAliveInterval },
2891ec0d754SDag-Erling Smørgrav 	{ "serveralivecountmax", oServerAliveCountMax },
29021e764dfSDag-Erling Smørgrav 	{ "sendenv", oSendEnv },
291190cef3dSDag-Erling Smørgrav 	{ "setenv", oSetEnv },
29221e764dfSDag-Erling Smørgrav 	{ "controlpath", oControlPath },
29321e764dfSDag-Erling Smørgrav 	{ "controlmaster", oControlMaster },
294e2f6069cSDag-Erling Smørgrav 	{ "controlpersist", oControlPersist },
295aa49c926SDag-Erling Smørgrav 	{ "hashknownhosts", oHashKnownHosts },
296076ad2f8SDag-Erling Smørgrav 	{ "include", oInclude },
297b74df5b2SDag-Erling Smørgrav 	{ "tunnel", oTunnel },
298b74df5b2SDag-Erling Smørgrav 	{ "tunneldevice", oTunnelDevice },
299b74df5b2SDag-Erling Smørgrav 	{ "localcommand", oLocalCommand },
300b74df5b2SDag-Erling Smørgrav 	{ "permitlocalcommand", oPermitLocalCommand },
3014f52dfbbSDag-Erling Smørgrav 	{ "remotecommand", oRemoteCommand },
302d4af9e69SDag-Erling Smørgrav 	{ "visualhostkey", oVisualHostKey },
3034a421b63SDag-Erling Smørgrav 	{ "kexalgorithms", oKexAlgorithms },
3044a421b63SDag-Erling Smørgrav 	{ "ipqos", oIPQoS },
305e146993eSDag-Erling Smørgrav 	{ "requesttty", oRequestTTY },
30619261079SEd Maste 	{ "sessiontype", oSessionType },
30719261079SEd Maste 	{ "stdinnull", oStdinNull },
30819261079SEd Maste 	{ "forkafterauthentication", oForkAfterAuthentication },
309f7167e0eSDag-Erling Smørgrav 	{ "proxyusefdpass", oProxyUseFdpass },
310f7167e0eSDag-Erling Smørgrav 	{ "canonicaldomains", oCanonicalDomains },
311f7167e0eSDag-Erling Smørgrav 	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
312f7167e0eSDag-Erling Smørgrav 	{ "canonicalizehostname", oCanonicalizeHostname },
313f7167e0eSDag-Erling Smørgrav 	{ "canonicalizemaxdots", oCanonicalizeMaxDots },
314f7167e0eSDag-Erling Smørgrav 	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
315a0ee8cc6SDag-Erling Smørgrav 	{ "streamlocalbindmask", oStreamLocalBindMask },
316a0ee8cc6SDag-Erling Smørgrav 	{ "streamlocalbindunlink", oStreamLocalBindUnlink },
317bc5531deSDag-Erling Smørgrav 	{ "revokedhostkeys", oRevokedHostKeys },
318bc5531deSDag-Erling Smørgrav 	{ "fingerprinthash", oFingerprintHash },
319bc5531deSDag-Erling Smørgrav 	{ "updatehostkeys", oUpdateHostkeys },
32019261079SEd Maste 	{ "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms },
32119261079SEd Maste 	{ "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */
32219261079SEd Maste 	{ "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
32319261079SEd Maste 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
324e4a9863fSDag-Erling Smørgrav 	{ "ignoreunknown", oIgnoreUnknown },
325076ad2f8SDag-Erling Smørgrav 	{ "proxyjump", oProxyJump },
32619261079SEd Maste 	{ "securitykeyprovider", oSecurityKeyProvider },
32719261079SEd Maste 	{ "knownhostscommand", oKnownHostsCommand },
32838a52bd3SEd Maste 	{ "requiredrsasize", oRequiredRSASize },
329f374ba41SEd Maste 	{ "enableescapecommandline", oEnableEscapeCommandline },
330edf85781SEd Maste 	{ "obscurekeystroketiming", oObscureKeystrokeTiming },
331069ac184SEd Maste 	{ "channeltimeout", oChannelTimeout },
332076ad2f8SDag-Erling Smørgrav 
333af12a3e7SDag-Erling Smørgrav 	{ NULL, oBadOption }
334511b41d2SMark Murray };
335511b41d2SMark Murray 
33619261079SEd Maste static const char *lookup_opcode_name(OpCodes code);
33719261079SEd Maste 
33819261079SEd Maste const char *
kex_default_pk_alg(void)33919261079SEd Maste kex_default_pk_alg(void)
34019261079SEd Maste {
34119261079SEd Maste 	static char *pkalgs;
34219261079SEd Maste 
34319261079SEd Maste 	if (pkalgs == NULL) {
34419261079SEd Maste 		char *all_key;
34519261079SEd Maste 
34619261079SEd Maste 		all_key = sshkey_alg_list(0, 0, 1, ',');
34719261079SEd Maste 		pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
34819261079SEd Maste 		free(all_key);
34919261079SEd Maste 	}
35019261079SEd Maste 	return pkalgs;
35119261079SEd Maste }
35219261079SEd Maste 
35319261079SEd Maste char *
ssh_connection_hash(const char * thishost,const char * host,const char * portstr,const char * user,const char * jumphost)35419261079SEd Maste ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
355069ac184SEd Maste     const char *user, const char *jumphost)
35619261079SEd Maste {
35719261079SEd Maste 	struct ssh_digest_ctx *md;
35819261079SEd Maste 	u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
35919261079SEd Maste 
36019261079SEd Maste 	if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
36119261079SEd Maste 	    ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
36219261079SEd Maste 	    ssh_digest_update(md, host, strlen(host)) < 0 ||
36319261079SEd Maste 	    ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
36419261079SEd Maste 	    ssh_digest_update(md, user, strlen(user)) < 0 ||
365069ac184SEd Maste 	    ssh_digest_update(md, jumphost, strlen(jumphost)) < 0 ||
36619261079SEd Maste 	    ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
36719261079SEd Maste 		fatal_f("mux digest failed");
36819261079SEd Maste 	ssh_digest_free(md);
36919261079SEd Maste 	return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
37019261079SEd Maste }
37119261079SEd Maste 
372511b41d2SMark Murray /*
373511b41d2SMark Murray  * Adds a local TCP/IP port forward to options.  Never returns if there is an
374511b41d2SMark Murray  * error.
375511b41d2SMark Murray  */
376511b41d2SMark Murray 
377511b41d2SMark Murray void
add_local_forward(Options * options,const struct Forward * newfwd)378a0ee8cc6SDag-Erling Smørgrav add_local_forward(Options *options, const struct Forward *newfwd)
379511b41d2SMark Murray {
380a0ee8cc6SDag-Erling Smørgrav 	struct Forward *fwd;
3819e14b918SEd Maste 	int i;
38203f6c5cdSDag-Erling Smørgrav 
383076ad2f8SDag-Erling Smørgrav 	/* Don't add duplicates */
384076ad2f8SDag-Erling Smørgrav 	for (i = 0; i < options->num_local_forwards; i++) {
385076ad2f8SDag-Erling Smørgrav 		if (forward_equals(newfwd, options->local_forwards + i))
386076ad2f8SDag-Erling Smørgrav 			return;
387076ad2f8SDag-Erling Smørgrav 	}
388557f75e5SDag-Erling Smørgrav 	options->local_forwards = xreallocarray(options->local_forwards,
389e2f6069cSDag-Erling Smørgrav 	    options->num_local_forwards + 1,
390e2f6069cSDag-Erling Smørgrav 	    sizeof(*options->local_forwards));
391511b41d2SMark Murray 	fwd = &options->local_forwards[options->num_local_forwards++];
392aa49c926SDag-Erling Smørgrav 
393cce7d346SDag-Erling Smørgrav 	fwd->listen_host = newfwd->listen_host;
394aa49c926SDag-Erling Smørgrav 	fwd->listen_port = newfwd->listen_port;
395a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = newfwd->listen_path;
396cce7d346SDag-Erling Smørgrav 	fwd->connect_host = newfwd->connect_host;
397aa49c926SDag-Erling Smørgrav 	fwd->connect_port = newfwd->connect_port;
398a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = newfwd->connect_path;
399511b41d2SMark Murray }
400511b41d2SMark Murray 
401511b41d2SMark Murray /*
402511b41d2SMark Murray  * Adds a remote TCP/IP port forward to options.  Never returns if there is
403511b41d2SMark Murray  * an error.
404511b41d2SMark Murray  */
405511b41d2SMark Murray 
406511b41d2SMark Murray void
add_remote_forward(Options * options,const struct Forward * newfwd)407a0ee8cc6SDag-Erling Smørgrav add_remote_forward(Options *options, const struct Forward *newfwd)
408511b41d2SMark Murray {
409a0ee8cc6SDag-Erling Smørgrav 	struct Forward *fwd;
410076ad2f8SDag-Erling Smørgrav 	int i;
411e2f6069cSDag-Erling Smørgrav 
412076ad2f8SDag-Erling Smørgrav 	/* Don't add duplicates */
413076ad2f8SDag-Erling Smørgrav 	for (i = 0; i < options->num_remote_forwards; i++) {
414076ad2f8SDag-Erling Smørgrav 		if (forward_equals(newfwd, options->remote_forwards + i))
415076ad2f8SDag-Erling Smørgrav 			return;
416076ad2f8SDag-Erling Smørgrav 	}
417557f75e5SDag-Erling Smørgrav 	options->remote_forwards = xreallocarray(options->remote_forwards,
418e2f6069cSDag-Erling Smørgrav 	    options->num_remote_forwards + 1,
419e2f6069cSDag-Erling Smørgrav 	    sizeof(*options->remote_forwards));
420511b41d2SMark Murray 	fwd = &options->remote_forwards[options->num_remote_forwards++];
421aa49c926SDag-Erling Smørgrav 
422cce7d346SDag-Erling Smørgrav 	fwd->listen_host = newfwd->listen_host;
423aa49c926SDag-Erling Smørgrav 	fwd->listen_port = newfwd->listen_port;
424a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = newfwd->listen_path;
425cce7d346SDag-Erling Smørgrav 	fwd->connect_host = newfwd->connect_host;
426aa49c926SDag-Erling Smørgrav 	fwd->connect_port = newfwd->connect_port;
427a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = newfwd->connect_path;
428462c32cbSDag-Erling Smørgrav 	fwd->handle = newfwd->handle;
429e2f6069cSDag-Erling Smørgrav 	fwd->allocated_port = 0;
430511b41d2SMark Murray }
431511b41d2SMark Murray 
432af12a3e7SDag-Erling Smørgrav static void
clear_forwardings(Options * options)433af12a3e7SDag-Erling Smørgrav clear_forwardings(Options *options)
434af12a3e7SDag-Erling Smørgrav {
435af12a3e7SDag-Erling Smørgrav 	int i;
436af12a3e7SDag-Erling Smørgrav 
437aa49c926SDag-Erling Smørgrav 	for (i = 0; i < options->num_local_forwards; i++) {
438e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards[i].listen_host);
439a0ee8cc6SDag-Erling Smørgrav 		free(options->local_forwards[i].listen_path);
440e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards[i].connect_host);
441a0ee8cc6SDag-Erling Smørgrav 		free(options->local_forwards[i].connect_path);
442aa49c926SDag-Erling Smørgrav 	}
443e2f6069cSDag-Erling Smørgrav 	if (options->num_local_forwards > 0) {
444e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards);
445e2f6069cSDag-Erling Smørgrav 		options->local_forwards = NULL;
446e2f6069cSDag-Erling Smørgrav 	}
447af12a3e7SDag-Erling Smørgrav 	options->num_local_forwards = 0;
448aa49c926SDag-Erling Smørgrav 	for (i = 0; i < options->num_remote_forwards; i++) {
449e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards[i].listen_host);
450a0ee8cc6SDag-Erling Smørgrav 		free(options->remote_forwards[i].listen_path);
451e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards[i].connect_host);
452a0ee8cc6SDag-Erling Smørgrav 		free(options->remote_forwards[i].connect_path);
453aa49c926SDag-Erling Smørgrav 	}
454e2f6069cSDag-Erling Smørgrav 	if (options->num_remote_forwards > 0) {
455e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards);
456e2f6069cSDag-Erling Smørgrav 		options->remote_forwards = NULL;
457e2f6069cSDag-Erling Smørgrav 	}
458af12a3e7SDag-Erling Smørgrav 	options->num_remote_forwards = 0;
459b74df5b2SDag-Erling Smørgrav 	options->tun_open = SSH_TUNMODE_NO;
460af12a3e7SDag-Erling Smørgrav }
461af12a3e7SDag-Erling Smørgrav 
462fa67e83cSDag-Erling Smørgrav void
add_certificate_file(Options * options,const char * path,int userprovided)463acc1a9efSDag-Erling Smørgrav add_certificate_file(Options *options, const char *path, int userprovided)
464acc1a9efSDag-Erling Smørgrav {
465acc1a9efSDag-Erling Smørgrav 	int i;
466acc1a9efSDag-Erling Smørgrav 
467acc1a9efSDag-Erling Smørgrav 	if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
468acc1a9efSDag-Erling Smørgrav 		fatal("Too many certificate files specified (max %d)",
469acc1a9efSDag-Erling Smørgrav 		    SSH_MAX_CERTIFICATE_FILES);
470acc1a9efSDag-Erling Smørgrav 
471acc1a9efSDag-Erling Smørgrav 	/* Avoid registering duplicates */
472acc1a9efSDag-Erling Smørgrav 	for (i = 0; i < options->num_certificate_files; i++) {
473acc1a9efSDag-Erling Smørgrav 		if (options->certificate_file_userprovided[i] == userprovided &&
474acc1a9efSDag-Erling Smørgrav 		    strcmp(options->certificate_files[i], path) == 0) {
47519261079SEd Maste 			debug2_f("ignoring duplicate key %s", path);
476acc1a9efSDag-Erling Smørgrav 			return;
477acc1a9efSDag-Erling Smørgrav 		}
478acc1a9efSDag-Erling Smørgrav 	}
479acc1a9efSDag-Erling Smørgrav 
480acc1a9efSDag-Erling Smørgrav 	options->certificate_file_userprovided[options->num_certificate_files] =
481acc1a9efSDag-Erling Smørgrav 	    userprovided;
482acc1a9efSDag-Erling Smørgrav 	options->certificate_files[options->num_certificate_files++] =
483acc1a9efSDag-Erling Smørgrav 	    xstrdup(path);
484acc1a9efSDag-Erling Smørgrav }
485acc1a9efSDag-Erling Smørgrav 
486acc1a9efSDag-Erling Smørgrav void
add_identity_file(Options * options,const char * dir,const char * filename,int userprovided)487fa67e83cSDag-Erling Smørgrav add_identity_file(Options *options, const char *dir, const char *filename,
488fa67e83cSDag-Erling Smørgrav     int userprovided)
489fa67e83cSDag-Erling Smørgrav {
490fa67e83cSDag-Erling Smørgrav 	char *path;
491a0ee8cc6SDag-Erling Smørgrav 	int i;
492fa67e83cSDag-Erling Smørgrav 
493fa67e83cSDag-Erling Smørgrav 	if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
494fa67e83cSDag-Erling Smørgrav 		fatal("Too many identity files specified (max %d)",
495fa67e83cSDag-Erling Smørgrav 		    SSH_MAX_IDENTITY_FILES);
496fa67e83cSDag-Erling Smørgrav 
497fa67e83cSDag-Erling Smørgrav 	if (dir == NULL) /* no dir, filename is absolute */
498fa67e83cSDag-Erling Smørgrav 		path = xstrdup(filename);
4994f52dfbbSDag-Erling Smørgrav 	else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX)
5004f52dfbbSDag-Erling Smørgrav 		fatal("Identity file path %s too long", path);
501fa67e83cSDag-Erling Smørgrav 
502a0ee8cc6SDag-Erling Smørgrav 	/* Avoid registering duplicates */
503a0ee8cc6SDag-Erling Smørgrav 	for (i = 0; i < options->num_identity_files; i++) {
504a0ee8cc6SDag-Erling Smørgrav 		if (options->identity_file_userprovided[i] == userprovided &&
505a0ee8cc6SDag-Erling Smørgrav 		    strcmp(options->identity_files[i], path) == 0) {
50619261079SEd Maste 			debug2_f("ignoring duplicate key %s", path);
507a0ee8cc6SDag-Erling Smørgrav 			free(path);
508a0ee8cc6SDag-Erling Smørgrav 			return;
509a0ee8cc6SDag-Erling Smørgrav 		}
510a0ee8cc6SDag-Erling Smørgrav 	}
511a0ee8cc6SDag-Erling Smørgrav 
512fa67e83cSDag-Erling Smørgrav 	options->identity_file_userprovided[options->num_identity_files] =
513fa67e83cSDag-Erling Smørgrav 	    userprovided;
514fa67e83cSDag-Erling Smørgrav 	options->identity_files[options->num_identity_files++] = path;
515fa67e83cSDag-Erling Smørgrav }
516fa67e83cSDag-Erling Smørgrav 
517f7167e0eSDag-Erling Smørgrav int
default_ssh_port(void)518f7167e0eSDag-Erling Smørgrav default_ssh_port(void)
519f7167e0eSDag-Erling Smørgrav {
520f7167e0eSDag-Erling Smørgrav 	static int port;
521f7167e0eSDag-Erling Smørgrav 	struct servent *sp;
522f7167e0eSDag-Erling Smørgrav 
523f7167e0eSDag-Erling Smørgrav 	if (port == 0) {
524f7167e0eSDag-Erling Smørgrav 		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
525f7167e0eSDag-Erling Smørgrav 		port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
526f7167e0eSDag-Erling Smørgrav 	}
527f7167e0eSDag-Erling Smørgrav 	return port;
528f7167e0eSDag-Erling Smørgrav }
529f7167e0eSDag-Erling Smørgrav 
530f7167e0eSDag-Erling Smørgrav /*
531f7167e0eSDag-Erling Smørgrav  * Execute a command in a shell.
532f7167e0eSDag-Erling Smørgrav  * Return its exit status or -1 on abnormal exit.
533f7167e0eSDag-Erling Smørgrav  */
534f7167e0eSDag-Erling Smørgrav static int
execute_in_shell(const char * cmd)535f7167e0eSDag-Erling Smørgrav execute_in_shell(const char *cmd)
536f7167e0eSDag-Erling Smørgrav {
537acc1a9efSDag-Erling Smørgrav 	char *shell;
538f7167e0eSDag-Erling Smørgrav 	pid_t pid;
53919261079SEd Maste 	int status;
540f7167e0eSDag-Erling Smørgrav 
541f7167e0eSDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL)
542f7167e0eSDag-Erling Smørgrav 		shell = _PATH_BSHELL;
543f7167e0eSDag-Erling Smørgrav 
54419261079SEd Maste 	if (access(shell, X_OK) == -1) {
54519261079SEd Maste 		fatal("Shell \"%s\" is not executable: %s",
54619261079SEd Maste 		    shell, strerror(errno));
54719261079SEd Maste 	}
548f7167e0eSDag-Erling Smørgrav 
549f7167e0eSDag-Erling Smørgrav 	debug("Executing command: '%.500s'", cmd);
550f7167e0eSDag-Erling Smørgrav 
551f7167e0eSDag-Erling Smørgrav 	/* Fork and execute the command. */
552f7167e0eSDag-Erling Smørgrav 	if ((pid = fork()) == 0) {
553f7167e0eSDag-Erling Smørgrav 		char *argv[4];
554f7167e0eSDag-Erling Smørgrav 
55519261079SEd Maste 		if (stdfd_devnull(1, 1, 0) == -1)
55619261079SEd Maste 			fatal_f("stdfd_devnull failed");
557f7167e0eSDag-Erling Smørgrav 		closefrom(STDERR_FILENO + 1);
558f7167e0eSDag-Erling Smørgrav 
559f7167e0eSDag-Erling Smørgrav 		argv[0] = shell;
560f7167e0eSDag-Erling Smørgrav 		argv[1] = "-c";
561acc1a9efSDag-Erling Smørgrav 		argv[2] = xstrdup(cmd);
562f7167e0eSDag-Erling Smørgrav 		argv[3] = NULL;
563f7167e0eSDag-Erling Smørgrav 
564f7167e0eSDag-Erling Smørgrav 		execv(argv[0], argv);
565f7167e0eSDag-Erling Smørgrav 		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
566f7167e0eSDag-Erling Smørgrav 		/* Die with signal to make this error apparent to parent. */
56719261079SEd Maste 		ssh_signal(SIGTERM, SIG_DFL);
568f7167e0eSDag-Erling Smørgrav 		kill(getpid(), SIGTERM);
569f7167e0eSDag-Erling Smørgrav 		_exit(1);
570f7167e0eSDag-Erling Smørgrav 	}
571f7167e0eSDag-Erling Smørgrav 	/* Parent. */
57219261079SEd Maste 	if (pid == -1)
57319261079SEd Maste 		fatal_f("fork: %.100s", strerror(errno));
574f7167e0eSDag-Erling Smørgrav 
575f7167e0eSDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1) {
576f7167e0eSDag-Erling Smørgrav 		if (errno != EINTR && errno != EAGAIN)
57719261079SEd Maste 			fatal_f("waitpid: %s", strerror(errno));
578f7167e0eSDag-Erling Smørgrav 	}
579f7167e0eSDag-Erling Smørgrav 	if (!WIFEXITED(status)) {
580f7167e0eSDag-Erling Smørgrav 		error("command '%.100s' exited abnormally", cmd);
581f7167e0eSDag-Erling Smørgrav 		return -1;
582f7167e0eSDag-Erling Smørgrav 	}
583f7167e0eSDag-Erling Smørgrav 	debug3("command returned status %d", WEXITSTATUS(status));
584f7167e0eSDag-Erling Smørgrav 	return WEXITSTATUS(status);
585f7167e0eSDag-Erling Smørgrav }
586f7167e0eSDag-Erling Smørgrav 
587f7167e0eSDag-Erling Smørgrav /*
588535af610SEd Maste  * Check whether a local network interface address appears in CIDR pattern-
589535af610SEd Maste  * list 'addrlist'. Returns 1 if matched or 0 otherwise.
590535af610SEd Maste  */
591535af610SEd Maste static int
check_match_ifaddrs(const char * addrlist)592535af610SEd Maste check_match_ifaddrs(const char *addrlist)
593535af610SEd Maste {
594535af610SEd Maste #ifdef HAVE_IFADDRS_H
595535af610SEd Maste 	struct ifaddrs *ifa, *ifaddrs = NULL;
596535af610SEd Maste 	int r, found = 0;
597535af610SEd Maste 	char addr[NI_MAXHOST];
598535af610SEd Maste 	socklen_t salen;
599535af610SEd Maste 
600535af610SEd Maste 	if (getifaddrs(&ifaddrs) != 0) {
601535af610SEd Maste 		error("match localnetwork: getifaddrs failed: %s",
602535af610SEd Maste 		    strerror(errno));
603535af610SEd Maste 		return 0;
604535af610SEd Maste 	}
605535af610SEd Maste 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
606535af610SEd Maste 		if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL ||
607535af610SEd Maste 		    (ifa->ifa_flags & IFF_UP) == 0)
608535af610SEd Maste 			continue;
609535af610SEd Maste 		switch (ifa->ifa_addr->sa_family) {
610535af610SEd Maste 		case AF_INET:
611535af610SEd Maste 			salen = sizeof(struct sockaddr_in);
612535af610SEd Maste 			break;
613535af610SEd Maste 		case AF_INET6:
614535af610SEd Maste 			salen = sizeof(struct sockaddr_in6);
615535af610SEd Maste 			break;
616535af610SEd Maste #ifdef AF_LINK
617535af610SEd Maste 		case AF_LINK:
618535af610SEd Maste 			/* ignore */
619535af610SEd Maste 			continue;
620535af610SEd Maste #endif /* AF_LINK */
621535af610SEd Maste 		default:
622535af610SEd Maste 			debug2_f("interface %s: unsupported address family %d",
623535af610SEd Maste 			    ifa->ifa_name, ifa->ifa_addr->sa_family);
624535af610SEd Maste 			continue;
625535af610SEd Maste 		}
626535af610SEd Maste 		if ((r = getnameinfo(ifa->ifa_addr, salen, addr, sizeof(addr),
627535af610SEd Maste 		    NULL, 0, NI_NUMERICHOST)) != 0) {
628535af610SEd Maste 			debug2_f("interface %s getnameinfo failed: %s",
629535af610SEd Maste 			    ifa->ifa_name, gai_strerror(r));
630535af610SEd Maste 			continue;
631535af610SEd Maste 		}
632535af610SEd Maste 		debug3_f("interface %s addr %s", ifa->ifa_name, addr);
633535af610SEd Maste 		if (addr_match_cidr_list(addr, addrlist) == 1) {
634535af610SEd Maste 			debug3_f("matched interface %s: address %s in %s",
635535af610SEd Maste 			    ifa->ifa_name, addr, addrlist);
636535af610SEd Maste 			found = 1;
637535af610SEd Maste 			break;
638535af610SEd Maste 		}
639535af610SEd Maste 	}
640535af610SEd Maste 	freeifaddrs(ifaddrs);
641535af610SEd Maste 	return found;
642535af610SEd Maste #else /* HAVE_IFADDRS_H */
643535af610SEd Maste 	error("match localnetwork: not supported on this platform");
644535af610SEd Maste 	return 0;
645535af610SEd Maste #endif /* HAVE_IFADDRS_H */
646535af610SEd Maste }
647535af610SEd Maste 
648535af610SEd Maste /*
6493d9fd9fcSEd Maste  * Expand a "match exec" command or an Include path, caller must free returned
6503d9fd9fcSEd Maste  * value.
6513d9fd9fcSEd Maste  */
6523d9fd9fcSEd Maste static char *
expand_match_exec_or_include_path(const char * path,Options * options,struct passwd * pw,const char * host_arg,const char * original_host,int final_pass,int is_include_path)6533d9fd9fcSEd Maste expand_match_exec_or_include_path(const char *path, Options *options,
6543d9fd9fcSEd Maste     struct passwd *pw, const char *host_arg, const char *original_host,
6553d9fd9fcSEd Maste     int final_pass, int is_include_path)
6563d9fd9fcSEd Maste {
6573d9fd9fcSEd Maste 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
6583d9fd9fcSEd Maste 	char uidstr[32], *conn_hash_hex, *keyalias, *jmphost, *ruser;
6593d9fd9fcSEd Maste 	char *host, *ret;
6603d9fd9fcSEd Maste 	int port;
6613d9fd9fcSEd Maste 
6623d9fd9fcSEd Maste 	port = options->port <= 0 ? default_ssh_port() : options->port;
6633d9fd9fcSEd Maste 	ruser = options->user == NULL ? pw->pw_name : options->user;
6643d9fd9fcSEd Maste 	if (final_pass) {
6653d9fd9fcSEd Maste 		host = xstrdup(options->hostname);
6663d9fd9fcSEd Maste 	} else if (options->hostname != NULL) {
6673d9fd9fcSEd Maste 		/* NB. Please keep in sync with ssh.c:main() */
6683d9fd9fcSEd Maste 		host = percent_expand(options->hostname,
6693d9fd9fcSEd Maste 		    "h", host_arg, (char *)NULL);
6703d9fd9fcSEd Maste 	} else {
6713d9fd9fcSEd Maste 		host = xstrdup(host_arg);
6723d9fd9fcSEd Maste 	}
6733d9fd9fcSEd Maste 	if (gethostname(thishost, sizeof(thishost)) == -1)
6743d9fd9fcSEd Maste 		fatal("gethostname: %s", strerror(errno));
6753d9fd9fcSEd Maste 	jmphost = option_clear_or_none(options->jump_host) ?
6763d9fd9fcSEd Maste 	    "" : options->jump_host;
6773d9fd9fcSEd Maste 	strlcpy(shorthost, thishost, sizeof(shorthost));
6783d9fd9fcSEd Maste 	shorthost[strcspn(thishost, ".")] = '\0';
6793d9fd9fcSEd Maste 	snprintf(portstr, sizeof(portstr), "%d", port);
6803d9fd9fcSEd Maste 	snprintf(uidstr, sizeof(uidstr), "%llu",
6813d9fd9fcSEd Maste 	    (unsigned long long)pw->pw_uid);
6823d9fd9fcSEd Maste 	conn_hash_hex = ssh_connection_hash(thishost, host,
6833d9fd9fcSEd Maste 	    portstr, ruser, jmphost);
6843d9fd9fcSEd Maste 	keyalias = options->host_key_alias ?  options->host_key_alias : host;
6853d9fd9fcSEd Maste 
6863d9fd9fcSEd Maste 	ret = (is_include_path ? percent_dollar_expand : percent_expand)(path,
6873d9fd9fcSEd Maste 	    "C", conn_hash_hex,
6883d9fd9fcSEd Maste 	    "L", shorthost,
6893d9fd9fcSEd Maste 	    "d", pw->pw_dir,
6903d9fd9fcSEd Maste 	    "h", host,
6913d9fd9fcSEd Maste 	    "k", keyalias,
6923d9fd9fcSEd Maste 	    "l", thishost,
6933d9fd9fcSEd Maste 	    "n", original_host,
6943d9fd9fcSEd Maste 	    "p", portstr,
6953d9fd9fcSEd Maste 	    "r", ruser,
6963d9fd9fcSEd Maste 	    "u", pw->pw_name,
6973d9fd9fcSEd Maste 	    "i", uidstr,
6983d9fd9fcSEd Maste 	    "j", jmphost,
6993d9fd9fcSEd Maste 	    (char *)NULL);
7003d9fd9fcSEd Maste 	free(host);
7013d9fd9fcSEd Maste 	free(conn_hash_hex);
7023d9fd9fcSEd Maste 	return ret;
7033d9fd9fcSEd Maste }
7043d9fd9fcSEd Maste 
7053d9fd9fcSEd Maste /*
706f7167e0eSDag-Erling Smørgrav  * Parse and execute a Match directive.
707f7167e0eSDag-Erling Smørgrav  */
708f7167e0eSDag-Erling Smørgrav static int
match_cfg_line(Options * options,const char * full_line,int * acp,char *** avp,struct passwd * pw,const char * host_arg,const char * original_host,int final_pass,int * want_final_pass,const char * filename,int linenum)7093d9fd9fcSEd Maste match_cfg_line(Options *options, const char *full_line, int *acp, char ***avp,
7103d9fd9fcSEd Maste     struct passwd *pw, const char *host_arg, const char *original_host,
7113d9fd9fcSEd Maste     int final_pass, int *want_final_pass, const char *filename, int linenum)
712f7167e0eSDag-Erling Smørgrav {
713*0ae642c7SEd Maste 	char *arg, *oattrib = NULL, *attrib = NULL, *cmd, *host, *criteria;
714f7167e0eSDag-Erling Smørgrav 	const char *ruser;
7153d9fd9fcSEd Maste 	int r, this_result, result = 1, attributes = 0, negate;
716f7167e0eSDag-Erling Smørgrav 
717f7167e0eSDag-Erling Smørgrav 	/*
718f7167e0eSDag-Erling Smørgrav 	 * Configuration is likely to be incomplete at this point so we
719f7167e0eSDag-Erling Smørgrav 	 * must be prepared to use default values.
720f7167e0eSDag-Erling Smørgrav 	 */
721f7167e0eSDag-Erling Smørgrav 	ruser = options->user == NULL ? pw->pw_name : options->user;
72219261079SEd Maste 	if (final_pass) {
723acc1a9efSDag-Erling Smørgrav 		host = xstrdup(options->hostname);
724acc1a9efSDag-Erling Smørgrav 	} else if (options->hostname != NULL) {
725f7167e0eSDag-Erling Smørgrav 		/* NB. Please keep in sync with ssh.c:main() */
726f7167e0eSDag-Erling Smørgrav 		host = percent_expand(options->hostname,
727f7167e0eSDag-Erling Smørgrav 		    "h", host_arg, (char *)NULL);
728acc1a9efSDag-Erling Smørgrav 	} else {
729f7167e0eSDag-Erling Smørgrav 		host = xstrdup(host_arg);
730acc1a9efSDag-Erling Smørgrav 	}
731f7167e0eSDag-Erling Smørgrav 
732bc5531deSDag-Erling Smørgrav 	debug2("checking match for '%s' host %s originally %s",
7333d9fd9fcSEd Maste 	    full_line, host, original_host);
734*0ae642c7SEd Maste 	while ((attrib = argv_next(acp, avp)) != NULL) {
735*0ae642c7SEd Maste 		attrib = oattrib = xstrdup(attrib);
73619261079SEd Maste 		/* Terminate on comment */
73719261079SEd Maste 		if (*attrib == '#') {
7383d9fd9fcSEd Maste 			argv_consume(acp);
73919261079SEd Maste 			break;
74019261079SEd Maste 		}
74119261079SEd Maste 		arg = criteria = NULL;
742bc5531deSDag-Erling Smørgrav 		this_result = 1;
7434d3fc8b0SEd Maste 		if ((negate = (attrib[0] == '!')))
744bc5531deSDag-Erling Smørgrav 			attrib++;
74519261079SEd Maste 		/* Criterion "all" has no argument and must appear alone */
746f7167e0eSDag-Erling Smørgrav 		if (strcasecmp(attrib, "all") == 0) {
7473d9fd9fcSEd Maste 			if (attributes > 1 ||
7483d9fd9fcSEd Maste 			    ((arg = argv_next(acp, avp)) != NULL &&
74919261079SEd Maste 			    *arg != '\0' && *arg != '#')) {
750bc5531deSDag-Erling Smørgrav 				error("%.200s line %d: '%s' cannot be combined "
751bc5531deSDag-Erling Smørgrav 				    "with other Match attributes",
752bc5531deSDag-Erling Smørgrav 				    filename, linenum, oattrib);
753f7167e0eSDag-Erling Smørgrav 				result = -1;
754f7167e0eSDag-Erling Smørgrav 				goto out;
755f7167e0eSDag-Erling Smørgrav 			}
75619261079SEd Maste 			if (arg != NULL && *arg == '#')
7573d9fd9fcSEd Maste 				argv_consume(acp); /* consume remaining args */
758bc5531deSDag-Erling Smørgrav 			if (result)
759bc5531deSDag-Erling Smørgrav 				result = negate ? 0 : 1;
760f7167e0eSDag-Erling Smørgrav 			goto out;
761f7167e0eSDag-Erling Smørgrav 		}
762bc5531deSDag-Erling Smørgrav 		attributes++;
76319261079SEd Maste 		/* criteria "final" and "canonical" have no argument */
76419261079SEd Maste 		if (strcasecmp(attrib, "canonical") == 0 ||
76519261079SEd Maste 		    strcasecmp(attrib, "final") == 0) {
76619261079SEd Maste 			/*
76719261079SEd Maste 			 * If the config requests "Match final" then remember
76819261079SEd Maste 			 * this so we can perform a second pass later.
76919261079SEd Maste 			 */
77019261079SEd Maste 			if (strcasecmp(attrib, "final") == 0 &&
77119261079SEd Maste 			    want_final_pass != NULL)
77219261079SEd Maste 				*want_final_pass = 1;
77319261079SEd Maste 			r = !!final_pass;  /* force bitmask member to boolean */
774bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
775bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
776bc5531deSDag-Erling Smørgrav 			debug3("%.200s line %d: %smatched '%s'",
777bc5531deSDag-Erling Smørgrav 			    filename, linenum,
778bc5531deSDag-Erling Smørgrav 			    this_result ? "" : "not ", oattrib);
779bc5531deSDag-Erling Smørgrav 			continue;
780bc5531deSDag-Erling Smørgrav 		}
781*0ae642c7SEd Maste 
782*0ae642c7SEd Maste 		/* Keep this list in sync with below */
783*0ae642c7SEd Maste 		if (strprefix(attrib, "host=", 1)  != NULL ||
784*0ae642c7SEd Maste 		    strprefix(attrib, "originalhost=", 1) != NULL ||
785*0ae642c7SEd Maste 		    strprefix(attrib, "user=", 1) != NULL ||
786*0ae642c7SEd Maste 		    strprefix(attrib, "localuser=", 1) != NULL ||
787*0ae642c7SEd Maste 		    strprefix(attrib, "localnetwork=", 1) != NULL ||
788*0ae642c7SEd Maste 		    strprefix(attrib, "tagged=", 1) != NULL ||
789*0ae642c7SEd Maste 		    strprefix(attrib, "exec=", 1) != NULL) {
790*0ae642c7SEd Maste 			arg = strchr(attrib, '=');
791*0ae642c7SEd Maste 			*(arg++) = '\0';
792*0ae642c7SEd Maste 		} else {
793*0ae642c7SEd Maste 			arg = argv_next(acp, avp);
794*0ae642c7SEd Maste 		}
795*0ae642c7SEd Maste 
796bc5531deSDag-Erling Smørgrav 		/* All other criteria require an argument */
797*0ae642c7SEd Maste 		if (arg == NULL || *arg == '\0' || *arg == '#') {
798f7167e0eSDag-Erling Smørgrav 			error("Missing Match criteria for %s", attrib);
799f7167e0eSDag-Erling Smørgrav 			result = -1;
800f7167e0eSDag-Erling Smørgrav 			goto out;
801f7167e0eSDag-Erling Smørgrav 		}
802f7167e0eSDag-Erling Smørgrav 		if (strcasecmp(attrib, "host") == 0) {
803bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(host);
804557f75e5SDag-Erling Smørgrav 			r = match_hostname(host, arg) == 1;
805bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
806bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
807f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "originalhost") == 0) {
808bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(original_host);
809557f75e5SDag-Erling Smørgrav 			r = match_hostname(original_host, arg) == 1;
810bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
811bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
812f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "user") == 0) {
813bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(ruser);
814557f75e5SDag-Erling Smørgrav 			r = match_pattern_list(ruser, arg, 0) == 1;
815bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
816bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
817f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "localuser") == 0) {
818bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(pw->pw_name);
819557f75e5SDag-Erling Smørgrav 			r = match_pattern_list(pw->pw_name, arg, 0) == 1;
820bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
821bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
822535af610SEd Maste 		} else if (strcasecmp(attrib, "localnetwork") == 0) {
823535af610SEd Maste 			if (addr_match_cidr_list(NULL, arg) == -1) {
824535af610SEd Maste 				/* Error already printed */
825535af610SEd Maste 				result = -1;
826535af610SEd Maste 				goto out;
827535af610SEd Maste 			}
828535af610SEd Maste 			r = check_match_ifaddrs(arg) == 1;
829535af610SEd Maste 			if (r == (negate ? 1 : 0))
830535af610SEd Maste 				this_result = result = 0;
831535af610SEd Maste 		} else if (strcasecmp(attrib, "tagged") == 0) {
832535af610SEd Maste 			criteria = xstrdup(options->tag == NULL ? "" :
833535af610SEd Maste 			    options->tag);
834535af610SEd Maste 			r = match_pattern_list(criteria, arg, 0) == 1;
835535af610SEd Maste 			if (r == (negate ? 1 : 0))
836535af610SEd Maste 				this_result = result = 0;
837f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "exec") == 0) {
8383d9fd9fcSEd Maste 			if ((cmd = expand_match_exec_or_include_path(arg,
8393d9fd9fcSEd Maste 			    options, pw, host_arg, original_host,
8403d9fd9fcSEd Maste 			    final_pass, 0)) == NULL) {
8413d9fd9fcSEd Maste 				fatal("%.200s line %d: failed to expand match "
8423d9fd9fcSEd Maste 				    "exec '%.100s'", filename, linenum, arg);
8433d9fd9fcSEd Maste 			}
844b83788ffSDag-Erling Smørgrav 			if (result != 1) {
845b83788ffSDag-Erling Smørgrav 				/* skip execution if prior predicate failed */
846bc5531deSDag-Erling Smørgrav 				debug3("%.200s line %d: skipped exec "
847bc5531deSDag-Erling Smørgrav 				    "\"%.100s\"", filename, linenum, cmd);
848bc5531deSDag-Erling Smørgrav 				free(cmd);
849bc5531deSDag-Erling Smørgrav 				continue;
850bc5531deSDag-Erling Smørgrav 			}
851f7167e0eSDag-Erling Smørgrav 			r = execute_in_shell(cmd);
852f7167e0eSDag-Erling Smørgrav 			if (r == -1) {
853b83788ffSDag-Erling Smørgrav 				fatal("%.200s line %d: match exec "
854b83788ffSDag-Erling Smørgrav 				    "'%.100s' error", filename,
855b83788ffSDag-Erling Smørgrav 				    linenum, cmd);
856b83788ffSDag-Erling Smørgrav 			}
857bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(cmd);
858f7167e0eSDag-Erling Smørgrav 			free(cmd);
859bc5531deSDag-Erling Smørgrav 			/* Force exit status to boolean */
860bc5531deSDag-Erling Smørgrav 			r = r == 0;
861bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
862bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
863f7167e0eSDag-Erling Smørgrav 		} else {
864f7167e0eSDag-Erling Smørgrav 			error("Unsupported Match attribute %s", attrib);
865f7167e0eSDag-Erling Smørgrav 			result = -1;
866f7167e0eSDag-Erling Smørgrav 			goto out;
867f7167e0eSDag-Erling Smørgrav 		}
868535af610SEd Maste 		debug3("%.200s line %d: %smatched '%s%s%.100s%s' ",
869535af610SEd Maste 		    filename, linenum, this_result ? "": "not ", oattrib,
870535af610SEd Maste 		    criteria == NULL ? "" : " \"",
871535af610SEd Maste 		    criteria == NULL ? "" : criteria,
872535af610SEd Maste 		    criteria == NULL ? "" : "\"");
873bc5531deSDag-Erling Smørgrav 		free(criteria);
874*0ae642c7SEd Maste 		free(oattrib);
875*0ae642c7SEd Maste 		oattrib = attrib = NULL;
876f7167e0eSDag-Erling Smørgrav 	}
877f7167e0eSDag-Erling Smørgrav 	if (attributes == 0) {
878f7167e0eSDag-Erling Smørgrav 		error("One or more attributes required for Match");
879f7167e0eSDag-Erling Smørgrav 		result = -1;
880f7167e0eSDag-Erling Smørgrav 		goto out;
881f7167e0eSDag-Erling Smørgrav 	}
882f7167e0eSDag-Erling Smørgrav  out:
883bc5531deSDag-Erling Smørgrav 	if (result != -1)
884bc5531deSDag-Erling Smørgrav 		debug2("match %sfound", result ? "" : "not ");
885*0ae642c7SEd Maste 	free(oattrib);
886f7167e0eSDag-Erling Smørgrav 	free(host);
887f7167e0eSDag-Erling Smørgrav 	return result;
888f7167e0eSDag-Erling Smørgrav }
889f7167e0eSDag-Erling Smørgrav 
890190cef3dSDag-Erling Smørgrav /* Remove environment variable by pattern */
891190cef3dSDag-Erling Smørgrav static void
rm_env(Options * options,const char * arg,const char * filename,int linenum)892190cef3dSDag-Erling Smørgrav rm_env(Options *options, const char *arg, const char *filename, int linenum)
893190cef3dSDag-Erling Smørgrav {
89438a52bd3SEd Maste 	u_int i, j, onum_send_env = options->num_send_env;
895190cef3dSDag-Erling Smørgrav 
896190cef3dSDag-Erling Smørgrav 	/* Remove an environment variable */
897190cef3dSDag-Erling Smørgrav 	for (i = 0; i < options->num_send_env; ) {
89838a52bd3SEd Maste 		if (!match_pattern(options->send_env[i], arg + 1)) {
899190cef3dSDag-Erling Smørgrav 			i++;
900190cef3dSDag-Erling Smørgrav 			continue;
901190cef3dSDag-Erling Smørgrav 		}
902190cef3dSDag-Erling Smørgrav 		debug3("%s line %d: removing environment %s",
90338a52bd3SEd Maste 		    filename, linenum, options->send_env[i]);
904190cef3dSDag-Erling Smørgrav 		free(options->send_env[i]);
905190cef3dSDag-Erling Smørgrav 		options->send_env[i] = NULL;
906190cef3dSDag-Erling Smørgrav 		for (j = i; j < options->num_send_env - 1; j++) {
907190cef3dSDag-Erling Smørgrav 			options->send_env[j] = options->send_env[j + 1];
908190cef3dSDag-Erling Smørgrav 			options->send_env[j + 1] = NULL;
909190cef3dSDag-Erling Smørgrav 		}
910190cef3dSDag-Erling Smørgrav 		options->num_send_env--;
911190cef3dSDag-Erling Smørgrav 		/* NB. don't increment i */
912190cef3dSDag-Erling Smørgrav 	}
91319261079SEd Maste 	if (onum_send_env != options->num_send_env) {
91419261079SEd Maste 		options->send_env = xrecallocarray(options->send_env,
91519261079SEd Maste 		    onum_send_env, options->num_send_env,
91619261079SEd Maste 		    sizeof(*options->send_env));
91719261079SEd Maste 	}
918190cef3dSDag-Erling Smørgrav }
919190cef3dSDag-Erling Smørgrav 
920511b41d2SMark Murray /*
921ca3176e7SBrian Feldman  * Returns the number of the token pointed to by cp or oBadOption.
922511b41d2SMark Murray  */
923511b41d2SMark Murray static OpCodes
parse_token(const char * cp,const char * filename,int linenum,const char * ignored_unknown)924e4a9863fSDag-Erling Smørgrav parse_token(const char *cp, const char *filename, int linenum,
925e4a9863fSDag-Erling Smørgrav     const char *ignored_unknown)
926511b41d2SMark Murray {
927e4a9863fSDag-Erling Smørgrav 	int i;
928511b41d2SMark Murray 
929511b41d2SMark Murray 	for (i = 0; keywords[i].name; i++)
930e4a9863fSDag-Erling Smørgrav 		if (strcmp(cp, keywords[i].name) == 0)
931511b41d2SMark Murray 			return keywords[i].opcode;
932557f75e5SDag-Erling Smørgrav 	if (ignored_unknown != NULL &&
933557f75e5SDag-Erling Smørgrav 	    match_pattern_list(cp, ignored_unknown, 1) == 1)
934e4a9863fSDag-Erling Smørgrav 		return oIgnoredUnknownOption;
935ca3176e7SBrian Feldman 	error("%s: line %d: Bad configuration option: %s",
936511b41d2SMark Murray 	    filename, linenum, cp);
937511b41d2SMark Murray 	return oBadOption;
938511b41d2SMark Murray }
939511b41d2SMark Murray 
940a91a2465SEd Maste static void
free_canon_cnames(struct allowed_cname * cnames,u_int n)941a91a2465SEd Maste free_canon_cnames(struct allowed_cname *cnames, u_int n)
942a91a2465SEd Maste {
943a91a2465SEd Maste 	u_int i;
944a91a2465SEd Maste 
945a91a2465SEd Maste 	if (cnames == NULL || n == 0)
946a91a2465SEd Maste 		return;
947a91a2465SEd Maste 	for (i = 0; i < n; i++) {
948a91a2465SEd Maste 		free(cnames[i].source_list);
949a91a2465SEd Maste 		free(cnames[i].target_list);
950a91a2465SEd Maste 	}
951a91a2465SEd Maste 	free(cnames);
952a91a2465SEd Maste }
953a91a2465SEd Maste 
954f7167e0eSDag-Erling Smørgrav /* Multistate option parsing */
955f7167e0eSDag-Erling Smørgrav struct multistate {
956f7167e0eSDag-Erling Smørgrav 	char *key;
957f7167e0eSDag-Erling Smørgrav 	int value;
958f7167e0eSDag-Erling Smørgrav };
959f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_flag[] = {
960f7167e0eSDag-Erling Smørgrav 	{ "true",			1 },
961f7167e0eSDag-Erling Smørgrav 	{ "false",			0 },
962f7167e0eSDag-Erling Smørgrav 	{ "yes",			1 },
963f7167e0eSDag-Erling Smørgrav 	{ "no",				0 },
964f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
965f7167e0eSDag-Erling Smørgrav };
966f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_yesnoask[] = {
967f7167e0eSDag-Erling Smørgrav 	{ "true",			1 },
968f7167e0eSDag-Erling Smørgrav 	{ "false",			0 },
969f7167e0eSDag-Erling Smørgrav 	{ "yes",			1 },
970f7167e0eSDag-Erling Smørgrav 	{ "no",				0 },
971f7167e0eSDag-Erling Smørgrav 	{ "ask",			2 },
972f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
973f7167e0eSDag-Erling Smørgrav };
9744f52dfbbSDag-Erling Smørgrav static const struct multistate multistate_strict_hostkey[] = {
9754f52dfbbSDag-Erling Smørgrav 	{ "true",			SSH_STRICT_HOSTKEY_YES },
9764f52dfbbSDag-Erling Smørgrav 	{ "false",			SSH_STRICT_HOSTKEY_OFF },
9774f52dfbbSDag-Erling Smørgrav 	{ "yes",			SSH_STRICT_HOSTKEY_YES },
9784f52dfbbSDag-Erling Smørgrav 	{ "no",				SSH_STRICT_HOSTKEY_OFF },
9794f52dfbbSDag-Erling Smørgrav 	{ "ask",			SSH_STRICT_HOSTKEY_ASK },
9804f52dfbbSDag-Erling Smørgrav 	{ "off",			SSH_STRICT_HOSTKEY_OFF },
9814f52dfbbSDag-Erling Smørgrav 	{ "accept-new",			SSH_STRICT_HOSTKEY_NEW },
9824f52dfbbSDag-Erling Smørgrav 	{ NULL, -1 }
9834f52dfbbSDag-Erling Smørgrav };
984acc1a9efSDag-Erling Smørgrav static const struct multistate multistate_yesnoaskconfirm[] = {
985acc1a9efSDag-Erling Smørgrav 	{ "true",			1 },
986acc1a9efSDag-Erling Smørgrav 	{ "false",			0 },
987acc1a9efSDag-Erling Smørgrav 	{ "yes",			1 },
988acc1a9efSDag-Erling Smørgrav 	{ "no",				0 },
989acc1a9efSDag-Erling Smørgrav 	{ "ask",			2 },
990acc1a9efSDag-Erling Smørgrav 	{ "confirm",			3 },
991acc1a9efSDag-Erling Smørgrav 	{ NULL, -1 }
992acc1a9efSDag-Erling Smørgrav };
993f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_addressfamily[] = {
994f7167e0eSDag-Erling Smørgrav 	{ "inet",			AF_INET },
995f7167e0eSDag-Erling Smørgrav 	{ "inet6",			AF_INET6 },
996f7167e0eSDag-Erling Smørgrav 	{ "any",			AF_UNSPEC },
997f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
998f7167e0eSDag-Erling Smørgrav };
999f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_controlmaster[] = {
1000f7167e0eSDag-Erling Smørgrav 	{ "true",			SSHCTL_MASTER_YES },
1001f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSHCTL_MASTER_YES },
1002f7167e0eSDag-Erling Smørgrav 	{ "false",			SSHCTL_MASTER_NO },
1003f7167e0eSDag-Erling Smørgrav 	{ "no",				SSHCTL_MASTER_NO },
1004f7167e0eSDag-Erling Smørgrav 	{ "auto",			SSHCTL_MASTER_AUTO },
1005f7167e0eSDag-Erling Smørgrav 	{ "ask",			SSHCTL_MASTER_ASK },
1006f7167e0eSDag-Erling Smørgrav 	{ "autoask",			SSHCTL_MASTER_AUTO_ASK },
1007f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
1008f7167e0eSDag-Erling Smørgrav };
1009f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_tunnel[] = {
1010f7167e0eSDag-Erling Smørgrav 	{ "ethernet",			SSH_TUNMODE_ETHERNET },
1011f7167e0eSDag-Erling Smørgrav 	{ "point-to-point",		SSH_TUNMODE_POINTOPOINT },
1012f7167e0eSDag-Erling Smørgrav 	{ "true",			SSH_TUNMODE_DEFAULT },
1013f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSH_TUNMODE_DEFAULT },
1014f7167e0eSDag-Erling Smørgrav 	{ "false",			SSH_TUNMODE_NO },
1015f7167e0eSDag-Erling Smørgrav 	{ "no",				SSH_TUNMODE_NO },
1016f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
1017f7167e0eSDag-Erling Smørgrav };
1018f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_requesttty[] = {
1019f7167e0eSDag-Erling Smørgrav 	{ "true",			REQUEST_TTY_YES },
1020f7167e0eSDag-Erling Smørgrav 	{ "yes",			REQUEST_TTY_YES },
1021f7167e0eSDag-Erling Smørgrav 	{ "false",			REQUEST_TTY_NO },
1022f7167e0eSDag-Erling Smørgrav 	{ "no",				REQUEST_TTY_NO },
1023f7167e0eSDag-Erling Smørgrav 	{ "force",			REQUEST_TTY_FORCE },
1024f7167e0eSDag-Erling Smørgrav 	{ "auto",			REQUEST_TTY_AUTO },
1025f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
1026f7167e0eSDag-Erling Smørgrav };
102719261079SEd Maste static const struct multistate multistate_sessiontype[] = {
102819261079SEd Maste 	{ "none",			SESSION_TYPE_NONE },
102919261079SEd Maste 	{ "subsystem",			SESSION_TYPE_SUBSYSTEM },
103019261079SEd Maste 	{ "default",			SESSION_TYPE_DEFAULT },
103119261079SEd Maste 	{ NULL, -1 }
103219261079SEd Maste };
1033f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_canonicalizehostname[] = {
1034f7167e0eSDag-Erling Smørgrav 	{ "true",			SSH_CANONICALISE_YES },
1035f7167e0eSDag-Erling Smørgrav 	{ "false",			SSH_CANONICALISE_NO },
1036f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSH_CANONICALISE_YES },
1037f7167e0eSDag-Erling Smørgrav 	{ "no",				SSH_CANONICALISE_NO },
1038f7167e0eSDag-Erling Smørgrav 	{ "always",			SSH_CANONICALISE_ALWAYS },
1039f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
1040f7167e0eSDag-Erling Smørgrav };
10411323ec57SEd Maste static const struct multistate multistate_pubkey_auth[] = {
10421323ec57SEd Maste 	{ "true",			SSH_PUBKEY_AUTH_ALL },
10431323ec57SEd Maste 	{ "false",			SSH_PUBKEY_AUTH_NO },
10441323ec57SEd Maste 	{ "yes",			SSH_PUBKEY_AUTH_ALL },
10451323ec57SEd Maste 	{ "no",				SSH_PUBKEY_AUTH_NO },
10461323ec57SEd Maste 	{ "unbound",			SSH_PUBKEY_AUTH_UNBOUND },
10471323ec57SEd Maste 	{ "host-bound",			SSH_PUBKEY_AUTH_HBOUND },
10481323ec57SEd Maste 	{ NULL, -1 }
10491323ec57SEd Maste };
105019261079SEd Maste static const struct multistate multistate_compression[] = {
105119261079SEd Maste #ifdef WITH_ZLIB
10523d9fd9fcSEd Maste 	{ "yes",			COMP_DELAYED },
105319261079SEd Maste #endif
105419261079SEd Maste 	{ "no",				COMP_NONE },
105519261079SEd Maste 	{ NULL, -1 }
105619261079SEd Maste };
105719261079SEd Maste 
105819261079SEd Maste static int
parse_multistate_value(const char * arg,const char * filename,int linenum,const struct multistate * multistate_ptr)105919261079SEd Maste parse_multistate_value(const char *arg, const char *filename, int linenum,
106019261079SEd Maste     const struct multistate *multistate_ptr)
106119261079SEd Maste {
106219261079SEd Maste 	int i;
106319261079SEd Maste 
106419261079SEd Maste 	if (!arg || *arg == '\0') {
106519261079SEd Maste 		error("%s line %d: missing argument.", filename, linenum);
106619261079SEd Maste 		return -1;
106719261079SEd Maste 	}
106819261079SEd Maste 	for (i = 0; multistate_ptr[i].key != NULL; i++) {
106919261079SEd Maste 		if (strcasecmp(arg, multistate_ptr[i].key) == 0)
107019261079SEd Maste 			return multistate_ptr[i].value;
107119261079SEd Maste 	}
107219261079SEd Maste 	return -1;
107319261079SEd Maste }
1074f7167e0eSDag-Erling Smørgrav 
1075511b41d2SMark Murray /*
1076511b41d2SMark Murray  * Processes a single option line as used in the configuration files. This
1077511b41d2SMark Murray  * only sets those values that have not already been set.
1078511b41d2SMark Murray  */
1079511b41d2SMark Murray 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)1080f7167e0eSDag-Erling Smørgrav process_config_line(Options *options, struct passwd *pw, const char *host,
1081bc5531deSDag-Erling Smørgrav     const char *original_host, char *line, const char *filename,
1082bc5531deSDag-Erling Smørgrav     int linenum, int *activep, int flags)
1083511b41d2SMark Murray {
1084076ad2f8SDag-Erling Smørgrav 	return process_config_line_depth(options, pw, host, original_host,
108519261079SEd Maste 	    line, filename, linenum, activep, flags, NULL, 0);
1086076ad2f8SDag-Erling Smørgrav }
1087076ad2f8SDag-Erling Smørgrav 
1088076ad2f8SDag-Erling Smørgrav #define WHITESPACE " \t\r\n"
1089076ad2f8SDag-Erling Smørgrav 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)1090076ad2f8SDag-Erling Smørgrav process_config_line_depth(Options *options, struct passwd *pw, const char *host,
1091076ad2f8SDag-Erling Smørgrav     const char *original_host, char *line, const char *filename,
109219261079SEd Maste     int linenum, int *activep, int flags, int *want_final_pass, int depth)
1093076ad2f8SDag-Erling Smørgrav {
10941323ec57SEd Maste 	char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
109519261079SEd Maste 	char **cpptr, ***cppptr, fwdarg[256];
1096a91a2465SEd Maste 	u_int i, *uintptr, max_entries = 0;
1097076ad2f8SDag-Erling Smørgrav 	int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
1098a91a2465SEd Maste 	int remotefwd, dynamicfwd, ca_only = 0, found = 0;
1099d4af9e69SDag-Erling Smørgrav 	LogLevel *log_level_ptr;
11004f52dfbbSDag-Erling Smørgrav 	SyslogFacility *log_facility_ptr;
1101e4a9863fSDag-Erling Smørgrav 	long long val64;
1102e73e9afaSDag-Erling Smørgrav 	size_t len;
1103a0ee8cc6SDag-Erling Smørgrav 	struct Forward fwd;
1104f7167e0eSDag-Erling Smørgrav 	const struct multistate *multistate_ptr;
1105076ad2f8SDag-Erling Smørgrav 	glob_t gl;
110647dd1d1bSDag-Erling Smørgrav 	const char *errstr;
110719261079SEd Maste 	char **oav = NULL, **av;
110819261079SEd Maste 	int oac = 0, ac;
110919261079SEd Maste 	int ret = -1;
1110a91a2465SEd Maste 	struct allowed_cname *cnames = NULL;
1111a91a2465SEd Maste 	u_int ncnames = 0;
1112a91a2465SEd Maste 	char **strs = NULL; /* string array arguments; freed implicitly */
1113a91a2465SEd Maste 	u_int nstrs = 0;
1114f7167e0eSDag-Erling Smørgrav 
1115f7167e0eSDag-Erling Smørgrav 	if (activep == NULL) { /* We are processing a command line directive */
1116f7167e0eSDag-Erling Smørgrav 		cmdline = 1;
1117f7167e0eSDag-Erling Smørgrav 		activep = &cmdline;
1118f7167e0eSDag-Erling Smørgrav 	}
1119511b41d2SMark Murray 
1120d93a896eSDag-Erling Smørgrav 	/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
1121557f75e5SDag-Erling Smørgrav 	if ((len = strlen(line)) == 0)
1122557f75e5SDag-Erling Smørgrav 		return 0;
1123557f75e5SDag-Erling Smørgrav 	for (len--; len > 0; len--) {
1124d93a896eSDag-Erling Smørgrav 		if (strchr(WHITESPACE "\f", line[len]) == NULL)
1125cf2b5f3bSDag-Erling Smørgrav 			break;
1126cf2b5f3bSDag-Erling Smørgrav 		line[len] = '\0';
1127cf2b5f3bSDag-Erling Smørgrav 	}
1128cf2b5f3bSDag-Erling Smørgrav 
112919261079SEd Maste 	str = line;
1130c2d3a559SKris Kennaway 	/* Get the keyword. (Each line is supposed to begin with a keyword). */
113119261079SEd Maste 	if ((keyword = strdelim(&str)) == NULL)
1132333ee039SDag-Erling Smørgrav 		return 0;
1133c2d3a559SKris Kennaway 	/* Ignore leading whitespace. */
1134c2d3a559SKris Kennaway 	if (*keyword == '\0')
113519261079SEd Maste 		keyword = strdelim(&str);
1136ca3176e7SBrian Feldman 	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
1137511b41d2SMark Murray 		return 0;
1138e4a9863fSDag-Erling Smørgrav 	/* Match lowercase keyword */
1139f7167e0eSDag-Erling Smørgrav 	lowercase(keyword);
1140511b41d2SMark Murray 
114119261079SEd Maste 	/* Prepare to parse remainder of line */
114219261079SEd Maste 	if (str != NULL)
114319261079SEd Maste 		str += strspn(str, WHITESPACE);
114419261079SEd Maste 	if (str == NULL || *str == '\0') {
114519261079SEd Maste 		error("%s line %d: no argument after keyword \"%s\"",
114619261079SEd Maste 		    filename, linenum, keyword);
114719261079SEd Maste 		return -1;
114819261079SEd Maste 	}
1149e4a9863fSDag-Erling Smørgrav 	opcode = parse_token(keyword, filename, linenum,
1150e4a9863fSDag-Erling Smørgrav 	    options->ignored_unknown);
115119261079SEd Maste 	if (argv_split(str, &oac, &oav, 1) != 0) {
115219261079SEd Maste 		error("%s line %d: invalid quotes", filename, linenum);
115319261079SEd Maste 		return -1;
115419261079SEd Maste 	}
115519261079SEd Maste 	ac = oac;
115619261079SEd Maste 	av = oav;
1157511b41d2SMark Murray 
1158511b41d2SMark Murray 	switch (opcode) {
1159511b41d2SMark Murray 	case oBadOption:
1160511b41d2SMark Murray 		/* don't panic, but count bad options */
116119261079SEd Maste 		goto out;
11624f52dfbbSDag-Erling Smørgrav 	case oIgnore:
116319261079SEd Maste 		argv_consume(&ac);
116419261079SEd Maste 		break;
1165e4a9863fSDag-Erling Smørgrav 	case oIgnoredUnknownOption:
1166e4a9863fSDag-Erling Smørgrav 		debug("%s line %d: Ignored unknown option \"%s\"",
1167e4a9863fSDag-Erling Smørgrav 		    filename, linenum, keyword);
116819261079SEd Maste 		argv_consume(&ac);
116919261079SEd Maste 		break;
1170cf2b5f3bSDag-Erling Smørgrav 	case oConnectTimeout:
1171cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->connection_timeout;
11721ec0d754SDag-Erling Smørgrav parse_time:
117319261079SEd Maste 		arg = argv_next(&ac, &av);
117419261079SEd Maste 		if (!arg || *arg == '\0') {
117519261079SEd Maste 			error("%s line %d: missing time value.",
1176cf2b5f3bSDag-Erling Smørgrav 			    filename, linenum);
117719261079SEd Maste 			goto out;
117819261079SEd Maste 		}
1179bc5531deSDag-Erling Smørgrav 		if (strcmp(arg, "none") == 0)
1180bc5531deSDag-Erling Smørgrav 			value = -1;
118119261079SEd Maste 		else if ((value = convtime(arg)) == -1) {
118219261079SEd Maste 			error("%s line %d: invalid time value.",
1183cf2b5f3bSDag-Erling Smørgrav 			    filename, linenum);
118419261079SEd Maste 			goto out;
118519261079SEd Maste 		}
1186d4af9e69SDag-Erling Smørgrav 		if (*activep && *intptr == -1)
1187cf2b5f3bSDag-Erling Smørgrav 			*intptr = value;
1188cf2b5f3bSDag-Erling Smørgrav 		break;
1189cf2b5f3bSDag-Erling Smørgrav 
1190511b41d2SMark Murray 	case oForwardAgent:
1191511b41d2SMark Murray 		intptr = &options->forward_agent;
119219261079SEd Maste 
119319261079SEd Maste 		arg = argv_next(&ac, &av);
119419261079SEd Maste 		if (!arg || *arg == '\0') {
119519261079SEd Maste 			error("%s line %d: missing argument.",
1196f7167e0eSDag-Erling Smørgrav 			    filename, linenum);
119719261079SEd Maste 			goto out;
119819261079SEd Maste 		}
119919261079SEd Maste 
1200f7167e0eSDag-Erling Smørgrav 		value = -1;
120119261079SEd Maste 		multistate_ptr = multistate_flag;
1202f7167e0eSDag-Erling Smørgrav 		for (i = 0; multistate_ptr[i].key != NULL; i++) {
1203f7167e0eSDag-Erling Smørgrav 			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
1204f7167e0eSDag-Erling Smørgrav 				value = multistate_ptr[i].value;
1205f7167e0eSDag-Erling Smørgrav 				break;
1206f7167e0eSDag-Erling Smørgrav 			}
1207f7167e0eSDag-Erling Smørgrav 		}
120819261079SEd Maste 		if (value != -1) {
1209511b41d2SMark Murray 			if (*activep && *intptr == -1)
1210511b41d2SMark Murray 				*intptr = value;
1211511b41d2SMark Murray 			break;
121219261079SEd Maste 		}
121319261079SEd Maste 		/* ForwardAgent wasn't 'yes' or 'no', assume a path */
121419261079SEd Maste 		if (*activep && *intptr == -1)
121519261079SEd Maste 			*intptr = 1;
121619261079SEd Maste 
121719261079SEd Maste 		charptr = &options->forward_agent_sock_path;
121819261079SEd Maste 		goto parse_agent_path;
1219511b41d2SMark Murray 
1220511b41d2SMark Murray 	case oForwardX11:
1221511b41d2SMark Murray 		intptr = &options->forward_x11;
122219261079SEd Maste  parse_flag:
122319261079SEd Maste 		multistate_ptr = multistate_flag;
122419261079SEd Maste  parse_multistate:
122519261079SEd Maste 		arg = argv_next(&ac, &av);
122619261079SEd Maste 		if ((value = parse_multistate_value(arg, filename, linenum,
122719261079SEd Maste 		    multistate_ptr)) == -1) {
122819261079SEd Maste 			error("%s line %d: unsupported option \"%s\".",
122919261079SEd Maste 			    filename, linenum, arg);
123019261079SEd Maste 			goto out;
123119261079SEd Maste 		}
123219261079SEd Maste 		if (*activep && *intptr == -1)
123319261079SEd Maste 			*intptr = value;
123419261079SEd Maste 		break;
1235511b41d2SMark Murray 
12361ec0d754SDag-Erling Smørgrav 	case oForwardX11Trusted:
12371ec0d754SDag-Erling Smørgrav 		intptr = &options->forward_x11_trusted;
12381ec0d754SDag-Erling Smørgrav 		goto parse_flag;
12391ec0d754SDag-Erling Smørgrav 
1240e2f6069cSDag-Erling Smørgrav 	case oForwardX11Timeout:
1241e2f6069cSDag-Erling Smørgrav 		intptr = &options->forward_x11_timeout;
1242e2f6069cSDag-Erling Smørgrav 		goto parse_time;
1243e2f6069cSDag-Erling Smørgrav 
1244511b41d2SMark Murray 	case oGatewayPorts:
1245a0ee8cc6SDag-Erling Smørgrav 		intptr = &options->fwd_opts.gateway_ports;
1246511b41d2SMark Murray 		goto parse_flag;
1247511b41d2SMark Murray 
1248333ee039SDag-Erling Smørgrav 	case oExitOnForwardFailure:
1249333ee039SDag-Erling Smørgrav 		intptr = &options->exit_on_forward_failure;
1250333ee039SDag-Erling Smørgrav 		goto parse_flag;
1251333ee039SDag-Erling Smørgrav 
1252511b41d2SMark Murray 	case oPasswordAuthentication:
1253511b41d2SMark Murray 		intptr = &options->password_authentication;
1254511b41d2SMark Murray 		goto parse_flag;
1255511b41d2SMark Murray 
125609958426SBrian Feldman 	case oKbdInteractiveAuthentication:
125709958426SBrian Feldman 		intptr = &options->kbd_interactive_authentication;
125809958426SBrian Feldman 		goto parse_flag;
125909958426SBrian Feldman 
126009958426SBrian Feldman 	case oKbdInteractiveDevices:
126109958426SBrian Feldman 		charptr = &options->kbd_interactive_devices;
126209958426SBrian Feldman 		goto parse_string;
126309958426SBrian Feldman 
1264ca3176e7SBrian Feldman 	case oPubkeyAuthentication:
12651323ec57SEd Maste 		multistate_ptr = multistate_pubkey_auth;
1266ca3176e7SBrian Feldman 		intptr = &options->pubkey_authentication;
12671323ec57SEd Maste 		goto parse_multistate;
1268e8aafc91SKris Kennaway 
1269ca3176e7SBrian Feldman 	case oHostbasedAuthentication:
1270ca3176e7SBrian Feldman 		intptr = &options->hostbased_authentication;
1271511b41d2SMark Murray 		goto parse_flag;
1272511b41d2SMark Murray 
1273cf2b5f3bSDag-Erling Smørgrav 	case oGssAuthentication:
1274cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->gss_authentication;
1275511b41d2SMark Murray 		goto parse_flag;
1276cf2b5f3bSDag-Erling Smørgrav 
1277cf2b5f3bSDag-Erling Smørgrav 	case oGssDelegateCreds:
1278cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->gss_deleg_creds;
1279ca3176e7SBrian Feldman 		goto parse_flag;
1280cf2b5f3bSDag-Erling Smørgrav 
1281511b41d2SMark Murray 	case oBatchMode:
1282511b41d2SMark Murray 		intptr = &options->batch_mode;
1283511b41d2SMark Murray 		goto parse_flag;
1284511b41d2SMark Murray 
1285511b41d2SMark Murray 	case oCheckHostIP:
1286511b41d2SMark Murray 		intptr = &options->check_host_ip;
1287511b41d2SMark Murray 		goto parse_flag;
1288511b41d2SMark Murray 
1289cf2b5f3bSDag-Erling Smørgrav 	case oVerifyHostKeyDNS:
1290cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->verify_host_key_dns;
1291f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_yesnoask;
1292f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1293cf2b5f3bSDag-Erling Smørgrav 
1294511b41d2SMark Murray 	case oStrictHostKeyChecking:
1295511b41d2SMark Murray 		intptr = &options->strict_host_key_checking;
12964f52dfbbSDag-Erling Smørgrav 		multistate_ptr = multistate_strict_hostkey;
1297f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1298511b41d2SMark Murray 
1299511b41d2SMark Murray 	case oCompression:
1300511b41d2SMark Murray 		intptr = &options->compression;
130119261079SEd Maste 		multistate_ptr = multistate_compression;
130219261079SEd Maste 		goto parse_multistate;
1303511b41d2SMark Murray 
13041ec0d754SDag-Erling Smørgrav 	case oTCPKeepAlive:
13051ec0d754SDag-Erling Smørgrav 		intptr = &options->tcp_keep_alive;
1306511b41d2SMark Murray 		goto parse_flag;
1307511b41d2SMark Murray 
1308af12a3e7SDag-Erling Smørgrav 	case oNoHostAuthenticationForLocalhost:
1309af12a3e7SDag-Erling Smørgrav 		intptr = &options->no_host_authentication_for_localhost;
1310af12a3e7SDag-Erling Smørgrav 		goto parse_flag;
1311af12a3e7SDag-Erling Smørgrav 
1312511b41d2SMark Murray 	case oNumberOfPasswordPrompts:
1313511b41d2SMark Murray 		intptr = &options->number_of_password_prompts;
1314511b41d2SMark Murray 		goto parse_int;
1315511b41d2SMark Murray 
1316cf2b5f3bSDag-Erling Smørgrav 	case oRekeyLimit:
131719261079SEd Maste 		arg = argv_next(&ac, &av);
131819261079SEd Maste 		if (!arg || *arg == '\0') {
131919261079SEd Maste 			error("%.200s line %d: Missing argument.", filename,
1320e4a9863fSDag-Erling Smørgrav 			    linenum);
132119261079SEd Maste 			goto out;
132219261079SEd Maste 		}
1323e4a9863fSDag-Erling Smørgrav 		if (strcmp(arg, "default") == 0) {
1324e4a9863fSDag-Erling Smørgrav 			val64 = 0;
1325e4a9863fSDag-Erling Smørgrav 		} else {
132619261079SEd Maste 			if (scan_scaled(arg, &val64) == -1) {
132719261079SEd Maste 				error("%.200s line %d: Bad number '%s': %s",
1328e4a9863fSDag-Erling Smørgrav 				    filename, linenum, arg, strerror(errno));
132919261079SEd Maste 				goto out;
133019261079SEd Maste 			}
133119261079SEd Maste 			if (val64 != 0 && val64 < 16) {
133219261079SEd Maste 				error("%.200s line %d: RekeyLimit too small",
1333333ee039SDag-Erling Smørgrav 				    filename, linenum);
133419261079SEd Maste 				goto out;
133519261079SEd Maste 			}
1336e4a9863fSDag-Erling Smørgrav 		}
1337d4af9e69SDag-Erling Smørgrav 		if (*activep && options->rekey_limit == -1)
1338acc1a9efSDag-Erling Smørgrav 			options->rekey_limit = val64;
133919261079SEd Maste 		if (ac != 0) { /* optional rekey interval present */
134019261079SEd Maste 			if (strcmp(av[0], "none") == 0) {
134119261079SEd Maste 				(void)argv_next(&ac, &av);	/* discard */
1342e4a9863fSDag-Erling Smørgrav 				break;
1343e4a9863fSDag-Erling Smørgrav 			}
1344e4a9863fSDag-Erling Smørgrav 			intptr = &options->rekey_interval;
1345e4a9863fSDag-Erling Smørgrav 			goto parse_time;
1346e4a9863fSDag-Erling Smørgrav 		}
1347cf2b5f3bSDag-Erling Smørgrav 		break;
1348cf2b5f3bSDag-Erling Smørgrav 
1349511b41d2SMark Murray 	case oIdentityFile:
135019261079SEd Maste 		arg = argv_next(&ac, &av);
135119261079SEd Maste 		if (!arg || *arg == '\0') {
135219261079SEd Maste 			error("%.200s line %d: Missing argument.",
135319261079SEd Maste 			    filename, linenum);
135419261079SEd Maste 			goto out;
135519261079SEd Maste 		}
1356511b41d2SMark Murray 		if (*activep) {
1357ca3176e7SBrian Feldman 			intptr = &options->num_identity_files;
135819261079SEd Maste 			if (*intptr >= SSH_MAX_IDENTITY_FILES) {
135919261079SEd Maste 				error("%.200s line %d: Too many identity files "
136019261079SEd Maste 				    "specified (max %d).", filename, linenum,
136119261079SEd Maste 				    SSH_MAX_IDENTITY_FILES);
136219261079SEd Maste 				goto out;
136319261079SEd Maste 			}
1364bc5531deSDag-Erling Smørgrav 			add_identity_file(options, NULL,
1365bc5531deSDag-Erling Smørgrav 			    arg, flags & SSHCONF_USERCONF);
1366511b41d2SMark Murray 		}
1367511b41d2SMark Murray 		break;
1368511b41d2SMark Murray 
1369acc1a9efSDag-Erling Smørgrav 	case oCertificateFile:
137019261079SEd Maste 		arg = argv_next(&ac, &av);
137119261079SEd Maste 		if (!arg || *arg == '\0') {
137219261079SEd Maste 			error("%.200s line %d: Missing argument.",
1373acc1a9efSDag-Erling Smørgrav 			    filename, linenum);
137419261079SEd Maste 			goto out;
137519261079SEd Maste 		}
1376acc1a9efSDag-Erling Smørgrav 		if (*activep) {
1377acc1a9efSDag-Erling Smørgrav 			intptr = &options->num_certificate_files;
1378acc1a9efSDag-Erling Smørgrav 			if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
137919261079SEd Maste 				error("%.200s line %d: Too many certificate "
1380acc1a9efSDag-Erling Smørgrav 				    "files specified (max %d).",
1381acc1a9efSDag-Erling Smørgrav 				    filename, linenum,
1382acc1a9efSDag-Erling Smørgrav 				    SSH_MAX_CERTIFICATE_FILES);
138319261079SEd Maste 				goto out;
1384acc1a9efSDag-Erling Smørgrav 			}
1385acc1a9efSDag-Erling Smørgrav 			add_certificate_file(options, arg,
1386acc1a9efSDag-Erling Smørgrav 			    flags & SSHCONF_USERCONF);
1387acc1a9efSDag-Erling Smørgrav 		}
1388acc1a9efSDag-Erling Smørgrav 		break;
1389acc1a9efSDag-Erling Smørgrav 
1390c2d3a559SKris Kennaway 	case oXAuthLocation:
1391c2d3a559SKris Kennaway 		charptr=&options->xauth_location;
1392c2d3a559SKris Kennaway 		goto parse_string;
1393c2d3a559SKris Kennaway 
1394511b41d2SMark Murray 	case oUser:
1395511b41d2SMark Murray 		charptr = &options->user;
1396511b41d2SMark Murray parse_string:
139719261079SEd Maste 		arg = argv_next(&ac, &av);
139819261079SEd Maste 		if (!arg || *arg == '\0') {
139919261079SEd Maste 			error("%.200s line %d: Missing argument.",
1400e146993eSDag-Erling Smørgrav 			    filename, linenum);
140119261079SEd Maste 			goto out;
140219261079SEd Maste 		}
1403511b41d2SMark Murray 		if (*activep && *charptr == NULL)
1404c2d3a559SKris Kennaway 			*charptr = xstrdup(arg);
1405511b41d2SMark Murray 		break;
1406511b41d2SMark Murray 
1407511b41d2SMark Murray 	case oGlobalKnownHostsFile:
1408e146993eSDag-Erling Smørgrav 		cpptr = (char **)&options->system_hostfiles;
1409e146993eSDag-Erling Smørgrav 		uintptr = &options->num_system_hostfiles;
1410e146993eSDag-Erling Smørgrav 		max_entries = SSH_MAX_HOSTS_FILES;
1411e146993eSDag-Erling Smørgrav parse_char_array:
141219261079SEd Maste 		i = 0;
141319261079SEd Maste 		value = *uintptr == 0; /* was array empty when we started? */
141419261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
141519261079SEd Maste 			if (*arg == '\0') {
141619261079SEd Maste 				error("%s line %d: keyword %s empty argument",
141719261079SEd Maste 				    filename, linenum, keyword);
141819261079SEd Maste 				goto out;
141919261079SEd Maste 			}
142019261079SEd Maste 			/* Allow "none" only in first position */
142119261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
142219261079SEd Maste 				if (i > 0 || ac > 0) {
142319261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
142419261079SEd Maste 					    "argument must appear alone.",
142519261079SEd Maste 					    filename, linenum, keyword);
142619261079SEd Maste 					goto out;
142719261079SEd Maste 				}
142819261079SEd Maste 			}
142919261079SEd Maste 			i++;
143019261079SEd Maste 			if (*activep && value) {
143119261079SEd Maste 				if ((*uintptr) >= max_entries) {
143219261079SEd Maste 					error("%s line %d: too many %s "
143319261079SEd Maste 					    "entries.", filename, linenum,
143419261079SEd Maste 					    keyword);
143519261079SEd Maste 					goto out;
143619261079SEd Maste 				}
1437e146993eSDag-Erling Smørgrav 				cpptr[(*uintptr)++] = xstrdup(arg);
1438e146993eSDag-Erling Smørgrav 			}
1439e146993eSDag-Erling Smørgrav 		}
144019261079SEd Maste 		break;
1441511b41d2SMark Murray 
1442511b41d2SMark Murray 	case oUserKnownHostsFile:
1443e146993eSDag-Erling Smørgrav 		cpptr = (char **)&options->user_hostfiles;
1444e146993eSDag-Erling Smørgrav 		uintptr = &options->num_user_hostfiles;
1445e146993eSDag-Erling Smørgrav 		max_entries = SSH_MAX_HOSTS_FILES;
1446e146993eSDag-Erling Smørgrav 		goto parse_char_array;
1447e8aafc91SKris Kennaway 
144819261079SEd Maste 	case oHostname:
1449511b41d2SMark Murray 		charptr = &options->hostname;
1450511b41d2SMark Murray 		goto parse_string;
1451511b41d2SMark Murray 
1452535af610SEd Maste 	case oTag:
1453535af610SEd Maste 		charptr = &options->tag;
1454535af610SEd Maste 		goto parse_string;
1455535af610SEd Maste 
1456ca3176e7SBrian Feldman 	case oHostKeyAlias:
1457ca3176e7SBrian Feldman 		charptr = &options->host_key_alias;
1458ca3176e7SBrian Feldman 		goto parse_string;
1459ca3176e7SBrian Feldman 
1460ca3176e7SBrian Feldman 	case oPreferredAuthentications:
1461ca3176e7SBrian Feldman 		charptr = &options->preferred_authentications;
1462ca3176e7SBrian Feldman 		goto parse_string;
1463ca3176e7SBrian Feldman 
1464af12a3e7SDag-Erling Smørgrav 	case oBindAddress:
1465af12a3e7SDag-Erling Smørgrav 		charptr = &options->bind_address;
1466af12a3e7SDag-Erling Smørgrav 		goto parse_string;
1467af12a3e7SDag-Erling Smørgrav 
146847dd1d1bSDag-Erling Smørgrav 	case oBindInterface:
146947dd1d1bSDag-Erling Smørgrav 		charptr = &options->bind_interface;
147047dd1d1bSDag-Erling Smørgrav 		goto parse_string;
147147dd1d1bSDag-Erling Smørgrav 
1472b15c8340SDag-Erling Smørgrav 	case oPKCS11Provider:
1473b15c8340SDag-Erling Smørgrav 		charptr = &options->pkcs11_provider;
1474af12a3e7SDag-Erling Smørgrav 		goto parse_string;
1475af12a3e7SDag-Erling Smørgrav 
147619261079SEd Maste 	case oSecurityKeyProvider:
147719261079SEd Maste 		charptr = &options->sk_provider;
147819261079SEd Maste 		goto parse_string;
147919261079SEd Maste 
148019261079SEd Maste 	case oKnownHostsCommand:
148119261079SEd Maste 		charptr = &options->known_hosts_command;
148219261079SEd Maste 		goto parse_command;
148319261079SEd Maste 
1484511b41d2SMark Murray 	case oProxyCommand:
1485b74df5b2SDag-Erling Smørgrav 		charptr = &options->proxy_command;
1486076ad2f8SDag-Erling Smørgrav 		/* Ignore ProxyCommand if ProxyJump already specified */
1487076ad2f8SDag-Erling Smørgrav 		if (options->jump_host != NULL)
1488076ad2f8SDag-Erling Smørgrav 			charptr = &options->jump_host; /* Skip below */
1489b74df5b2SDag-Erling Smørgrav parse_command:
149019261079SEd Maste 		if (str == NULL) {
149119261079SEd Maste 			error("%.200s line %d: Missing argument.",
149219261079SEd Maste 			    filename, linenum);
149319261079SEd Maste 			goto out;
149419261079SEd Maste 		}
149519261079SEd Maste 		len = strspn(str, WHITESPACE "=");
1496511b41d2SMark Murray 		if (*activep && *charptr == NULL)
149719261079SEd Maste 			*charptr = xstrdup(str + len);
149819261079SEd Maste 		argv_consume(&ac);
149919261079SEd Maste 		break;
1500511b41d2SMark Murray 
1501076ad2f8SDag-Erling Smørgrav 	case oProxyJump:
150219261079SEd Maste 		if (str == NULL) {
150319261079SEd Maste 			error("%.200s line %d: Missing argument.",
1504076ad2f8SDag-Erling Smørgrav 			    filename, linenum);
150519261079SEd Maste 			goto out;
1506076ad2f8SDag-Erling Smørgrav 		}
150719261079SEd Maste 		len = strspn(str, WHITESPACE "=");
150819261079SEd Maste 		/* XXX use argv? */
150919261079SEd Maste 		if (parse_jump(str + len, options, *activep) == -1) {
151019261079SEd Maste 			error("%.200s line %d: Invalid ProxyJump \"%s\"",
151119261079SEd Maste 			    filename, linenum, str + len);
151219261079SEd Maste 			goto out;
1513076ad2f8SDag-Erling Smørgrav 		}
151419261079SEd Maste 		argv_consume(&ac);
151519261079SEd Maste 		break;
1516076ad2f8SDag-Erling Smørgrav 
1517511b41d2SMark Murray 	case oPort:
151819261079SEd Maste 		arg = argv_next(&ac, &av);
151919261079SEd Maste 		if (!arg || *arg == '\0') {
152019261079SEd Maste 			error("%.200s line %d: Missing argument.",
15212f513db7SEd Maste 			    filename, linenum);
152219261079SEd Maste 			goto out;
152319261079SEd Maste 		}
15242f513db7SEd Maste 		value = a2port(arg);
152519261079SEd Maste 		if (value <= 0) {
152619261079SEd Maste 			error("%.200s line %d: Bad port '%s'.",
15272f513db7SEd Maste 			    filename, linenum, arg);
152819261079SEd Maste 			goto out;
152919261079SEd Maste 		}
15302f513db7SEd Maste 		if (*activep && options->port == -1)
15312f513db7SEd Maste 			options->port = value;
15322f513db7SEd Maste 		break;
15332f513db7SEd Maste 
15342f513db7SEd Maste 	case oConnectionAttempts:
15352f513db7SEd Maste 		intptr = &options->connection_attempts;
1536511b41d2SMark Murray parse_int:
153719261079SEd Maste 		arg = argv_next(&ac, &av);
153819261079SEd Maste 		if ((errstr = atoi_err(arg, &value)) != NULL) {
153919261079SEd Maste 			error("%s line %d: integer value %s.",
154047dd1d1bSDag-Erling Smørgrav 			    filename, linenum, errstr);
154119261079SEd Maste 			goto out;
154219261079SEd Maste 		}
1543511b41d2SMark Murray 		if (*activep && *intptr == -1)
1544511b41d2SMark Murray 			*intptr = value;
1545511b41d2SMark Murray 		break;
1546511b41d2SMark Murray 
1547e8aafc91SKris Kennaway 	case oCiphers:
154819261079SEd Maste 		arg = argv_next(&ac, &av);
154919261079SEd Maste 		if (!arg || *arg == '\0') {
155019261079SEd Maste 			error("%.200s line %d: Missing argument.",
155119261079SEd Maste 			    filename, linenum);
155219261079SEd Maste 			goto out;
155319261079SEd Maste 		}
155419261079SEd Maste 		if (*arg != '-' &&
155519261079SEd Maste 		    !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
155619261079SEd Maste 			error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
1557c2d3a559SKris Kennaway 			    filename, linenum, arg ? arg : "<NONE>");
155819261079SEd Maste 			goto out;
155919261079SEd Maste 		}
1560e8aafc91SKris Kennaway 		if (*activep && options->ciphers == NULL)
1561c2d3a559SKris Kennaway 			options->ciphers = xstrdup(arg);
1562e8aafc91SKris Kennaway 		break;
1563e8aafc91SKris Kennaway 
1564ca3176e7SBrian Feldman 	case oMacs:
156519261079SEd Maste 		arg = argv_next(&ac, &av);
156619261079SEd Maste 		if (!arg || *arg == '\0') {
156719261079SEd Maste 			error("%.200s line %d: Missing argument.",
156819261079SEd Maste 			    filename, linenum);
156919261079SEd Maste 			goto out;
157019261079SEd Maste 		}
157119261079SEd Maste 		if (*arg != '-' &&
157219261079SEd Maste 		    !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
157319261079SEd Maste 			error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
1574ca3176e7SBrian Feldman 			    filename, linenum, arg ? arg : "<NONE>");
157519261079SEd Maste 			goto out;
157619261079SEd Maste 		}
1577ca3176e7SBrian Feldman 		if (*activep && options->macs == NULL)
1578ca3176e7SBrian Feldman 			options->macs = xstrdup(arg);
1579ca3176e7SBrian Feldman 		break;
1580ca3176e7SBrian Feldman 
15814a421b63SDag-Erling Smørgrav 	case oKexAlgorithms:
158219261079SEd Maste 		arg = argv_next(&ac, &av);
158319261079SEd Maste 		if (!arg || *arg == '\0') {
158419261079SEd Maste 			error("%.200s line %d: Missing argument.",
15854a421b63SDag-Erling Smørgrav 			    filename, linenum);
158619261079SEd Maste 			goto out;
158719261079SEd Maste 		}
1588d93a896eSDag-Erling Smørgrav 		if (*arg != '-' &&
158919261079SEd Maste 		    !kex_names_valid(*arg == '+' || *arg == '^' ?
159019261079SEd Maste 		    arg + 1 : arg)) {
159119261079SEd Maste 			error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
15924a421b63SDag-Erling Smørgrav 			    filename, linenum, arg ? arg : "<NONE>");
159319261079SEd Maste 			goto out;
159419261079SEd Maste 		}
15954a421b63SDag-Erling Smørgrav 		if (*activep && options->kex_algorithms == NULL)
15964a421b63SDag-Erling Smørgrav 			options->kex_algorithms = xstrdup(arg);
15974a421b63SDag-Erling Smørgrav 		break;
15984a421b63SDag-Erling Smørgrav 
1599ca3176e7SBrian Feldman 	case oHostKeyAlgorithms:
1600eccfee6eSDag-Erling Smørgrav 		charptr = &options->hostkeyalgorithms;
1601535af610SEd Maste 		ca_only = 0;
160219261079SEd Maste parse_pubkey_algos:
160319261079SEd Maste 		arg = argv_next(&ac, &av);
160419261079SEd Maste 		if (!arg || *arg == '\0') {
160519261079SEd Maste 			error("%.200s line %d: Missing argument.",
1606eccfee6eSDag-Erling Smørgrav 			    filename, linenum);
160719261079SEd Maste 			goto out;
160819261079SEd Maste 		}
1609d93a896eSDag-Erling Smørgrav 		if (*arg != '-' &&
161019261079SEd Maste 		    !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
1611535af610SEd Maste 		    arg + 1 : arg, 1, ca_only)) {
161219261079SEd Maste 			error("%s line %d: Bad key types '%s'.",
1613ca3176e7SBrian Feldman 			    filename, linenum, arg ? arg : "<NONE>");
161419261079SEd Maste 			goto out;
161519261079SEd Maste 		}
1616eccfee6eSDag-Erling Smørgrav 		if (*activep && *charptr == NULL)
1617eccfee6eSDag-Erling Smørgrav 			*charptr = xstrdup(arg);
1618ca3176e7SBrian Feldman 		break;
1619ca3176e7SBrian Feldman 
16202f513db7SEd Maste 	case oCASignatureAlgorithms:
16212f513db7SEd Maste 		charptr = &options->ca_sign_algorithms;
1622535af610SEd Maste 		ca_only = 1;
162319261079SEd Maste 		goto parse_pubkey_algos;
16242f513db7SEd Maste 
1625511b41d2SMark Murray 	case oLogLevel:
1626d4af9e69SDag-Erling Smørgrav 		log_level_ptr = &options->log_level;
162719261079SEd Maste 		arg = argv_next(&ac, &av);
1628c2d3a559SKris Kennaway 		value = log_level_number(arg);
162919261079SEd Maste 		if (value == SYSLOG_LEVEL_NOT_SET) {
163019261079SEd Maste 			error("%.200s line %d: unsupported log level '%s'",
1631c2d3a559SKris Kennaway 			    filename, linenum, arg ? arg : "<NONE>");
163219261079SEd Maste 			goto out;
163319261079SEd Maste 		}
1634d4af9e69SDag-Erling Smørgrav 		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
1635d4af9e69SDag-Erling Smørgrav 			*log_level_ptr = (LogLevel) value;
1636511b41d2SMark Murray 		break;
1637511b41d2SMark Murray 
16384f52dfbbSDag-Erling Smørgrav 	case oLogFacility:
16394f52dfbbSDag-Erling Smørgrav 		log_facility_ptr = &options->log_facility;
164019261079SEd Maste 		arg = argv_next(&ac, &av);
16414f52dfbbSDag-Erling Smørgrav 		value = log_facility_number(arg);
164219261079SEd Maste 		if (value == SYSLOG_FACILITY_NOT_SET) {
164319261079SEd Maste 			error("%.200s line %d: unsupported log facility '%s'",
16444f52dfbbSDag-Erling Smørgrav 			    filename, linenum, arg ? arg : "<NONE>");
164519261079SEd Maste 			goto out;
164619261079SEd Maste 		}
16474f52dfbbSDag-Erling Smørgrav 		if (*log_facility_ptr == -1)
16484f52dfbbSDag-Erling Smørgrav 			*log_facility_ptr = (SyslogFacility) value;
16494f52dfbbSDag-Erling Smørgrav 		break;
16504f52dfbbSDag-Erling Smørgrav 
165119261079SEd Maste 	case oLogVerbose:
165219261079SEd Maste 		cppptr = &options->log_verbose;
165319261079SEd Maste 		uintptr = &options->num_log_verbose;
165419261079SEd Maste 		i = 0;
165519261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
165619261079SEd Maste 			if (*arg == '\0') {
165719261079SEd Maste 				error("%s line %d: keyword %s empty argument",
165819261079SEd Maste 				    filename, linenum, keyword);
165919261079SEd Maste 				goto out;
166019261079SEd Maste 			}
166119261079SEd Maste 			/* Allow "none" only in first position */
166219261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
166319261079SEd Maste 				if (i > 0 || ac > 0) {
166419261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
166519261079SEd Maste 					    "argument must appear alone.",
166619261079SEd Maste 					    filename, linenum, keyword);
166719261079SEd Maste 					goto out;
166819261079SEd Maste 				}
166919261079SEd Maste 			}
167019261079SEd Maste 			i++;
167119261079SEd Maste 			if (*activep && *uintptr == 0) {
167219261079SEd Maste 				*cppptr = xrecallocarray(*cppptr, *uintptr,
167319261079SEd Maste 				    *uintptr + 1, sizeof(**cppptr));
167419261079SEd Maste 				(*cppptr)[(*uintptr)++] = xstrdup(arg);
167519261079SEd Maste 			}
167619261079SEd Maste 		}
167719261079SEd Maste 		break;
167819261079SEd Maste 
1679af12a3e7SDag-Erling Smørgrav 	case oLocalForward:
1680511b41d2SMark Murray 	case oRemoteForward:
1681cce7d346SDag-Erling Smørgrav 	case oDynamicForward:
168219261079SEd Maste 		arg = argv_next(&ac, &av);
168319261079SEd Maste 		if (!arg || *arg == '\0') {
168419261079SEd Maste 			error("%.200s line %d: Missing argument.",
1685af12a3e7SDag-Erling Smørgrav 			    filename, linenum);
168619261079SEd Maste 			goto out;
168719261079SEd Maste 		}
1688cce7d346SDag-Erling Smørgrav 
16894f52dfbbSDag-Erling Smørgrav 		remotefwd = (opcode == oRemoteForward);
16904f52dfbbSDag-Erling Smørgrav 		dynamicfwd = (opcode == oDynamicForward);
16914f52dfbbSDag-Erling Smørgrav 
16924f52dfbbSDag-Erling Smørgrav 		if (!dynamicfwd) {
169319261079SEd Maste 			arg2 = argv_next(&ac, &av);
16944f52dfbbSDag-Erling Smørgrav 			if (arg2 == NULL || *arg2 == '\0') {
16954f52dfbbSDag-Erling Smørgrav 				if (remotefwd)
16964f52dfbbSDag-Erling Smørgrav 					dynamicfwd = 1;
169719261079SEd Maste 				else {
169819261079SEd Maste 					error("%.200s line %d: Missing target "
16994f52dfbbSDag-Erling Smørgrav 					    "argument.", filename, linenum);
170019261079SEd Maste 					goto out;
170119261079SEd Maste 				}
17024f52dfbbSDag-Erling Smørgrav 			} else {
1703aa49c926SDag-Erling Smørgrav 				/* construct a string for parse_forward */
17044f52dfbbSDag-Erling Smørgrav 				snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
17054f52dfbbSDag-Erling Smørgrav 				    arg2);
1706cce7d346SDag-Erling Smørgrav 			}
17074f52dfbbSDag-Erling Smørgrav 		}
17084f52dfbbSDag-Erling Smørgrav 		if (dynamicfwd)
17094f52dfbbSDag-Erling Smørgrav 			strlcpy(fwdarg, arg, sizeof(fwdarg));
1710aa49c926SDag-Erling Smørgrav 
171119261079SEd Maste 		if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
171219261079SEd Maste 			error("%.200s line %d: Bad forwarding specification.",
1713511b41d2SMark Murray 			    filename, linenum);
171419261079SEd Maste 			goto out;
171519261079SEd Maste 		}
1716aa49c926SDag-Erling Smørgrav 
1717af12a3e7SDag-Erling Smørgrav 		if (*activep) {
17184f52dfbbSDag-Erling Smørgrav 			if (remotefwd) {
1719aa49c926SDag-Erling Smørgrav 				add_remote_forward(options, &fwd);
17204f52dfbbSDag-Erling Smørgrav 			} else {
17214f52dfbbSDag-Erling Smørgrav 				add_local_forward(options, &fwd);
17224f52dfbbSDag-Erling Smørgrav 			}
1723af12a3e7SDag-Erling Smørgrav 		}
1724511b41d2SMark Murray 		break;
1725511b41d2SMark Murray 
172619261079SEd Maste 	case oPermitRemoteOpen:
172719261079SEd Maste 		uintptr = &options->num_permitted_remote_opens;
172819261079SEd Maste 		cppptr = &options->permitted_remote_opens;
1729a91a2465SEd Maste 		found = *uintptr == 0;
173019261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
173119261079SEd Maste 			arg2 = xstrdup(arg);
173294e21addSEd Maste 			/* Allow any/none only in first position */
173394e21addSEd Maste 			if (strcasecmp(arg, "none") == 0 ||
173494e21addSEd Maste 			    strcasecmp(arg, "any") == 0) {
1735a91a2465SEd Maste 				if (nstrs > 0 || ac > 0) {
173694e21addSEd Maste 					error("%s line %d: keyword %s \"%s\" "
173794e21addSEd Maste 					    "argument must appear alone.",
173894e21addSEd Maste 					    filename, linenum, keyword, arg);
1739535af610SEd Maste 					free(arg2);
174094e21addSEd Maste 					goto out;
174194e21addSEd Maste 				}
174294e21addSEd Maste 			} else {
17431323ec57SEd Maste 				p = hpdelim(&arg);
17441323ec57SEd Maste 				if (p == NULL) {
174519261079SEd Maste 					fatal("%s line %d: missing host in %s",
174619261079SEd Maste 					    filename, linenum,
174719261079SEd Maste 					    lookup_opcode_name(opcode));
174819261079SEd Maste 				}
174919261079SEd Maste 				p = cleanhostname(p);
175019261079SEd Maste 				/*
175119261079SEd Maste 				 * don't want to use permitopen_port to avoid
175219261079SEd Maste 				 * dependency on channels.[ch] here.
175319261079SEd Maste 				 */
175494e21addSEd Maste 				if (arg == NULL || (strcmp(arg, "*") != 0 &&
175594e21addSEd Maste 				    a2port(arg) <= 0)) {
175694e21addSEd Maste 					fatal("%s line %d: bad port number "
175794e21addSEd Maste 					    "in %s", filename, linenum,
175819261079SEd Maste 					    lookup_opcode_name(opcode));
175919261079SEd Maste 				}
176094e21addSEd Maste 			}
176119261079SEd Maste 			opt_array_append(filename, linenum,
176219261079SEd Maste 			    lookup_opcode_name(opcode),
1763a91a2465SEd Maste 			    &strs, &nstrs, arg2);
176419261079SEd Maste 			free(arg2);
176519261079SEd Maste 		}
1766a91a2465SEd Maste 		if (nstrs == 0)
176794e21addSEd Maste 			fatal("%s line %d: missing %s specification",
176894e21addSEd Maste 			    filename, linenum, lookup_opcode_name(opcode));
1769a91a2465SEd Maste 		if (found && *activep) {
1770a91a2465SEd Maste 			*cppptr = strs;
1771a91a2465SEd Maste 			*uintptr = nstrs;
1772a91a2465SEd Maste 			strs = NULL; /* transferred */
1773a91a2465SEd Maste 			nstrs = 0;
1774a91a2465SEd Maste 		}
177519261079SEd Maste 		break;
177619261079SEd Maste 
1777af12a3e7SDag-Erling Smørgrav 	case oClearAllForwardings:
1778af12a3e7SDag-Erling Smørgrav 		intptr = &options->clear_forwardings;
1779af12a3e7SDag-Erling Smørgrav 		goto parse_flag;
1780af12a3e7SDag-Erling Smørgrav 
1781511b41d2SMark Murray 	case oHost:
178219261079SEd Maste 		if (cmdline) {
178319261079SEd Maste 			error("Host directive not supported as a command-line "
1784f7167e0eSDag-Erling Smørgrav 			    "option");
178519261079SEd Maste 			goto out;
178619261079SEd Maste 		}
1787511b41d2SMark Murray 		*activep = 0;
1788e146993eSDag-Erling Smørgrav 		arg2 = NULL;
178919261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
179019261079SEd Maste 			if (*arg == '\0') {
179119261079SEd Maste 				error("%s line %d: keyword %s empty argument",
179219261079SEd Maste 				    filename, linenum, keyword);
179319261079SEd Maste 				goto out;
179419261079SEd Maste 			}
179519261079SEd Maste 			if ((flags & SSHCONF_NEVERMATCH) != 0) {
179619261079SEd Maste 				argv_consume(&ac);
1797076ad2f8SDag-Erling Smørgrav 				break;
179819261079SEd Maste 			}
1799e146993eSDag-Erling Smørgrav 			negated = *arg == '!';
1800e146993eSDag-Erling Smørgrav 			if (negated)
1801e146993eSDag-Erling Smørgrav 				arg++;
1802c2d3a559SKris Kennaway 			if (match_pattern(host, arg)) {
1803e146993eSDag-Erling Smørgrav 				if (negated) {
1804e146993eSDag-Erling Smørgrav 					debug("%.200s line %d: Skipping Host "
1805e146993eSDag-Erling Smørgrav 					    "block because of negated match "
1806e146993eSDag-Erling Smørgrav 					    "for %.100s", filename, linenum,
1807e146993eSDag-Erling Smørgrav 					    arg);
1808e146993eSDag-Erling Smørgrav 					*activep = 0;
180919261079SEd Maste 					argv_consume(&ac);
1810511b41d2SMark Murray 					break;
1811511b41d2SMark Murray 				}
1812e146993eSDag-Erling Smørgrav 				if (!*activep)
1813e146993eSDag-Erling Smørgrav 					arg2 = arg; /* logged below */
1814e146993eSDag-Erling Smørgrav 				*activep = 1;
1815e146993eSDag-Erling Smørgrav 			}
1816e146993eSDag-Erling Smørgrav 		}
1817e146993eSDag-Erling Smørgrav 		if (*activep)
1818e146993eSDag-Erling Smørgrav 			debug("%.200s line %d: Applying options for %.100s",
1819e146993eSDag-Erling Smørgrav 			    filename, linenum, arg2);
182019261079SEd Maste 		break;
1821511b41d2SMark Murray 
1822f7167e0eSDag-Erling Smørgrav 	case oMatch:
182319261079SEd Maste 		if (cmdline) {
182419261079SEd Maste 			error("Host directive not supported as a command-line "
1825f7167e0eSDag-Erling Smørgrav 			    "option");
182619261079SEd Maste 			goto out;
182719261079SEd Maste 		}
18283d9fd9fcSEd Maste 		value = match_cfg_line(options, str, &ac, &av, pw, host,
18293d9fd9fcSEd Maste 		    original_host, flags & SSHCONF_FINAL, want_final_pass,
183019261079SEd Maste 		    filename, linenum);
183119261079SEd Maste 		if (value < 0) {
183219261079SEd Maste 			error("%.200s line %d: Bad Match condition", filename,
1833f7167e0eSDag-Erling Smørgrav 			    linenum);
183419261079SEd Maste 			goto out;
183519261079SEd Maste 		}
1836076ad2f8SDag-Erling Smørgrav 		*activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
1837f7167e0eSDag-Erling Smørgrav 		break;
1838f7167e0eSDag-Erling Smørgrav 
1839511b41d2SMark Murray 	case oEscapeChar:
1840511b41d2SMark Murray 		intptr = &options->escape_char;
184119261079SEd Maste 		arg = argv_next(&ac, &av);
184219261079SEd Maste 		if (!arg || *arg == '\0') {
184319261079SEd Maste 			error("%.200s line %d: Missing argument.",
184419261079SEd Maste 			    filename, linenum);
184519261079SEd Maste 			goto out;
184619261079SEd Maste 		}
1847557f75e5SDag-Erling Smørgrav 		if (strcmp(arg, "none") == 0)
1848557f75e5SDag-Erling Smørgrav 			value = SSH_ESCAPECHAR_NONE;
1849557f75e5SDag-Erling Smørgrav 		else if (arg[1] == '\0')
1850557f75e5SDag-Erling Smørgrav 			value = (u_char) arg[0];
1851557f75e5SDag-Erling Smørgrav 		else if (arg[0] == '^' && arg[2] == 0 &&
1852ca3176e7SBrian Feldman 		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
1853ca3176e7SBrian Feldman 			value = (u_char) arg[1] & 31;
1854511b41d2SMark Murray 		else {
185519261079SEd Maste 			error("%.200s line %d: Bad escape character.",
1856511b41d2SMark Murray 			    filename, linenum);
185719261079SEd Maste 			goto out;
1858511b41d2SMark Murray 		}
1859511b41d2SMark Murray 		if (*activep && *intptr == -1)
1860511b41d2SMark Murray 			*intptr = value;
1861511b41d2SMark Murray 		break;
1862511b41d2SMark Murray 
1863cf2b5f3bSDag-Erling Smørgrav 	case oAddressFamily:
1864cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->address_family;
1865f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_addressfamily;
1866f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1867cf2b5f3bSDag-Erling Smørgrav 
1868e73e9afaSDag-Erling Smørgrav 	case oEnableSSHKeysign:
1869e73e9afaSDag-Erling Smørgrav 		intptr = &options->enable_ssh_keysign;
1870e73e9afaSDag-Erling Smørgrav 		goto parse_flag;
1871e73e9afaSDag-Erling Smørgrav 
18725962c0e9SDag-Erling Smørgrav 	case oIdentitiesOnly:
18735962c0e9SDag-Erling Smørgrav 		intptr = &options->identities_only;
18745962c0e9SDag-Erling Smørgrav 		goto parse_flag;
18755962c0e9SDag-Erling Smørgrav 
18761ec0d754SDag-Erling Smørgrav 	case oServerAliveInterval:
18771ec0d754SDag-Erling Smørgrav 		intptr = &options->server_alive_interval;
18781ec0d754SDag-Erling Smørgrav 		goto parse_time;
18791ec0d754SDag-Erling Smørgrav 
18801ec0d754SDag-Erling Smørgrav 	case oServerAliveCountMax:
18811ec0d754SDag-Erling Smørgrav 		intptr = &options->server_alive_count_max;
18821ec0d754SDag-Erling Smørgrav 		goto parse_int;
18831ec0d754SDag-Erling Smørgrav 
188421e764dfSDag-Erling Smørgrav 	case oSendEnv:
1885a91a2465SEd Maste 		/* XXX appends to list; doesn't respect first-match-wins */
188619261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
188719261079SEd Maste 			if (*arg == '\0' || strchr(arg, '=') != NULL) {
188819261079SEd Maste 				error("%s line %d: Invalid environment name.",
188921e764dfSDag-Erling Smørgrav 				    filename, linenum);
189019261079SEd Maste 				goto out;
189119261079SEd Maste 			}
1892a91a2465SEd Maste 			found = 1;
1893aa49c926SDag-Erling Smørgrav 			if (!*activep)
1894aa49c926SDag-Erling Smørgrav 				continue;
1895190cef3dSDag-Erling Smørgrav 			if (*arg == '-') {
1896190cef3dSDag-Erling Smørgrav 				/* Removing an env var */
1897190cef3dSDag-Erling Smørgrav 				rm_env(options, arg, filename, linenum);
1898190cef3dSDag-Erling Smørgrav 				continue;
189919261079SEd Maste 			}
190038a52bd3SEd Maste 			opt_array_append(filename, linenum,
190138a52bd3SEd Maste 			    lookup_opcode_name(opcode),
190238a52bd3SEd Maste 			    &options->send_env, &options->num_send_env, arg);
1903190cef3dSDag-Erling Smørgrav 		}
1904a91a2465SEd Maste 		if (!found) {
1905a91a2465SEd Maste 			fatal("%s line %d: no %s specified",
1906a91a2465SEd Maste 			    filename, linenum, keyword);
1907a91a2465SEd Maste 		}
1908190cef3dSDag-Erling Smørgrav 		break;
1909190cef3dSDag-Erling Smørgrav 
1910190cef3dSDag-Erling Smørgrav 	case oSetEnv:
1911a91a2465SEd Maste 		found = options->num_setenv == 0;
191219261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
191319261079SEd Maste 			if (strchr(arg, '=') == NULL) {
191419261079SEd Maste 				error("%s line %d: Invalid SetEnv.",
1915190cef3dSDag-Erling Smørgrav 				    filename, linenum);
191619261079SEd Maste 				goto out;
191719261079SEd Maste 			}
1918a91a2465SEd Maste 			if (lookup_setenv_in_list(arg, strs, nstrs) != NULL) {
191938a52bd3SEd Maste 				debug2("%s line %d: ignoring duplicate env "
192038a52bd3SEd Maste 				    "name \"%.64s\"", filename, linenum, arg);
192138a52bd3SEd Maste 				continue;
192219261079SEd Maste 			}
192338a52bd3SEd Maste 			opt_array_append(filename, linenum,
192438a52bd3SEd Maste 			    lookup_opcode_name(opcode),
1925a91a2465SEd Maste 			    &strs, &nstrs, arg);
1926a91a2465SEd Maste 		}
1927a91a2465SEd Maste 		if (nstrs == 0) {
1928a91a2465SEd Maste 			fatal("%s line %d: no %s specified",
1929a91a2465SEd Maste 			    filename, linenum, keyword);
1930a91a2465SEd Maste 		}
1931a91a2465SEd Maste 		if (found && *activep) {
1932a91a2465SEd Maste 			options->setenv = strs;
1933a91a2465SEd Maste 			options->num_setenv = nstrs;
1934a91a2465SEd Maste 			strs = NULL; /* transferred */
1935a91a2465SEd Maste 			nstrs = 0;
1936190cef3dSDag-Erling Smørgrav 		}
193721e764dfSDag-Erling Smørgrav 		break;
193821e764dfSDag-Erling Smørgrav 
193921e764dfSDag-Erling Smørgrav 	case oControlPath:
194021e764dfSDag-Erling Smørgrav 		charptr = &options->control_path;
194121e764dfSDag-Erling Smørgrav 		goto parse_string;
194221e764dfSDag-Erling Smørgrav 
194321e764dfSDag-Erling Smørgrav 	case oControlMaster:
194421e764dfSDag-Erling Smørgrav 		intptr = &options->control_master;
1945f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_controlmaster;
1946f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
194721e764dfSDag-Erling Smørgrav 
1948e2f6069cSDag-Erling Smørgrav 	case oControlPersist:
1949e2f6069cSDag-Erling Smørgrav 		/* no/false/yes/true, or a time spec */
1950e2f6069cSDag-Erling Smørgrav 		intptr = &options->control_persist;
195119261079SEd Maste 		arg = argv_next(&ac, &av);
195219261079SEd Maste 		if (!arg || *arg == '\0') {
195319261079SEd Maste 			error("%.200s line %d: Missing ControlPersist"
1954e2f6069cSDag-Erling Smørgrav 			    " argument.", filename, linenum);
195519261079SEd Maste 			goto out;
195619261079SEd Maste 		}
1957e2f6069cSDag-Erling Smørgrav 		value = 0;
1958e2f6069cSDag-Erling Smørgrav 		value2 = 0;	/* timeout */
1959e2f6069cSDag-Erling Smørgrav 		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
1960e2f6069cSDag-Erling Smørgrav 			value = 0;
1961e2f6069cSDag-Erling Smørgrav 		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
1962e2f6069cSDag-Erling Smørgrav 			value = 1;
1963e2f6069cSDag-Erling Smørgrav 		else if ((value2 = convtime(arg)) >= 0)
1964e2f6069cSDag-Erling Smørgrav 			value = 1;
196519261079SEd Maste 		else {
196619261079SEd Maste 			error("%.200s line %d: Bad ControlPersist argument.",
1967e2f6069cSDag-Erling Smørgrav 			    filename, linenum);
196819261079SEd Maste 			goto out;
196919261079SEd Maste 		}
1970e2f6069cSDag-Erling Smørgrav 		if (*activep && *intptr == -1) {
1971e2f6069cSDag-Erling Smørgrav 			*intptr = value;
1972e2f6069cSDag-Erling Smørgrav 			options->control_persist_timeout = value2;
1973e2f6069cSDag-Erling Smørgrav 		}
1974e2f6069cSDag-Erling Smørgrav 		break;
1975e2f6069cSDag-Erling Smørgrav 
1976aa49c926SDag-Erling Smørgrav 	case oHashKnownHosts:
1977aa49c926SDag-Erling Smørgrav 		intptr = &options->hash_known_hosts;
1978aa49c926SDag-Erling Smørgrav 		goto parse_flag;
1979aa49c926SDag-Erling Smørgrav 
1980b74df5b2SDag-Erling Smørgrav 	case oTunnel:
1981b74df5b2SDag-Erling Smørgrav 		intptr = &options->tun_open;
1982f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_tunnel;
1983f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1984b74df5b2SDag-Erling Smørgrav 
1985b74df5b2SDag-Erling Smørgrav 	case oTunnelDevice:
198619261079SEd Maste 		arg = argv_next(&ac, &av);
198719261079SEd Maste 		if (!arg || *arg == '\0') {
198819261079SEd Maste 			error("%.200s line %d: Missing argument.",
198919261079SEd Maste 			    filename, linenum);
199019261079SEd Maste 			goto out;
199119261079SEd Maste 		}
1992b74df5b2SDag-Erling Smørgrav 		value = a2tun(arg, &value2);
199319261079SEd Maste 		if (value == SSH_TUNID_ERR) {
199419261079SEd Maste 			error("%.200s line %d: Bad tun device.",
199519261079SEd Maste 			    filename, linenum);
199619261079SEd Maste 			goto out;
199719261079SEd Maste 		}
199819261079SEd Maste 		if (*activep && options->tun_local == -1) {
1999b74df5b2SDag-Erling Smørgrav 			options->tun_local = value;
2000b74df5b2SDag-Erling Smørgrav 			options->tun_remote = value2;
2001b74df5b2SDag-Erling Smørgrav 		}
2002b74df5b2SDag-Erling Smørgrav 		break;
2003b74df5b2SDag-Erling Smørgrav 
2004b74df5b2SDag-Erling Smørgrav 	case oLocalCommand:
2005b74df5b2SDag-Erling Smørgrav 		charptr = &options->local_command;
2006b74df5b2SDag-Erling Smørgrav 		goto parse_command;
2007b74df5b2SDag-Erling Smørgrav 
2008b74df5b2SDag-Erling Smørgrav 	case oPermitLocalCommand:
2009b74df5b2SDag-Erling Smørgrav 		intptr = &options->permit_local_command;
2010b74df5b2SDag-Erling Smørgrav 		goto parse_flag;
2011b74df5b2SDag-Erling Smørgrav 
20124f52dfbbSDag-Erling Smørgrav 	case oRemoteCommand:
20134f52dfbbSDag-Erling Smørgrav 		charptr = &options->remote_command;
20144f52dfbbSDag-Erling Smørgrav 		goto parse_command;
20154f52dfbbSDag-Erling Smørgrav 
2016d4af9e69SDag-Erling Smørgrav 	case oVisualHostKey:
2017d4af9e69SDag-Erling Smørgrav 		intptr = &options->visual_host_key;
2018d4af9e69SDag-Erling Smørgrav 		goto parse_flag;
2019d4af9e69SDag-Erling Smørgrav 
2020076ad2f8SDag-Erling Smørgrav 	case oInclude:
202119261079SEd Maste 		if (cmdline) {
202219261079SEd Maste 			error("Include directive not supported as a "
2023076ad2f8SDag-Erling Smørgrav 			    "command-line option");
202419261079SEd Maste 			goto out;
202519261079SEd Maste 		}
2026076ad2f8SDag-Erling Smørgrav 		value = 0;
202719261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
202819261079SEd Maste 			if (*arg == '\0') {
202919261079SEd Maste 				error("%s line %d: keyword %s empty argument",
203019261079SEd Maste 				    filename, linenum, keyword);
203119261079SEd Maste 				goto out;
203219261079SEd Maste 			}
20333d9fd9fcSEd Maste 			/* Expand %tokens and environment variables */
20343d9fd9fcSEd Maste 			if ((p = expand_match_exec_or_include_path(arg,
20353d9fd9fcSEd Maste 			    options, pw, host, original_host,
20363d9fd9fcSEd Maste 			    flags & SSHCONF_FINAL, 1)) == NULL) {
20373d9fd9fcSEd Maste 				error("%.200s line %d: Unable to expand user "
20383d9fd9fcSEd Maste 				    "config file '%.100s'",
20393d9fd9fcSEd Maste 				    filename, linenum, arg);
20403d9fd9fcSEd Maste 				continue;
20413d9fd9fcSEd Maste 			}
2042076ad2f8SDag-Erling Smørgrav 			/*
2043076ad2f8SDag-Erling Smørgrav 			 * Ensure all paths are anchored. User configuration
2044076ad2f8SDag-Erling Smørgrav 			 * files may begin with '~/' but system configurations
2045076ad2f8SDag-Erling Smørgrav 			 * must not. If the path is relative, then treat it
2046076ad2f8SDag-Erling Smørgrav 			 * as living in ~/.ssh for user configurations or
2047076ad2f8SDag-Erling Smørgrav 			 * /etc/ssh for system ones.
2048076ad2f8SDag-Erling Smørgrav 			 */
20493d9fd9fcSEd Maste 			if (*p == '~' && (flags & SSHCONF_USERCONF) == 0) {
205019261079SEd Maste 				error("%.200s line %d: bad include path %s.",
20513d9fd9fcSEd Maste 				    filename, linenum, p);
205219261079SEd Maste 				goto out;
205319261079SEd Maste 			}
20543d9fd9fcSEd Maste 			if (!path_absolute(p) && *p != '~') {
2055076ad2f8SDag-Erling Smørgrav 				xasprintf(&arg2, "%s/%s",
2056076ad2f8SDag-Erling Smørgrav 				    (flags & SSHCONF_USERCONF) ?
20573d9fd9fcSEd Maste 				    "~/" _PATH_SSH_USER_DIR : SSHDIR, p);
20583d9fd9fcSEd Maste 			} else {
20593d9fd9fcSEd Maste 				arg2 = xstrdup(p);
20603d9fd9fcSEd Maste 			}
20613d9fd9fcSEd Maste 			free(p);
2062076ad2f8SDag-Erling Smørgrav 			memset(&gl, 0, sizeof(gl));
2063076ad2f8SDag-Erling Smørgrav 			r = glob(arg2, GLOB_TILDE, NULL, &gl);
2064076ad2f8SDag-Erling Smørgrav 			if (r == GLOB_NOMATCH) {
2065076ad2f8SDag-Erling Smørgrav 				debug("%.200s line %d: include %s matched no "
2066076ad2f8SDag-Erling Smørgrav 				    "files",filename, linenum, arg2);
2067d93a896eSDag-Erling Smørgrav 				free(arg2);
2068076ad2f8SDag-Erling Smørgrav 				continue;
206919261079SEd Maste 			} else if (r != 0) {
207019261079SEd Maste 				error("%.200s line %d: glob failed for %s.",
2071076ad2f8SDag-Erling Smørgrav 				    filename, linenum, arg2);
207219261079SEd Maste 				goto out;
207319261079SEd Maste 			}
2074076ad2f8SDag-Erling Smørgrav 			free(arg2);
2075076ad2f8SDag-Erling Smørgrav 			oactive = *activep;
207619261079SEd Maste 			for (i = 0; i < gl.gl_pathc; i++) {
2077076ad2f8SDag-Erling Smørgrav 				debug3("%.200s line %d: Including file %s "
2078076ad2f8SDag-Erling Smørgrav 				    "depth %d%s", filename, linenum,
2079076ad2f8SDag-Erling Smørgrav 				    gl.gl_pathv[i], depth,
2080076ad2f8SDag-Erling Smørgrav 				    oactive ? "" : " (parse only)");
2081076ad2f8SDag-Erling Smørgrav 				r = read_config_file_depth(gl.gl_pathv[i],
2082076ad2f8SDag-Erling Smørgrav 				    pw, host, original_host, options,
2083076ad2f8SDag-Erling Smørgrav 				    flags | SSHCONF_CHECKPERM |
2084076ad2f8SDag-Erling Smørgrav 				    (oactive ? 0 : SSHCONF_NEVERMATCH),
208519261079SEd Maste 				    activep, want_final_pass, depth + 1);
2086d93a896eSDag-Erling Smørgrav 				if (r != 1 && errno != ENOENT) {
20873d9fd9fcSEd Maste 					error("%.200s line %d: Can't open user "
20883d9fd9fcSEd Maste 					    "config file %.100s: %.100s",
20893d9fd9fcSEd Maste 					    filename, linenum, gl.gl_pathv[i],
2090d93a896eSDag-Erling Smørgrav 					    strerror(errno));
209119261079SEd Maste 					globfree(&gl);
209219261079SEd Maste 					goto out;
2093d93a896eSDag-Erling Smørgrav 				}
2094076ad2f8SDag-Erling Smørgrav 				/*
2095076ad2f8SDag-Erling Smørgrav 				 * don't let Match in includes clobber the
2096076ad2f8SDag-Erling Smørgrav 				 * containing file's Match state.
2097076ad2f8SDag-Erling Smørgrav 				 */
2098076ad2f8SDag-Erling Smørgrav 				*activep = oactive;
2099076ad2f8SDag-Erling Smørgrav 				if (r != 1)
2100076ad2f8SDag-Erling Smørgrav 					value = -1;
2101076ad2f8SDag-Erling Smørgrav 			}
2102076ad2f8SDag-Erling Smørgrav 			globfree(&gl);
2103076ad2f8SDag-Erling Smørgrav 		}
2104076ad2f8SDag-Erling Smørgrav 		if (value != 0)
210519261079SEd Maste 			ret = value;
2106076ad2f8SDag-Erling Smørgrav 		break;
2107076ad2f8SDag-Erling Smørgrav 
21084a421b63SDag-Erling Smørgrav 	case oIPQoS:
210919261079SEd Maste 		arg = argv_next(&ac, &av);
211019261079SEd Maste 		if ((value = parse_ipqos(arg)) == -1) {
211119261079SEd Maste 			error("%s line %d: Bad IPQoS value: %s",
21124a421b63SDag-Erling Smørgrav 			    filename, linenum, arg);
211319261079SEd Maste 			goto out;
211419261079SEd Maste 		}
211519261079SEd Maste 		arg = argv_next(&ac, &av);
21164a421b63SDag-Erling Smørgrav 		if (arg == NULL)
21174a421b63SDag-Erling Smørgrav 			value2 = value;
211819261079SEd Maste 		else if ((value2 = parse_ipqos(arg)) == -1) {
211919261079SEd Maste 			error("%s line %d: Bad IPQoS value: %s",
21204a421b63SDag-Erling Smørgrav 			    filename, linenum, arg);
212119261079SEd Maste 			goto out;
212219261079SEd Maste 		}
212319261079SEd Maste 		if (*activep && options->ip_qos_interactive == -1) {
21244a421b63SDag-Erling Smørgrav 			options->ip_qos_interactive = value;
21254a421b63SDag-Erling Smørgrav 			options->ip_qos_bulk = value2;
21264a421b63SDag-Erling Smørgrav 		}
21274a421b63SDag-Erling Smørgrav 		break;
21284a421b63SDag-Erling Smørgrav 
2129e146993eSDag-Erling Smørgrav 	case oRequestTTY:
2130e146993eSDag-Erling Smørgrav 		intptr = &options->request_tty;
2131f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_requesttty;
2132f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
2133975616f0SDag-Erling Smørgrav 
213419261079SEd Maste 	case oSessionType:
213519261079SEd Maste 		intptr = &options->session_type;
213619261079SEd Maste 		multistate_ptr = multistate_sessiontype;
213719261079SEd Maste 		goto parse_multistate;
213819261079SEd Maste 
213919261079SEd Maste 	case oStdinNull:
214019261079SEd Maste 		intptr = &options->stdin_null;
214119261079SEd Maste 		goto parse_flag;
214219261079SEd Maste 
214319261079SEd Maste 	case oForkAfterAuthentication:
214419261079SEd Maste 		intptr = &options->fork_after_authentication;
214519261079SEd Maste 		goto parse_flag;
214619261079SEd Maste 
2147e4a9863fSDag-Erling Smørgrav 	case oIgnoreUnknown:
2148e4a9863fSDag-Erling Smørgrav 		charptr = &options->ignored_unknown;
2149e4a9863fSDag-Erling Smørgrav 		goto parse_string;
2150e4a9863fSDag-Erling Smørgrav 
2151f7167e0eSDag-Erling Smørgrav 	case oProxyUseFdpass:
2152f7167e0eSDag-Erling Smørgrav 		intptr = &options->proxy_use_fdpass;
2153f7167e0eSDag-Erling Smørgrav 		goto parse_flag;
2154f7167e0eSDag-Erling Smørgrav 
2155f7167e0eSDag-Erling Smørgrav 	case oCanonicalDomains:
2156a91a2465SEd Maste 		found = options->num_canonical_domains == 0;
215719261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
215819261079SEd Maste 			/* Allow "none" only in first position */
215919261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
2160a91a2465SEd Maste 				if (nstrs > 0 || ac > 0) {
216119261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
216219261079SEd Maste 					    "argument must appear alone.",
216319261079SEd Maste 					    filename, linenum, keyword);
216419261079SEd Maste 					goto out;
216519261079SEd Maste 				}
216619261079SEd Maste 			}
216747dd1d1bSDag-Erling Smørgrav 			if (!valid_domain(arg, 1, &errstr)) {
216819261079SEd Maste 				error("%s line %d: %s", filename, linenum,
216947dd1d1bSDag-Erling Smørgrav 				    errstr);
217019261079SEd Maste 				goto out;
217147dd1d1bSDag-Erling Smørgrav 			}
2172a91a2465SEd Maste 			opt_array_append(filename, linenum, keyword,
2173a91a2465SEd Maste 			    &strs, &nstrs, arg);
217419261079SEd Maste 		}
2175a91a2465SEd Maste 		if (nstrs == 0) {
2176a91a2465SEd Maste 			fatal("%s line %d: no %s specified",
2177a91a2465SEd Maste 			    filename, linenum, keyword);
2178a91a2465SEd Maste 		}
2179a91a2465SEd Maste 		if (found && *activep) {
2180a91a2465SEd Maste 			options->canonical_domains = strs;
2181a91a2465SEd Maste 			options->num_canonical_domains = nstrs;
2182a91a2465SEd Maste 			strs = NULL; /* transferred */
2183a91a2465SEd Maste 			nstrs = 0;
2184f7167e0eSDag-Erling Smørgrav 		}
2185f7167e0eSDag-Erling Smørgrav 		break;
2186f7167e0eSDag-Erling Smørgrav 
2187f7167e0eSDag-Erling Smørgrav 	case oCanonicalizePermittedCNAMEs:
2188a91a2465SEd Maste 		found = options->num_permitted_cnames == 0;
218919261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
2190e9e8876aSEd Maste 			/*
2191e9e8876aSEd Maste 			 * Either 'none' (only in first position), '*' for
2192e9e8876aSEd Maste 			 * everything or 'list:list'
2193e9e8876aSEd Maste 			 */
2194e9e8876aSEd Maste 			if (strcasecmp(arg, "none") == 0) {
2195a91a2465SEd Maste 				if (ncnames > 0 || ac > 0) {
2196e9e8876aSEd Maste 					error("%s line %d: keyword %s \"none\" "
2197e9e8876aSEd Maste 					    "argument must appear alone.",
2198e9e8876aSEd Maste 					    filename, linenum, keyword);
2199e9e8876aSEd Maste 					goto out;
2200e9e8876aSEd Maste 				}
2201e9e8876aSEd Maste 				arg2 = "";
2202e9e8876aSEd Maste 			} else if (strcmp(arg, "*") == 0) {
2203f7167e0eSDag-Erling Smørgrav 				arg2 = arg;
2204e9e8876aSEd Maste 			} else {
2205f7167e0eSDag-Erling Smørgrav 				lowercase(arg);
2206f7167e0eSDag-Erling Smørgrav 				if ((arg2 = strchr(arg, ':')) == NULL ||
2207f7167e0eSDag-Erling Smørgrav 				    arg2[1] == '\0') {
220819261079SEd Maste 					error("%s line %d: "
2209f7167e0eSDag-Erling Smørgrav 					    "Invalid permitted CNAME \"%s\"",
2210f7167e0eSDag-Erling Smørgrav 					    filename, linenum, arg);
221119261079SEd Maste 					goto out;
2212f7167e0eSDag-Erling Smørgrav 				}
2213f7167e0eSDag-Erling Smørgrav 				*arg2 = '\0';
2214f7167e0eSDag-Erling Smørgrav 				arg2++;
2215f7167e0eSDag-Erling Smørgrav 			}
2216a91a2465SEd Maste 			cnames = xrecallocarray(cnames, ncnames, ncnames + 1,
2217a91a2465SEd Maste 			    sizeof(*cnames));
2218a91a2465SEd Maste 			cnames[ncnames].source_list = xstrdup(arg);
2219a91a2465SEd Maste 			cnames[ncnames].target_list = xstrdup(arg2);
2220a91a2465SEd Maste 			ncnames++;
222119261079SEd Maste 		}
2222a91a2465SEd Maste 		if (ncnames == 0) {
2223a91a2465SEd Maste 			fatal("%s line %d: no %s specified",
2224a91a2465SEd Maste 			    filename, linenum, keyword);
2225f7167e0eSDag-Erling Smørgrav 		}
2226a91a2465SEd Maste 		if (found && *activep) {
2227a91a2465SEd Maste 			options->permitted_cnames = cnames;
2228a91a2465SEd Maste 			options->num_permitted_cnames = ncnames;
2229a91a2465SEd Maste 			cnames = NULL; /* transferred */
2230a91a2465SEd Maste 			ncnames = 0;
2231a91a2465SEd Maste 		}
2232a91a2465SEd Maste 		/* un-transferred cnames is cleaned up before exit */
2233f7167e0eSDag-Erling Smørgrav 		break;
2234f7167e0eSDag-Erling Smørgrav 
2235f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeHostname:
2236f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_hostname;
2237f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_canonicalizehostname;
2238f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
2239f7167e0eSDag-Erling Smørgrav 
2240f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeMaxDots:
2241f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_max_dots;
2242f7167e0eSDag-Erling Smørgrav 		goto parse_int;
2243f7167e0eSDag-Erling Smørgrav 
2244f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeFallbackLocal:
2245f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_fallback_local;
2246f7167e0eSDag-Erling Smørgrav 		goto parse_flag;
2247f7167e0eSDag-Erling Smørgrav 
2248a0ee8cc6SDag-Erling Smørgrav 	case oStreamLocalBindMask:
224919261079SEd Maste 		arg = argv_next(&ac, &av);
225019261079SEd Maste 		if (!arg || *arg == '\0') {
225119261079SEd Maste 			error("%.200s line %d: Missing StreamLocalBindMask "
225219261079SEd Maste 			    "argument.", filename, linenum);
225319261079SEd Maste 			goto out;
225419261079SEd Maste 		}
2255a0ee8cc6SDag-Erling Smørgrav 		/* Parse mode in octal format */
2256a0ee8cc6SDag-Erling Smørgrav 		value = strtol(arg, &endofnumber, 8);
225719261079SEd Maste 		if (arg == endofnumber || value < 0 || value > 0777) {
225819261079SEd Maste 			error("%.200s line %d: Bad mask.", filename, linenum);
225919261079SEd Maste 			goto out;
226019261079SEd Maste 		}
2261a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
2262a0ee8cc6SDag-Erling Smørgrav 		break;
2263a0ee8cc6SDag-Erling Smørgrav 
2264a0ee8cc6SDag-Erling Smørgrav 	case oStreamLocalBindUnlink:
2265a0ee8cc6SDag-Erling Smørgrav 		intptr = &options->fwd_opts.streamlocal_bind_unlink;
2266a0ee8cc6SDag-Erling Smørgrav 		goto parse_flag;
2267a0ee8cc6SDag-Erling Smørgrav 
2268bc5531deSDag-Erling Smørgrav 	case oRevokedHostKeys:
2269bc5531deSDag-Erling Smørgrav 		charptr = &options->revoked_host_keys;
2270bc5531deSDag-Erling Smørgrav 		goto parse_string;
2271bc5531deSDag-Erling Smørgrav 
2272bc5531deSDag-Erling Smørgrav 	case oFingerprintHash:
2273bc5531deSDag-Erling Smørgrav 		intptr = &options->fingerprint_hash;
227419261079SEd Maste 		arg = argv_next(&ac, &av);
227519261079SEd Maste 		if (!arg || *arg == '\0') {
227619261079SEd Maste 			error("%.200s line %d: Missing argument.",
2277bc5531deSDag-Erling Smørgrav 			    filename, linenum);
227819261079SEd Maste 			goto out;
227919261079SEd Maste 		}
228019261079SEd Maste 		if ((value = ssh_digest_alg_by_name(arg)) == -1) {
228119261079SEd Maste 			error("%.200s line %d: Invalid hash algorithm \"%s\".",
2282bc5531deSDag-Erling Smørgrav 			    filename, linenum, arg);
228319261079SEd Maste 			goto out;
228419261079SEd Maste 		}
2285bc5531deSDag-Erling Smørgrav 		if (*activep && *intptr == -1)
2286bc5531deSDag-Erling Smørgrav 			*intptr = value;
2287bc5531deSDag-Erling Smørgrav 		break;
2288bc5531deSDag-Erling Smørgrav 
2289bc5531deSDag-Erling Smørgrav 	case oUpdateHostkeys:
2290bc5531deSDag-Erling Smørgrav 		intptr = &options->update_hostkeys;
2291bc5531deSDag-Erling Smørgrav 		multistate_ptr = multistate_yesnoask;
2292bc5531deSDag-Erling Smørgrav 		goto parse_multistate;
2293bc5531deSDag-Erling Smørgrav 
229419261079SEd Maste 	case oHostbasedAcceptedAlgorithms:
229519261079SEd Maste 		charptr = &options->hostbased_accepted_algos;
2296535af610SEd Maste 		ca_only = 0;
229719261079SEd Maste 		goto parse_pubkey_algos;
2298eccfee6eSDag-Erling Smørgrav 
229919261079SEd Maste 	case oPubkeyAcceptedAlgorithms:
230019261079SEd Maste 		charptr = &options->pubkey_accepted_algos;
2301535af610SEd Maste 		ca_only = 0;
230219261079SEd Maste 		goto parse_pubkey_algos;
2303bc5531deSDag-Erling Smørgrav 
2304acc1a9efSDag-Erling Smørgrav 	case oAddKeysToAgent:
230519261079SEd Maste 		arg = argv_next(&ac, &av);
230619261079SEd Maste 		arg2 = argv_next(&ac, &av);
230719261079SEd Maste 		value = parse_multistate_value(arg, filename, linenum,
230819261079SEd Maste 		    multistate_yesnoaskconfirm);
230919261079SEd Maste 		value2 = 0; /* unlimited lifespan by default */
231019261079SEd Maste 		if (value == 3 && arg2 != NULL) {
231119261079SEd Maste 			/* allow "AddKeysToAgent confirm 5m" */
23124d3fc8b0SEd Maste 			if ((value2 = convtime(arg2)) == -1) {
231319261079SEd Maste 				error("%s line %d: invalid time value.",
231419261079SEd Maste 				    filename, linenum);
231519261079SEd Maste 				goto out;
231619261079SEd Maste 			}
231719261079SEd Maste 		} else if (value == -1 && arg2 == NULL) {
23184d3fc8b0SEd Maste 			if ((value2 = convtime(arg)) == -1) {
231919261079SEd Maste 				error("%s line %d: unsupported option",
232019261079SEd Maste 				    filename, linenum);
232119261079SEd Maste 				goto out;
232219261079SEd Maste 			}
232319261079SEd Maste 			value = 1; /* yes */
232419261079SEd Maste 		} else if (value == -1 || arg2 != NULL) {
232519261079SEd Maste 			error("%s line %d: unsupported option",
232619261079SEd Maste 			    filename, linenum);
232719261079SEd Maste 			goto out;
232819261079SEd Maste 		}
232919261079SEd Maste 		if (*activep && options->add_keys_to_agent == -1) {
233019261079SEd Maste 			options->add_keys_to_agent = value;
233119261079SEd Maste 			options->add_keys_to_agent_lifespan = value2;
233219261079SEd Maste 		}
233319261079SEd Maste 		break;
2334acc1a9efSDag-Erling Smørgrav 
2335076ad2f8SDag-Erling Smørgrav 	case oIdentityAgent:
2336076ad2f8SDag-Erling Smørgrav 		charptr = &options->identity_agent;
233719261079SEd Maste 		arg = argv_next(&ac, &av);
233819261079SEd Maste 		if (!arg || *arg == '\0') {
233919261079SEd Maste 			error("%.200s line %d: Missing argument.",
23402f513db7SEd Maste 			    filename, linenum);
234119261079SEd Maste 			goto out;
234219261079SEd Maste 		}
234319261079SEd Maste   parse_agent_path:
23442f513db7SEd Maste 		/* Extra validation if the string represents an env var. */
234519261079SEd Maste 		if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
234619261079SEd Maste 			error("%.200s line %d: Invalid environment expansion "
234719261079SEd Maste 			    "%s.", filename, linenum, arg);
234819261079SEd Maste 			goto out;
234919261079SEd Maste 		}
235019261079SEd Maste 		free(arg2);
235119261079SEd Maste 		/* check for legacy environment format */
235219261079SEd Maste 		if (arg[0] == '$' && arg[1] != '{' &&
235319261079SEd Maste 		    !valid_env_name(arg + 1)) {
235419261079SEd Maste 			error("%.200s line %d: Invalid environment name %s.",
23552f513db7SEd Maste 			    filename, linenum, arg);
235619261079SEd Maste 			goto out;
23572f513db7SEd Maste 		}
23582f513db7SEd Maste 		if (*activep && *charptr == NULL)
23592f513db7SEd Maste 			*charptr = xstrdup(arg);
23602f513db7SEd Maste 		break;
2361076ad2f8SDag-Erling Smørgrav 
2362f374ba41SEd Maste 	case oEnableEscapeCommandline:
2363f374ba41SEd Maste 		intptr = &options->enable_escape_commandline;
2364f374ba41SEd Maste 		goto parse_flag;
2365f374ba41SEd Maste 
236638a52bd3SEd Maste 	case oRequiredRSASize:
236738a52bd3SEd Maste 		intptr = &options->required_rsa_size;
236838a52bd3SEd Maste 		goto parse_int;
236938a52bd3SEd Maste 
2370edf85781SEd Maste 	case oObscureKeystrokeTiming:
2371edf85781SEd Maste 		value = -1;
2372edf85781SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
2373edf85781SEd Maste 			if (value != -1) {
2374edf85781SEd Maste 				error("%s line %d: invalid arguments",
2375edf85781SEd Maste 				    filename, linenum);
2376edf85781SEd Maste 				goto out;
2377edf85781SEd Maste 			}
2378edf85781SEd Maste 			if (strcmp(arg, "yes") == 0 ||
2379edf85781SEd Maste 			    strcmp(arg, "true") == 0)
2380edf85781SEd Maste 				value = SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2381edf85781SEd Maste 			else if (strcmp(arg, "no") == 0 ||
2382edf85781SEd Maste 			    strcmp(arg, "false") == 0)
2383edf85781SEd Maste 				value = 0;
2384edf85781SEd Maste 			else if (strncmp(arg, "interval:", 9) == 0) {
2385edf85781SEd Maste 				if ((errstr = atoi_err(arg + 9,
2386edf85781SEd Maste 				    &value)) != NULL) {
2387edf85781SEd Maste 					error("%s line %d: integer value %s.",
2388edf85781SEd Maste 					    filename, linenum, errstr);
2389edf85781SEd Maste 					goto out;
2390edf85781SEd Maste 				}
2391edf85781SEd Maste 				if (value <= 0 || value > 1000) {
2392edf85781SEd Maste 					error("%s line %d: value out of range.",
2393edf85781SEd Maste 					    filename, linenum);
2394edf85781SEd Maste 					goto out;
2395edf85781SEd Maste 				}
2396edf85781SEd Maste 			} else {
2397edf85781SEd Maste 				error("%s line %d: unsupported argument \"%s\"",
2398edf85781SEd Maste 				    filename, linenum, arg);
2399edf85781SEd Maste 				goto out;
2400edf85781SEd Maste 			}
2401edf85781SEd Maste 		}
2402edf85781SEd Maste 		if (value == -1) {
2403edf85781SEd Maste 			error("%s line %d: missing argument",
2404edf85781SEd Maste 			    filename, linenum);
2405edf85781SEd Maste 			goto out;
2406edf85781SEd Maste 		}
2407edf85781SEd Maste 		intptr = &options->obscure_keystroke_timing_interval;
2408edf85781SEd Maste 		if (*activep && *intptr == -1)
2409edf85781SEd Maste 			*intptr = value;
2410edf85781SEd Maste 		break;
2411edf85781SEd Maste 
2412069ac184SEd Maste 	case oChannelTimeout:
2413a91a2465SEd Maste 		found = options->num_channel_timeouts == 0;
2414069ac184SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
2415069ac184SEd Maste 			/* Allow "none" only in first position */
2416069ac184SEd Maste 			if (strcasecmp(arg, "none") == 0) {
2417a91a2465SEd Maste 				if (nstrs > 0 || ac > 0) {
2418069ac184SEd Maste 					error("%s line %d: keyword %s \"none\" "
2419069ac184SEd Maste 					    "argument must appear alone.",
2420069ac184SEd Maste 					    filename, linenum, keyword);
2421069ac184SEd Maste 					goto out;
2422069ac184SEd Maste 				}
2423069ac184SEd Maste 			} else if (parse_pattern_interval(arg,
2424069ac184SEd Maste 			    NULL, NULL) != 0) {
2425069ac184SEd Maste 				fatal("%s line %d: invalid channel timeout %s",
2426069ac184SEd Maste 				    filename, linenum, arg);
2427069ac184SEd Maste 			}
2428069ac184SEd Maste 			opt_array_append(filename, linenum, keyword,
2429a91a2465SEd Maste 			    &strs, &nstrs, arg);
2430a91a2465SEd Maste 		}
2431a91a2465SEd Maste 		if (nstrs == 0) {
2432a91a2465SEd Maste 			fatal("%s line %d: no %s specified",
2433a91a2465SEd Maste 			    filename, linenum, keyword);
2434a91a2465SEd Maste 		}
2435a91a2465SEd Maste 		if (found && *activep) {
2436a91a2465SEd Maste 			options->channel_timeouts = strs;
2437a91a2465SEd Maste 			options->num_channel_timeouts = nstrs;
2438a91a2465SEd Maste 			strs = NULL; /* transferred */
2439a91a2465SEd Maste 			nstrs = 0;
2440069ac184SEd Maste 		}
2441069ac184SEd Maste 		break;
2442069ac184SEd Maste 
244380628bacSDag-Erling Smørgrav 	case oDeprecated:
244480628bacSDag-Erling Smørgrav 		debug("%s line %d: Deprecated option \"%s\"",
244580628bacSDag-Erling Smørgrav 		    filename, linenum, keyword);
244619261079SEd Maste 		argv_consume(&ac);
244719261079SEd Maste 		break;
244880628bacSDag-Erling Smørgrav 
2449cf2b5f3bSDag-Erling Smørgrav 	case oUnsupported:
2450cf2b5f3bSDag-Erling Smørgrav 		error("%s line %d: Unsupported option \"%s\"",
2451cf2b5f3bSDag-Erling Smørgrav 		    filename, linenum, keyword);
245219261079SEd Maste 		argv_consume(&ac);
245319261079SEd Maste 		break;
2454cf2b5f3bSDag-Erling Smørgrav 
2455511b41d2SMark Murray 	default:
245619261079SEd Maste 		error("%s line %d: Unimplemented opcode %d",
245719261079SEd Maste 		    filename, linenum, opcode);
245819261079SEd Maste 		goto out;
2459511b41d2SMark Murray 	}
2460511b41d2SMark Murray 
2461511b41d2SMark Murray 	/* Check that there is no garbage at end of line. */
246219261079SEd Maste 	if (ac > 0) {
246319261079SEd Maste 		error("%.200s line %d: keyword %s extra arguments "
246419261079SEd Maste 		    "at end of line", filename, linenum, keyword);
246519261079SEd Maste 		goto out;
2466c2d3a559SKris Kennaway 	}
246719261079SEd Maste 
246819261079SEd Maste 	/* success */
246919261079SEd Maste 	ret = 0;
247019261079SEd Maste  out:
2471a91a2465SEd Maste 	free_canon_cnames(cnames, ncnames);
2472a91a2465SEd Maste 	opt_array_free2(strs, NULL, nstrs);
247319261079SEd Maste 	argv_free(oav, oac);
247419261079SEd Maste 	return ret;
2475511b41d2SMark Murray }
2476511b41d2SMark Murray 
2477511b41d2SMark Murray /*
2478511b41d2SMark Murray  * Reads the config file and modifies the options accordingly.  Options
2479511b41d2SMark Murray  * should already be initialized before this call.  This never returns if
2480af12a3e7SDag-Erling Smørgrav  * there is an error.  If the file does not exist, this returns 0.
2481511b41d2SMark Murray  */
2482af12a3e7SDag-Erling Smørgrav 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)2483f7167e0eSDag-Erling Smørgrav read_config_file(const char *filename, struct passwd *pw, const char *host,
248419261079SEd Maste     const char *original_host, Options *options, int flags,
248519261079SEd Maste     int *want_final_pass)
2486511b41d2SMark Murray {
2487076ad2f8SDag-Erling Smørgrav 	int active = 1;
2488076ad2f8SDag-Erling Smørgrav 
2489076ad2f8SDag-Erling Smørgrav 	return read_config_file_depth(filename, pw, host, original_host,
249019261079SEd Maste 	    options, flags, &active, want_final_pass, 0);
2491076ad2f8SDag-Erling Smørgrav }
2492076ad2f8SDag-Erling Smørgrav 
2493076ad2f8SDag-Erling Smørgrav #define READCONF_MAX_DEPTH	16
2494076ad2f8SDag-Erling Smørgrav 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)2495076ad2f8SDag-Erling Smørgrav read_config_file_depth(const char *filename, struct passwd *pw,
2496076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, Options *options,
249719261079SEd Maste     int flags, int *activep, int *want_final_pass, int depth)
2498076ad2f8SDag-Erling Smørgrav {
2499511b41d2SMark Murray 	FILE *f;
2500190cef3dSDag-Erling Smørgrav 	char *line = NULL;
2501190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
2502076ad2f8SDag-Erling Smørgrav 	int linenum;
2503511b41d2SMark Murray 	int bad_options = 0;
2504511b41d2SMark Murray 
2505076ad2f8SDag-Erling Smørgrav 	if (depth < 0 || depth > READCONF_MAX_DEPTH)
2506076ad2f8SDag-Erling Smørgrav 		fatal("Too many recursive configuration includes");
2507076ad2f8SDag-Erling Smørgrav 
250821e764dfSDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
2509af12a3e7SDag-Erling Smørgrav 		return 0;
2510511b41d2SMark Murray 
251173370613SDag-Erling Smørgrav 	if (flags & SSHCONF_CHECKPERM) {
251221e764dfSDag-Erling Smørgrav 		struct stat sb;
251321e764dfSDag-Erling Smørgrav 
251421e764dfSDag-Erling Smørgrav 		if (fstat(fileno(f), &sb) == -1)
251521e764dfSDag-Erling Smørgrav 			fatal("fstat %s: %s", filename, strerror(errno));
251621e764dfSDag-Erling Smørgrav 		if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
251721e764dfSDag-Erling Smørgrav 		    (sb.st_mode & 022) != 0))
251821e764dfSDag-Erling Smørgrav 			fatal("Bad owner or permissions on %s", filename);
251921e764dfSDag-Erling Smørgrav 	}
252021e764dfSDag-Erling Smørgrav 
2521511b41d2SMark Murray 	debug("Reading configuration data %.200s", filename);
2522511b41d2SMark Murray 
2523511b41d2SMark Murray 	/*
2524511b41d2SMark Murray 	 * Mark that we are now processing the options.  This flag is turned
2525511b41d2SMark Murray 	 * on/off by Host specifications.
2526511b41d2SMark Murray 	 */
2527511b41d2SMark Murray 	linenum = 0;
2528190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
2529511b41d2SMark Murray 		/* Update line number counter. */
2530511b41d2SMark Murray 		linenum++;
253119261079SEd Maste 		/*
253219261079SEd Maste 		 * Trim out comments and strip whitespace.
253319261079SEd Maste 		 * NB - preserve newlines, they are needed to reproduce
253419261079SEd Maste 		 * line numbers later for error messages.
253519261079SEd Maste 		 */
2536076ad2f8SDag-Erling Smørgrav 		if (process_config_line_depth(options, pw, host, original_host,
253719261079SEd Maste 		    line, filename, linenum, activep, flags, want_final_pass,
253819261079SEd Maste 		    depth) != 0)
2539511b41d2SMark Murray 			bad_options++;
2540511b41d2SMark Murray 	}
2541190cef3dSDag-Erling Smørgrav 	free(line);
2542511b41d2SMark Murray 	fclose(f);
2543511b41d2SMark Murray 	if (bad_options > 0)
2544ca3176e7SBrian Feldman 		fatal("%s: terminating, %d bad configuration options",
2545511b41d2SMark Murray 		    filename, bad_options);
2546af12a3e7SDag-Erling Smørgrav 	return 1;
2547511b41d2SMark Murray }
2548511b41d2SMark Murray 
2549b83788ffSDag-Erling Smørgrav /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
2550b83788ffSDag-Erling Smørgrav int
option_clear_or_none(const char * o)2551b83788ffSDag-Erling Smørgrav option_clear_or_none(const char *o)
2552b83788ffSDag-Erling Smørgrav {
2553b83788ffSDag-Erling Smørgrav 	return o == NULL || strcasecmp(o, "none") == 0;
2554b83788ffSDag-Erling Smørgrav }
2555b83788ffSDag-Erling Smørgrav 
2556511b41d2SMark Murray /*
2557e9e8876aSEd Maste  * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
2558e9e8876aSEd Maste  * Allowed to be called on non-final configuration.
2559e9e8876aSEd Maste  */
2560e9e8876aSEd Maste int
config_has_permitted_cnames(Options * options)2561e9e8876aSEd Maste config_has_permitted_cnames(Options *options)
2562e9e8876aSEd Maste {
2563e9e8876aSEd Maste 	if (options->num_permitted_cnames == 1 &&
2564e9e8876aSEd Maste 	    strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 &&
2565e9e8876aSEd Maste 	    strcmp(options->permitted_cnames[0].target_list, "") == 0)
2566e9e8876aSEd Maste 		return 0;
2567e9e8876aSEd Maste 	return options->num_permitted_cnames > 0;
2568e9e8876aSEd Maste }
2569e9e8876aSEd Maste 
2570e9e8876aSEd Maste /*
2571511b41d2SMark Murray  * Initializes options to special values that indicate that they have not yet
2572511b41d2SMark Murray  * been set.  Read_config_file will only set options with this value. Options
2573511b41d2SMark Murray  * are processed in the following order: command line, user config file,
2574511b41d2SMark Murray  * system config file.  Last, fill_default_options is called.
2575511b41d2SMark Murray  */
2576511b41d2SMark Murray 
2577511b41d2SMark Murray void
initialize_options(Options * options)2578511b41d2SMark Murray initialize_options(Options * options)
2579511b41d2SMark Murray {
2580511b41d2SMark Murray 	memset(options, 'X', sizeof(*options));
2581f374ba41SEd Maste 	options->host_arg = NULL;
2582511b41d2SMark Murray 	options->forward_agent = -1;
258319261079SEd Maste 	options->forward_agent_sock_path = NULL;
2584511b41d2SMark Murray 	options->forward_x11 = -1;
25851ec0d754SDag-Erling Smørgrav 	options->forward_x11_trusted = -1;
2586e2f6069cSDag-Erling Smørgrav 	options->forward_x11_timeout = -1;
2587076ad2f8SDag-Erling Smørgrav 	options->stdio_forward_host = NULL;
2588076ad2f8SDag-Erling Smørgrav 	options->stdio_forward_port = 0;
2589076ad2f8SDag-Erling Smørgrav 	options->clear_forwardings = -1;
2590333ee039SDag-Erling Smørgrav 	options->exit_on_forward_failure = -1;
2591c2d3a559SKris Kennaway 	options->xauth_location = NULL;
2592a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.gateway_ports = -1;
2593a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
2594a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.streamlocal_bind_unlink = -1;
2595ca3176e7SBrian Feldman 	options->pubkey_authentication = -1;
2596cf2b5f3bSDag-Erling Smørgrav 	options->gss_authentication = -1;
2597cf2b5f3bSDag-Erling Smørgrav 	options->gss_deleg_creds = -1;
2598511b41d2SMark Murray 	options->password_authentication = -1;
259909958426SBrian Feldman 	options->kbd_interactive_authentication = -1;
260009958426SBrian Feldman 	options->kbd_interactive_devices = NULL;
2601ca3176e7SBrian Feldman 	options->hostbased_authentication = -1;
2602511b41d2SMark Murray 	options->batch_mode = -1;
2603511b41d2SMark Murray 	options->check_host_ip = -1;
2604511b41d2SMark Murray 	options->strict_host_key_checking = -1;
2605511b41d2SMark Murray 	options->compression = -1;
26061ec0d754SDag-Erling Smørgrav 	options->tcp_keep_alive = -1;
2607511b41d2SMark Murray 	options->port = -1;
2608cf2b5f3bSDag-Erling Smørgrav 	options->address_family = -1;
2609511b41d2SMark Murray 	options->connection_attempts = -1;
2610cf2b5f3bSDag-Erling Smørgrav 	options->connection_timeout = -1;
2611511b41d2SMark Murray 	options->number_of_password_prompts = -1;
2612e8aafc91SKris Kennaway 	options->ciphers = NULL;
2613ca3176e7SBrian Feldman 	options->macs = NULL;
26144a421b63SDag-Erling Smørgrav 	options->kex_algorithms = NULL;
2615ca3176e7SBrian Feldman 	options->hostkeyalgorithms = NULL;
26162f513db7SEd Maste 	options->ca_sign_algorithms = NULL;
2617511b41d2SMark Murray 	options->num_identity_files = 0;
261819261079SEd Maste 	memset(options->identity_keys, 0, sizeof(options->identity_keys));
2619acc1a9efSDag-Erling Smørgrav 	options->num_certificate_files = 0;
262019261079SEd Maste 	memset(options->certificates, 0, sizeof(options->certificates));
2621511b41d2SMark Murray 	options->hostname = NULL;
2622ca3176e7SBrian Feldman 	options->host_key_alias = NULL;
2623511b41d2SMark Murray 	options->proxy_command = NULL;
2624076ad2f8SDag-Erling Smørgrav 	options->jump_user = NULL;
2625076ad2f8SDag-Erling Smørgrav 	options->jump_host = NULL;
2626076ad2f8SDag-Erling Smørgrav 	options->jump_port = -1;
2627076ad2f8SDag-Erling Smørgrav 	options->jump_extra = NULL;
2628511b41d2SMark Murray 	options->user = NULL;
2629511b41d2SMark Murray 	options->escape_char = -1;
2630e146993eSDag-Erling Smørgrav 	options->num_system_hostfiles = 0;
2631e146993eSDag-Erling Smørgrav 	options->num_user_hostfiles = 0;
2632e2f6069cSDag-Erling Smørgrav 	options->local_forwards = NULL;
2633511b41d2SMark Murray 	options->num_local_forwards = 0;
2634e2f6069cSDag-Erling Smørgrav 	options->remote_forwards = NULL;
2635511b41d2SMark Murray 	options->num_remote_forwards = 0;
263619261079SEd Maste 	options->permitted_remote_opens = NULL;
263719261079SEd Maste 	options->num_permitted_remote_opens = 0;
26384f52dfbbSDag-Erling Smørgrav 	options->log_facility = SYSLOG_FACILITY_NOT_SET;
2639af12a3e7SDag-Erling Smørgrav 	options->log_level = SYSLOG_LEVEL_NOT_SET;
264019261079SEd Maste 	options->num_log_verbose = 0;
264119261079SEd Maste 	options->log_verbose = NULL;
2642ca3176e7SBrian Feldman 	options->preferred_authentications = NULL;
2643af12a3e7SDag-Erling Smørgrav 	options->bind_address = NULL;
264447dd1d1bSDag-Erling Smørgrav 	options->bind_interface = NULL;
2645b15c8340SDag-Erling Smørgrav 	options->pkcs11_provider = NULL;
264619261079SEd Maste 	options->sk_provider = NULL;
2647e73e9afaSDag-Erling Smørgrav 	options->enable_ssh_keysign = - 1;
2648af12a3e7SDag-Erling Smørgrav 	options->no_host_authentication_for_localhost = - 1;
26495962c0e9SDag-Erling Smørgrav 	options->identities_only = - 1;
2650cf2b5f3bSDag-Erling Smørgrav 	options->rekey_limit = - 1;
2651e4a9863fSDag-Erling Smørgrav 	options->rekey_interval = -1;
2652cf2b5f3bSDag-Erling Smørgrav 	options->verify_host_key_dns = -1;
26531ec0d754SDag-Erling Smørgrav 	options->server_alive_interval = -1;
26541ec0d754SDag-Erling Smørgrav 	options->server_alive_count_max = -1;
2655190cef3dSDag-Erling Smørgrav 	options->send_env = NULL;
265621e764dfSDag-Erling Smørgrav 	options->num_send_env = 0;
2657190cef3dSDag-Erling Smørgrav 	options->setenv = NULL;
2658190cef3dSDag-Erling Smørgrav 	options->num_setenv = 0;
265921e764dfSDag-Erling Smørgrav 	options->control_path = NULL;
266021e764dfSDag-Erling Smørgrav 	options->control_master = -1;
2661e2f6069cSDag-Erling Smørgrav 	options->control_persist = -1;
2662e2f6069cSDag-Erling Smørgrav 	options->control_persist_timeout = 0;
2663aa49c926SDag-Erling Smørgrav 	options->hash_known_hosts = -1;
2664b74df5b2SDag-Erling Smørgrav 	options->tun_open = -1;
2665b74df5b2SDag-Erling Smørgrav 	options->tun_local = -1;
2666b74df5b2SDag-Erling Smørgrav 	options->tun_remote = -1;
2667b74df5b2SDag-Erling Smørgrav 	options->local_command = NULL;
2668b74df5b2SDag-Erling Smørgrav 	options->permit_local_command = -1;
26694f52dfbbSDag-Erling Smørgrav 	options->remote_command = NULL;
2670acc1a9efSDag-Erling Smørgrav 	options->add_keys_to_agent = -1;
267119261079SEd Maste 	options->add_keys_to_agent_lifespan = -1;
2672076ad2f8SDag-Erling Smørgrav 	options->identity_agent = NULL;
2673d4af9e69SDag-Erling Smørgrav 	options->visual_host_key = -1;
26744a421b63SDag-Erling Smørgrav 	options->ip_qos_interactive = -1;
26754a421b63SDag-Erling Smørgrav 	options->ip_qos_bulk = -1;
2676e146993eSDag-Erling Smørgrav 	options->request_tty = -1;
267719261079SEd Maste 	options->session_type = -1;
267819261079SEd Maste 	options->stdin_null = -1;
267919261079SEd Maste 	options->fork_after_authentication = -1;
2680f7167e0eSDag-Erling Smørgrav 	options->proxy_use_fdpass = -1;
2681e4a9863fSDag-Erling Smørgrav 	options->ignored_unknown = NULL;
2682f7167e0eSDag-Erling Smørgrav 	options->num_canonical_domains = 0;
2683f7167e0eSDag-Erling Smørgrav 	options->num_permitted_cnames = 0;
2684f7167e0eSDag-Erling Smørgrav 	options->canonicalize_max_dots = -1;
2685f7167e0eSDag-Erling Smørgrav 	options->canonicalize_fallback_local = -1;
2686f7167e0eSDag-Erling Smørgrav 	options->canonicalize_hostname = -1;
2687bc5531deSDag-Erling Smørgrav 	options->revoked_host_keys = NULL;
2688bc5531deSDag-Erling Smørgrav 	options->fingerprint_hash = -1;
2689bc5531deSDag-Erling Smørgrav 	options->update_hostkeys = -1;
269019261079SEd Maste 	options->hostbased_accepted_algos = NULL;
269119261079SEd Maste 	options->pubkey_accepted_algos = NULL;
269219261079SEd Maste 	options->known_hosts_command = NULL;
269338a52bd3SEd Maste 	options->required_rsa_size = -1;
2694f374ba41SEd Maste 	options->enable_escape_commandline = -1;
2695edf85781SEd Maste 	options->obscure_keystroke_timing_interval = -1;
2696535af610SEd Maste 	options->tag = NULL;
2697069ac184SEd Maste 	options->channel_timeouts = NULL;
2698069ac184SEd Maste 	options->num_channel_timeouts = 0;
2699511b41d2SMark Murray }
2700511b41d2SMark Murray 
2701511b41d2SMark Murray /*
2702b83788ffSDag-Erling Smørgrav  * A petite version of fill_default_options() that just fills the options
2703b83788ffSDag-Erling Smørgrav  * needed for hostname canonicalization to proceed.
2704b83788ffSDag-Erling Smørgrav  */
2705b83788ffSDag-Erling Smørgrav void
fill_default_options_for_canonicalization(Options * options)2706b83788ffSDag-Erling Smørgrav fill_default_options_for_canonicalization(Options *options)
2707b83788ffSDag-Erling Smørgrav {
2708b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_max_dots == -1)
2709b83788ffSDag-Erling Smørgrav 		options->canonicalize_max_dots = 1;
2710b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_fallback_local == -1)
2711b83788ffSDag-Erling Smørgrav 		options->canonicalize_fallback_local = 1;
2712b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_hostname == -1)
2713b83788ffSDag-Erling Smørgrav 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
2714b83788ffSDag-Erling Smørgrav }
2715b83788ffSDag-Erling Smørgrav 
2716b83788ffSDag-Erling Smørgrav /*
2717511b41d2SMark Murray  * Called after processing other sources of option data, this fills those
2718511b41d2SMark Murray  * options for which no value has been specified with their default values.
2719511b41d2SMark Murray  */
272019261079SEd Maste int
fill_default_options(Options * options)2721511b41d2SMark Murray fill_default_options(Options * options)
2722511b41d2SMark Murray {
27232f513db7SEd Maste 	char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig;
272419261079SEd Maste 	char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig;
272519261079SEd Maste 	int ret = 0, r;
2726190cef3dSDag-Erling Smørgrav 
2727511b41d2SMark Murray 	if (options->forward_agent == -1)
2728db1cb46cSKris Kennaway 		options->forward_agent = 0;
2729511b41d2SMark Murray 	if (options->forward_x11 == -1)
27305dc73ebeSBrian Feldman 		options->forward_x11 = 0;
27311ec0d754SDag-Erling Smørgrav 	if (options->forward_x11_trusted == -1)
27321ec0d754SDag-Erling Smørgrav 		options->forward_x11_trusted = 0;
2733e2f6069cSDag-Erling Smørgrav 	if (options->forward_x11_timeout == -1)
2734e2f6069cSDag-Erling Smørgrav 		options->forward_x11_timeout = 1200;
2735076ad2f8SDag-Erling Smørgrav 	/*
2736076ad2f8SDag-Erling Smørgrav 	 * stdio forwarding (-W) changes the default for these but we defer
2737076ad2f8SDag-Erling Smørgrav 	 * setting the values so they can be overridden.
2738076ad2f8SDag-Erling Smørgrav 	 */
2739333ee039SDag-Erling Smørgrav 	if (options->exit_on_forward_failure == -1)
2740076ad2f8SDag-Erling Smørgrav 		options->exit_on_forward_failure =
2741076ad2f8SDag-Erling Smørgrav 		    options->stdio_forward_host != NULL ? 1 : 0;
2742076ad2f8SDag-Erling Smørgrav 	if (options->clear_forwardings == -1)
2743076ad2f8SDag-Erling Smørgrav 		options->clear_forwardings =
2744076ad2f8SDag-Erling Smørgrav 		    options->stdio_forward_host != NULL ? 1 : 0;
2745076ad2f8SDag-Erling Smørgrav 	if (options->clear_forwardings == 1)
2746076ad2f8SDag-Erling Smørgrav 		clear_forwardings(options);
2747076ad2f8SDag-Erling Smørgrav 
2748c2d3a559SKris Kennaway 	if (options->xauth_location == NULL)
274919261079SEd Maste 		options->xauth_location = xstrdup(_PATH_XAUTH);
2750a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.gateway_ports == -1)
2751a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.gateway_ports = 0;
2752a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
2753a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_mask = 0177;
2754a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.streamlocal_bind_unlink == -1)
2755a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_unlink = 0;
2756ca3176e7SBrian Feldman 	if (options->pubkey_authentication == -1)
27571323ec57SEd Maste 		options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
2758cf2b5f3bSDag-Erling Smørgrav 	if (options->gss_authentication == -1)
27591ec0d754SDag-Erling Smørgrav 		options->gss_authentication = 0;
2760cf2b5f3bSDag-Erling Smørgrav 	if (options->gss_deleg_creds == -1)
2761cf2b5f3bSDag-Erling Smørgrav 		options->gss_deleg_creds = 0;
2762511b41d2SMark Murray 	if (options->password_authentication == -1)
2763511b41d2SMark Murray 		options->password_authentication = 1;
276409958426SBrian Feldman 	if (options->kbd_interactive_authentication == -1)
2765ca3176e7SBrian Feldman 		options->kbd_interactive_authentication = 1;
2766ca3176e7SBrian Feldman 	if (options->hostbased_authentication == -1)
2767ca3176e7SBrian Feldman 		options->hostbased_authentication = 0;
2768511b41d2SMark Murray 	if (options->batch_mode == -1)
2769511b41d2SMark Murray 		options->batch_mode = 0;
2770511b41d2SMark Murray 	if (options->check_host_ip == -1)
2771975616f0SDag-Erling Smørgrav 		options->check_host_ip = 0;
2772511b41d2SMark Murray 	if (options->strict_host_key_checking == -1)
27734f52dfbbSDag-Erling Smørgrav 		options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK;
2774511b41d2SMark Murray 	if (options->compression == -1)
2775511b41d2SMark Murray 		options->compression = 0;
27761ec0d754SDag-Erling Smørgrav 	if (options->tcp_keep_alive == -1)
27771ec0d754SDag-Erling Smørgrav 		options->tcp_keep_alive = 1;
2778511b41d2SMark Murray 	if (options->port == -1)
2779511b41d2SMark Murray 		options->port = 0;	/* Filled in ssh_connect. */
2780cf2b5f3bSDag-Erling Smørgrav 	if (options->address_family == -1)
2781cf2b5f3bSDag-Erling Smørgrav 		options->address_family = AF_UNSPEC;
2782511b41d2SMark Murray 	if (options->connection_attempts == -1)
2783af12a3e7SDag-Erling Smørgrav 		options->connection_attempts = 1;
2784511b41d2SMark Murray 	if (options->number_of_password_prompts == -1)
2785511b41d2SMark Murray 		options->number_of_password_prompts = 3;
2786ca3176e7SBrian Feldman 	/* options->hostkeyalgorithms, default set in myproposals.h */
278719261079SEd Maste 	if (options->add_keys_to_agent == -1) {
2788acc1a9efSDag-Erling Smørgrav 		options->add_keys_to_agent = 0;
278919261079SEd Maste 		options->add_keys_to_agent_lifespan = 0;
279019261079SEd Maste 	}
2791511b41d2SMark Murray 	if (options->num_identity_files == 0) {
27924f52dfbbSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0);
27934a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
27944f52dfbbSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
279519261079SEd Maste 		add_identity_file(options, "~/",
279619261079SEd Maste 		    _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
27974a421b63SDag-Erling Smørgrav #endif
2798f7167e0eSDag-Erling Smørgrav 		add_identity_file(options, "~/",
2799f7167e0eSDag-Erling Smørgrav 		    _PATH_SSH_CLIENT_ID_ED25519, 0);
280019261079SEd Maste 		add_identity_file(options, "~/",
280119261079SEd Maste 		    _PATH_SSH_CLIENT_ID_ED25519_SK, 0);
280247dd1d1bSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0);
2803a91a2465SEd Maste #ifdef WITH_DSA
28041323ec57SEd Maste 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0);
2805a91a2465SEd Maste #endif
2806ca3176e7SBrian Feldman 	}
2807511b41d2SMark Murray 	if (options->escape_char == -1)
2808511b41d2SMark Murray 		options->escape_char = '~';
2809e146993eSDag-Erling Smørgrav 	if (options->num_system_hostfiles == 0) {
2810e146993eSDag-Erling Smørgrav 		options->system_hostfiles[options->num_system_hostfiles++] =
2811e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
2812e146993eSDag-Erling Smørgrav 		options->system_hostfiles[options->num_system_hostfiles++] =
2813e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
2814e146993eSDag-Erling Smørgrav 	}
281519261079SEd Maste 	if (options->update_hostkeys == -1) {
281619261079SEd Maste 		if (options->verify_host_key_dns <= 0 &&
281719261079SEd Maste 		    (options->num_user_hostfiles == 0 ||
281819261079SEd Maste 		    (options->num_user_hostfiles == 1 && strcmp(options->
281919261079SEd Maste 		    user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0)))
282019261079SEd Maste 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES;
282119261079SEd Maste 		else
282219261079SEd Maste 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO;
282319261079SEd Maste 	}
2824e146993eSDag-Erling Smørgrav 	if (options->num_user_hostfiles == 0) {
2825e146993eSDag-Erling Smørgrav 		options->user_hostfiles[options->num_user_hostfiles++] =
2826e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_USER_HOSTFILE);
2827e146993eSDag-Erling Smørgrav 		options->user_hostfiles[options->num_user_hostfiles++] =
2828e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_USER_HOSTFILE2);
2829e146993eSDag-Erling Smørgrav 	}
2830af12a3e7SDag-Erling Smørgrav 	if (options->log_level == SYSLOG_LEVEL_NOT_SET)
2831511b41d2SMark Murray 		options->log_level = SYSLOG_LEVEL_INFO;
28324f52dfbbSDag-Erling Smørgrav 	if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
28334f52dfbbSDag-Erling Smørgrav 		options->log_facility = SYSLOG_FACILITY_USER;
2834af12a3e7SDag-Erling Smørgrav 	if (options->no_host_authentication_for_localhost == - 1)
2835af12a3e7SDag-Erling Smørgrav 		options->no_host_authentication_for_localhost = 0;
28365962c0e9SDag-Erling Smørgrav 	if (options->identities_only == -1)
28375962c0e9SDag-Erling Smørgrav 		options->identities_only = 0;
2838e73e9afaSDag-Erling Smørgrav 	if (options->enable_ssh_keysign == -1)
2839e73e9afaSDag-Erling Smørgrav 		options->enable_ssh_keysign = 0;
2840cf2b5f3bSDag-Erling Smørgrav 	if (options->rekey_limit == -1)
2841cf2b5f3bSDag-Erling Smørgrav 		options->rekey_limit = 0;
2842e4a9863fSDag-Erling Smørgrav 	if (options->rekey_interval == -1)
2843e4a9863fSDag-Erling Smørgrav 		options->rekey_interval = 0;
2844cf2b5f3bSDag-Erling Smørgrav 	if (options->verify_host_key_dns == -1)
2845cf2b5f3bSDag-Erling Smørgrav 		options->verify_host_key_dns = 0;
28461ec0d754SDag-Erling Smørgrav 	if (options->server_alive_interval == -1)
28471ec0d754SDag-Erling Smørgrav 		options->server_alive_interval = 0;
28481ec0d754SDag-Erling Smørgrav 	if (options->server_alive_count_max == -1)
28491ec0d754SDag-Erling Smørgrav 		options->server_alive_count_max = 3;
285021e764dfSDag-Erling Smørgrav 	if (options->control_master == -1)
285121e764dfSDag-Erling Smørgrav 		options->control_master = 0;
2852e2f6069cSDag-Erling Smørgrav 	if (options->control_persist == -1) {
2853e2f6069cSDag-Erling Smørgrav 		options->control_persist = 0;
2854e2f6069cSDag-Erling Smørgrav 		options->control_persist_timeout = 0;
2855e2f6069cSDag-Erling Smørgrav 	}
2856aa49c926SDag-Erling Smørgrav 	if (options->hash_known_hosts == -1)
2857aa49c926SDag-Erling Smørgrav 		options->hash_known_hosts = 0;
2858b74df5b2SDag-Erling Smørgrav 	if (options->tun_open == -1)
2859b74df5b2SDag-Erling Smørgrav 		options->tun_open = SSH_TUNMODE_NO;
2860b74df5b2SDag-Erling Smørgrav 	if (options->tun_local == -1)
2861b74df5b2SDag-Erling Smørgrav 		options->tun_local = SSH_TUNID_ANY;
2862b74df5b2SDag-Erling Smørgrav 	if (options->tun_remote == -1)
2863b74df5b2SDag-Erling Smørgrav 		options->tun_remote = SSH_TUNID_ANY;
2864b74df5b2SDag-Erling Smørgrav 	if (options->permit_local_command == -1)
2865b74df5b2SDag-Erling Smørgrav 		options->permit_local_command = 0;
2866d4af9e69SDag-Erling Smørgrav 	if (options->visual_host_key == -1)
2867d4af9e69SDag-Erling Smørgrav 		options->visual_host_key = 0;
28684a421b63SDag-Erling Smørgrav 	if (options->ip_qos_interactive == -1)
2869190cef3dSDag-Erling Smørgrav 		options->ip_qos_interactive = IPTOS_DSCP_AF21;
28704a421b63SDag-Erling Smørgrav 	if (options->ip_qos_bulk == -1)
2871190cef3dSDag-Erling Smørgrav 		options->ip_qos_bulk = IPTOS_DSCP_CS1;
2872e146993eSDag-Erling Smørgrav 	if (options->request_tty == -1)
2873e146993eSDag-Erling Smørgrav 		options->request_tty = REQUEST_TTY_AUTO;
287419261079SEd Maste 	if (options->session_type == -1)
287519261079SEd Maste 		options->session_type = SESSION_TYPE_DEFAULT;
287619261079SEd Maste 	if (options->stdin_null == -1)
287719261079SEd Maste 		options->stdin_null = 0;
287819261079SEd Maste 	if (options->fork_after_authentication == -1)
287919261079SEd Maste 		options->fork_after_authentication = 0;
2880f7167e0eSDag-Erling Smørgrav 	if (options->proxy_use_fdpass == -1)
2881f7167e0eSDag-Erling Smørgrav 		options->proxy_use_fdpass = 0;
2882f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_max_dots == -1)
2883f7167e0eSDag-Erling Smørgrav 		options->canonicalize_max_dots = 1;
2884f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_fallback_local == -1)
2885f7167e0eSDag-Erling Smørgrav 		options->canonicalize_fallback_local = 1;
2886f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_hostname == -1)
2887f7167e0eSDag-Erling Smørgrav 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
2888bc5531deSDag-Erling Smørgrav 	if (options->fingerprint_hash == -1)
2889bc5531deSDag-Erling Smørgrav 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
289019261079SEd Maste #ifdef ENABLE_SK_INTERNAL
289119261079SEd Maste 	if (options->sk_provider == NULL)
289219261079SEd Maste 		options->sk_provider = xstrdup("internal");
289319261079SEd Maste #else
289419261079SEd Maste 	if (options->sk_provider == NULL)
289519261079SEd Maste 		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
289619261079SEd Maste #endif
289738a52bd3SEd Maste 	if (options->required_rsa_size == -1)
289838a52bd3SEd Maste 		options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
2899f374ba41SEd Maste 	if (options->enable_escape_commandline == -1)
2900f374ba41SEd Maste 		options->enable_escape_commandline = 0;
2901edf85781SEd Maste 	if (options->obscure_keystroke_timing_interval == -1) {
2902edf85781SEd Maste 		options->obscure_keystroke_timing_interval =
2903edf85781SEd Maste 		    SSH_KEYSTROKE_DEFAULT_INTERVAL_MS;
2904edf85781SEd Maste 	}
2905190cef3dSDag-Erling Smørgrav 
2906190cef3dSDag-Erling Smørgrav 	/* Expand KEX name lists */
2907190cef3dSDag-Erling Smørgrav 	all_cipher = cipher_alg_list(',', 0);
2908190cef3dSDag-Erling Smørgrav 	all_mac = mac_alg_list(',');
2909190cef3dSDag-Erling Smørgrav 	all_kex = kex_alg_list(',');
2910190cef3dSDag-Erling Smørgrav 	all_key = sshkey_alg_list(0, 0, 1, ',');
29112f513db7SEd Maste 	all_sig = sshkey_alg_list(0, 1, 1, ',');
291219261079SEd Maste 	/* remove unsupported algos from default lists */
291319261079SEd Maste 	def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher);
291419261079SEd Maste 	def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac);
291519261079SEd Maste 	def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex);
291619261079SEd Maste 	def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
291719261079SEd Maste 	def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig);
2918190cef3dSDag-Erling Smørgrav #define ASSEMBLE(what, defaults, all) \
2919190cef3dSDag-Erling Smørgrav 	do { \
2920190cef3dSDag-Erling Smørgrav 		if ((r = kex_assemble_names(&options->what, \
292119261079SEd Maste 		    defaults, all)) != 0) { \
292219261079SEd Maste 			error_fr(r, "%s", #what); \
292319261079SEd Maste 			goto fail; \
292419261079SEd Maste 		} \
2925190cef3dSDag-Erling Smørgrav 	} while (0)
292619261079SEd Maste 	ASSEMBLE(ciphers, def_cipher, all_cipher);
292719261079SEd Maste 	ASSEMBLE(macs, def_mac, all_mac);
292819261079SEd Maste 	ASSEMBLE(kex_algorithms, def_kex, all_kex);
292919261079SEd Maste 	ASSEMBLE(hostbased_accepted_algos, def_key, all_key);
293019261079SEd Maste 	ASSEMBLE(pubkey_accepted_algos, def_key, all_key);
293119261079SEd Maste 	ASSEMBLE(ca_sign_algorithms, def_sig, all_sig);
2932190cef3dSDag-Erling Smørgrav #undef ASSEMBLE
2933bc5531deSDag-Erling Smørgrav 
2934f7167e0eSDag-Erling Smørgrav #define CLEAR_ON_NONE(v) \
2935f7167e0eSDag-Erling Smørgrav 	do { \
2936b83788ffSDag-Erling Smørgrav 		if (option_clear_or_none(v)) { \
2937f7167e0eSDag-Erling Smørgrav 			free(v); \
2938f7167e0eSDag-Erling Smørgrav 			v = NULL; \
2939f7167e0eSDag-Erling Smørgrav 		} \
2940f7167e0eSDag-Erling Smørgrav 	} while(0)
2941069ac184SEd Maste #define CLEAR_ON_NONE_ARRAY(v, nv, none) \
2942069ac184SEd Maste 	do { \
2943069ac184SEd Maste 		if (options->nv == 1 && \
2944069ac184SEd Maste 		    strcasecmp(options->v[0], none) == 0) { \
2945069ac184SEd Maste 			free(options->v[0]); \
2946069ac184SEd Maste 			free(options->v); \
2947069ac184SEd Maste 			options->v = NULL; \
2948069ac184SEd Maste 			options->nv = 0; \
2949069ac184SEd Maste 		} \
2950069ac184SEd Maste 	} while (0)
2951f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->local_command);
29524f52dfbbSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->remote_command);
2953f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->proxy_command);
2954f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->control_path);
2955bc5531deSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->revoked_host_keys);
295619261079SEd Maste 	CLEAR_ON_NONE(options->pkcs11_provider);
295719261079SEd Maste 	CLEAR_ON_NONE(options->sk_provider);
295819261079SEd Maste 	CLEAR_ON_NONE(options->known_hosts_command);
2959069ac184SEd Maste 	CLEAR_ON_NONE_ARRAY(channel_timeouts, num_channel_timeouts, "none");
2960069ac184SEd Maste #undef CLEAR_ON_NONE
2961069ac184SEd Maste #undef CLEAR_ON_NONE_ARRAY
2962190cef3dSDag-Erling Smørgrav 	if (options->jump_host != NULL &&
2963190cef3dSDag-Erling Smørgrav 	    strcmp(options->jump_host, "none") == 0 &&
2964190cef3dSDag-Erling Smørgrav 	    options->jump_port == 0 && options->jump_user == NULL) {
2965190cef3dSDag-Erling Smørgrav 		free(options->jump_host);
2966190cef3dSDag-Erling Smørgrav 		options->jump_host = NULL;
2967190cef3dSDag-Erling Smørgrav 	}
2968e9e8876aSEd Maste 	if (options->num_permitted_cnames == 1 &&
2969e9e8876aSEd Maste 	    !config_has_permitted_cnames(options)) {
2970e9e8876aSEd Maste 		/* clean up CanonicalizePermittedCNAMEs=none */
2971e9e8876aSEd Maste 		free(options->permitted_cnames[0].source_list);
2972e9e8876aSEd Maste 		free(options->permitted_cnames[0].target_list);
2973e9e8876aSEd Maste 		memset(options->permitted_cnames, '\0',
2974e9e8876aSEd Maste 		    sizeof(*options->permitted_cnames));
2975e9e8876aSEd Maste 		options->num_permitted_cnames = 0;
2976e9e8876aSEd Maste 	}
2977076ad2f8SDag-Erling Smørgrav 	/* options->identity_agent distinguishes NULL from 'none' */
2978511b41d2SMark Murray 	/* options->user will be set in the main program if appropriate */
2979511b41d2SMark Murray 	/* options->hostname will be set in the main program if appropriate */
2980ca3176e7SBrian Feldman 	/* options->host_key_alias should not be set by default */
2981ca3176e7SBrian Feldman 	/* options->preferred_authentications will be set in ssh */
298219261079SEd Maste 
298319261079SEd Maste 	/* success */
298419261079SEd Maste 	ret = 0;
298519261079SEd Maste  fail:
298619261079SEd Maste 	free(all_cipher);
298719261079SEd Maste 	free(all_mac);
298819261079SEd Maste 	free(all_kex);
298919261079SEd Maste 	free(all_key);
299019261079SEd Maste 	free(all_sig);
299119261079SEd Maste 	free(def_cipher);
299219261079SEd Maste 	free(def_mac);
299319261079SEd Maste 	free(def_kex);
299419261079SEd Maste 	free(def_key);
299519261079SEd Maste 	free(def_sig);
299619261079SEd Maste 	return ret;
299719261079SEd Maste }
299819261079SEd Maste 
299919261079SEd Maste void
free_options(Options * o)300019261079SEd Maste free_options(Options *o)
300119261079SEd Maste {
300219261079SEd Maste 	int i;
300319261079SEd Maste 
300419261079SEd Maste 	if (o == NULL)
300519261079SEd Maste 		return;
300619261079SEd Maste 
300719261079SEd Maste #define FREE_ARRAY(type, n, a) \
300819261079SEd Maste 	do { \
300919261079SEd Maste 		type _i; \
301019261079SEd Maste 		for (_i = 0; _i < (n); _i++) \
301119261079SEd Maste 			free((a)[_i]); \
301219261079SEd Maste 	} while (0)
301319261079SEd Maste 
301419261079SEd Maste 	free(o->forward_agent_sock_path);
301519261079SEd Maste 	free(o->xauth_location);
301619261079SEd Maste 	FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
301719261079SEd Maste 	free(o->log_verbose);
301819261079SEd Maste 	free(o->ciphers);
301919261079SEd Maste 	free(o->macs);
302019261079SEd Maste 	free(o->hostkeyalgorithms);
302119261079SEd Maste 	free(o->kex_algorithms);
302219261079SEd Maste 	free(o->ca_sign_algorithms);
302319261079SEd Maste 	free(o->hostname);
302419261079SEd Maste 	free(o->host_key_alias);
302519261079SEd Maste 	free(o->proxy_command);
302619261079SEd Maste 	free(o->user);
302719261079SEd Maste 	FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles);
302819261079SEd Maste 	FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles);
302919261079SEd Maste 	free(o->preferred_authentications);
303019261079SEd Maste 	free(o->bind_address);
303119261079SEd Maste 	free(o->bind_interface);
303219261079SEd Maste 	free(o->pkcs11_provider);
303319261079SEd Maste 	free(o->sk_provider);
303419261079SEd Maste 	for (i = 0; i < o->num_identity_files; i++) {
303519261079SEd Maste 		free(o->identity_files[i]);
303619261079SEd Maste 		sshkey_free(o->identity_keys[i]);
303719261079SEd Maste 	}
303819261079SEd Maste 	for (i = 0; i < o->num_certificate_files; i++) {
303919261079SEd Maste 		free(o->certificate_files[i]);
304019261079SEd Maste 		sshkey_free(o->certificates[i]);
304119261079SEd Maste 	}
304219261079SEd Maste 	free(o->identity_agent);
304319261079SEd Maste 	for (i = 0; i < o->num_local_forwards; i++) {
304419261079SEd Maste 		free(o->local_forwards[i].listen_host);
304519261079SEd Maste 		free(o->local_forwards[i].listen_path);
304619261079SEd Maste 		free(o->local_forwards[i].connect_host);
304719261079SEd Maste 		free(o->local_forwards[i].connect_path);
304819261079SEd Maste 	}
304919261079SEd Maste 	free(o->local_forwards);
305019261079SEd Maste 	for (i = 0; i < o->num_remote_forwards; i++) {
305119261079SEd Maste 		free(o->remote_forwards[i].listen_host);
305219261079SEd Maste 		free(o->remote_forwards[i].listen_path);
305319261079SEd Maste 		free(o->remote_forwards[i].connect_host);
305419261079SEd Maste 		free(o->remote_forwards[i].connect_path);
305519261079SEd Maste 	}
305619261079SEd Maste 	free(o->remote_forwards);
305719261079SEd Maste 	free(o->stdio_forward_host);
305838a52bd3SEd Maste 	FREE_ARRAY(u_int, o->num_send_env, o->send_env);
305919261079SEd Maste 	free(o->send_env);
306038a52bd3SEd Maste 	FREE_ARRAY(u_int, o->num_setenv, o->setenv);
306119261079SEd Maste 	free(o->setenv);
306219261079SEd Maste 	free(o->control_path);
306319261079SEd Maste 	free(o->local_command);
306419261079SEd Maste 	free(o->remote_command);
306519261079SEd Maste 	FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains);
306619261079SEd Maste 	for (i = 0; i < o->num_permitted_cnames; i++) {
306719261079SEd Maste 		free(o->permitted_cnames[i].source_list);
306819261079SEd Maste 		free(o->permitted_cnames[i].target_list);
306919261079SEd Maste 	}
307019261079SEd Maste 	free(o->revoked_host_keys);
307119261079SEd Maste 	free(o->hostbased_accepted_algos);
307219261079SEd Maste 	free(o->pubkey_accepted_algos);
307319261079SEd Maste 	free(o->jump_user);
307419261079SEd Maste 	free(o->jump_host);
307519261079SEd Maste 	free(o->jump_extra);
307619261079SEd Maste 	free(o->ignored_unknown);
307719261079SEd Maste 	explicit_bzero(o, sizeof(*o));
307819261079SEd Maste #undef FREE_ARRAY
3079511b41d2SMark Murray }
3080aa49c926SDag-Erling Smørgrav 
3081a0ee8cc6SDag-Erling Smørgrav struct fwdarg {
3082a0ee8cc6SDag-Erling Smørgrav 	char *arg;
3083a0ee8cc6SDag-Erling Smørgrav 	int ispath;
3084a0ee8cc6SDag-Erling Smørgrav };
3085a0ee8cc6SDag-Erling Smørgrav 
3086a0ee8cc6SDag-Erling Smørgrav /*
3087a0ee8cc6SDag-Erling Smørgrav  * parse_fwd_field
3088a0ee8cc6SDag-Erling Smørgrav  * parses the next field in a port forwarding specification.
3089a0ee8cc6SDag-Erling Smørgrav  * sets fwd to the parsed field and advances p past the colon
3090a0ee8cc6SDag-Erling Smørgrav  * or sets it to NULL at end of string.
3091a0ee8cc6SDag-Erling Smørgrav  * returns 0 on success, else non-zero.
3092a0ee8cc6SDag-Erling Smørgrav  */
3093a0ee8cc6SDag-Erling Smørgrav static int
parse_fwd_field(char ** p,struct fwdarg * fwd)3094a0ee8cc6SDag-Erling Smørgrav parse_fwd_field(char **p, struct fwdarg *fwd)
3095a0ee8cc6SDag-Erling Smørgrav {
3096a0ee8cc6SDag-Erling Smørgrav 	char *ep, *cp = *p;
3097a0ee8cc6SDag-Erling Smørgrav 	int ispath = 0;
3098a0ee8cc6SDag-Erling Smørgrav 
3099a0ee8cc6SDag-Erling Smørgrav 	if (*cp == '\0') {
3100a0ee8cc6SDag-Erling Smørgrav 		*p = NULL;
3101a0ee8cc6SDag-Erling Smørgrav 		return -1;	/* end of string */
3102a0ee8cc6SDag-Erling Smørgrav 	}
3103a0ee8cc6SDag-Erling Smørgrav 
3104a0ee8cc6SDag-Erling Smørgrav 	/*
3105a0ee8cc6SDag-Erling Smørgrav 	 * A field escaped with square brackets is used literally.
3106a0ee8cc6SDag-Erling Smørgrav 	 * XXX - allow ']' to be escaped via backslash?
3107a0ee8cc6SDag-Erling Smørgrav 	 */
3108a0ee8cc6SDag-Erling Smørgrav 	if (*cp == '[') {
3109a0ee8cc6SDag-Erling Smørgrav 		/* find matching ']' */
3110a0ee8cc6SDag-Erling Smørgrav 		for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) {
3111a0ee8cc6SDag-Erling Smørgrav 			if (*ep == '/')
3112a0ee8cc6SDag-Erling Smørgrav 				ispath = 1;
3113a0ee8cc6SDag-Erling Smørgrav 		}
3114a0ee8cc6SDag-Erling Smørgrav 		/* no matching ']' or not at end of field. */
3115a0ee8cc6SDag-Erling Smørgrav 		if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0'))
3116a0ee8cc6SDag-Erling Smørgrav 			return -1;
3117a0ee8cc6SDag-Erling Smørgrav 		/* NUL terminate the field and advance p past the colon */
3118a0ee8cc6SDag-Erling Smørgrav 		*ep++ = '\0';
3119a0ee8cc6SDag-Erling Smørgrav 		if (*ep != '\0')
3120a0ee8cc6SDag-Erling Smørgrav 			*ep++ = '\0';
3121a0ee8cc6SDag-Erling Smørgrav 		fwd->arg = cp + 1;
3122a0ee8cc6SDag-Erling Smørgrav 		fwd->ispath = ispath;
3123a0ee8cc6SDag-Erling Smørgrav 		*p = ep;
3124a0ee8cc6SDag-Erling Smørgrav 		return 0;
3125a0ee8cc6SDag-Erling Smørgrav 	}
3126a0ee8cc6SDag-Erling Smørgrav 
3127a0ee8cc6SDag-Erling Smørgrav 	for (cp = *p; *cp != '\0'; cp++) {
3128a0ee8cc6SDag-Erling Smørgrav 		switch (*cp) {
3129a0ee8cc6SDag-Erling Smørgrav 		case '\\':
3130a0ee8cc6SDag-Erling Smørgrav 			memmove(cp, cp + 1, strlen(cp + 1) + 1);
3131557f75e5SDag-Erling Smørgrav 			if (*cp == '\0')
3132557f75e5SDag-Erling Smørgrav 				return -1;
3133a0ee8cc6SDag-Erling Smørgrav 			break;
3134a0ee8cc6SDag-Erling Smørgrav 		case '/':
3135a0ee8cc6SDag-Erling Smørgrav 			ispath = 1;
3136a0ee8cc6SDag-Erling Smørgrav 			break;
3137a0ee8cc6SDag-Erling Smørgrav 		case ':':
3138a0ee8cc6SDag-Erling Smørgrav 			*cp++ = '\0';
3139a0ee8cc6SDag-Erling Smørgrav 			goto done;
3140a0ee8cc6SDag-Erling Smørgrav 		}
3141a0ee8cc6SDag-Erling Smørgrav 	}
3142a0ee8cc6SDag-Erling Smørgrav done:
3143a0ee8cc6SDag-Erling Smørgrav 	fwd->arg = *p;
3144a0ee8cc6SDag-Erling Smørgrav 	fwd->ispath = ispath;
3145a0ee8cc6SDag-Erling Smørgrav 	*p = cp;
3146a0ee8cc6SDag-Erling Smørgrav 	return 0;
3147a0ee8cc6SDag-Erling Smørgrav }
3148a0ee8cc6SDag-Erling Smørgrav 
3149aa49c926SDag-Erling Smørgrav /*
3150aa49c926SDag-Erling Smørgrav  * parse_forward
3151aa49c926SDag-Erling Smørgrav  * parses a string containing a port forwarding specification of the form:
3152cce7d346SDag-Erling Smørgrav  *   dynamicfwd == 0
3153a0ee8cc6SDag-Erling Smørgrav  *	[listenhost:]listenport|listenpath:connecthost:connectport|connectpath
3154a0ee8cc6SDag-Erling Smørgrav  *	listenpath:connectpath
3155cce7d346SDag-Erling Smørgrav  *   dynamicfwd == 1
3156cce7d346SDag-Erling Smørgrav  *	[listenhost:]listenport
3157aa49c926SDag-Erling Smørgrav  * returns number of arguments parsed or zero on error
3158aa49c926SDag-Erling Smørgrav  */
3159aa49c926SDag-Erling Smørgrav int
parse_forward(struct Forward * fwd,const char * fwdspec,int dynamicfwd,int remotefwd)3160a0ee8cc6SDag-Erling Smørgrav parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
3161aa49c926SDag-Erling Smørgrav {
3162a0ee8cc6SDag-Erling Smørgrav 	struct fwdarg fwdargs[4];
3163a0ee8cc6SDag-Erling Smørgrav 	char *p, *cp;
316419261079SEd Maste 	int i, err;
3165aa49c926SDag-Erling Smørgrav 
3166a0ee8cc6SDag-Erling Smørgrav 	memset(fwd, 0, sizeof(*fwd));
3167a0ee8cc6SDag-Erling Smørgrav 	memset(fwdargs, 0, sizeof(fwdargs));
3168aa49c926SDag-Erling Smørgrav 
316919261079SEd Maste 	/*
317019261079SEd Maste 	 * We expand environment variables before checking if we think they're
317119261079SEd Maste 	 * paths so that if ${VAR} expands to a fully qualified path it is
317219261079SEd Maste 	 * treated as a path.
317319261079SEd Maste 	 */
317419261079SEd Maste 	cp = p = dollar_expand(&err, fwdspec);
317519261079SEd Maste 	if (p == NULL || err)
317619261079SEd Maste 		return 0;
3177aa49c926SDag-Erling Smørgrav 
3178aa49c926SDag-Erling Smørgrav 	/* skip leading spaces */
3179f7167e0eSDag-Erling Smørgrav 	while (isspace((u_char)*cp))
3180aa49c926SDag-Erling Smørgrav 		cp++;
3181aa49c926SDag-Erling Smørgrav 
3182a0ee8cc6SDag-Erling Smørgrav 	for (i = 0; i < 4; ++i) {
3183a0ee8cc6SDag-Erling Smørgrav 		if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
3184aa49c926SDag-Erling Smørgrav 			break;
3185a0ee8cc6SDag-Erling Smørgrav 	}
3186aa49c926SDag-Erling Smørgrav 
3187cce7d346SDag-Erling Smørgrav 	/* Check for trailing garbage */
3188a0ee8cc6SDag-Erling Smørgrav 	if (cp != NULL && *cp != '\0') {
3189aa49c926SDag-Erling Smørgrav 		i = 0;	/* failure */
3190a0ee8cc6SDag-Erling Smørgrav 	}
3191aa49c926SDag-Erling Smørgrav 
3192aa49c926SDag-Erling Smørgrav 	switch (i) {
3193cce7d346SDag-Erling Smørgrav 	case 1:
3194a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath) {
3195a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
3196a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
3197a0ee8cc6SDag-Erling Smørgrav 		} else {
3198cce7d346SDag-Erling Smørgrav 			fwd->listen_host = NULL;
3199a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
3200a0ee8cc6SDag-Erling Smørgrav 		}
3201cce7d346SDag-Erling Smørgrav 		fwd->connect_host = xstrdup("socks");
3202cce7d346SDag-Erling Smørgrav 		break;
3203cce7d346SDag-Erling Smørgrav 
3204cce7d346SDag-Erling Smørgrav 	case 2:
3205a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath && fwdargs[1].ispath) {
3206a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
3207a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
3208a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[1].arg);
3209a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
3210a0ee8cc6SDag-Erling Smørgrav 		} else if (fwdargs[1].ispath) {
3211a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = NULL;
3212a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
3213a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[1].arg);
3214a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
3215a0ee8cc6SDag-Erling Smørgrav 		} else {
3216a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = xstrdup(fwdargs[0].arg);
3217a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[1].arg);
3218cce7d346SDag-Erling Smørgrav 			fwd->connect_host = xstrdup("socks");
3219a0ee8cc6SDag-Erling Smørgrav 		}
3220cce7d346SDag-Erling Smørgrav 		break;
3221cce7d346SDag-Erling Smørgrav 
3222aa49c926SDag-Erling Smørgrav 	case 3:
3223a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath) {
3224a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
3225a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
3226a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_host = xstrdup(fwdargs[1].arg);
3227a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = a2port(fwdargs[2].arg);
3228a0ee8cc6SDag-Erling Smørgrav 		} else if (fwdargs[2].ispath) {
3229a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = xstrdup(fwdargs[0].arg);
3230a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[1].arg);
3231a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[2].arg);
3232a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
3233a0ee8cc6SDag-Erling Smørgrav 		} else {
3234aa49c926SDag-Erling Smørgrav 			fwd->listen_host = NULL;
3235a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
3236a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_host = xstrdup(fwdargs[1].arg);
3237a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = a2port(fwdargs[2].arg);
3238a0ee8cc6SDag-Erling Smørgrav 		}
3239aa49c926SDag-Erling Smørgrav 		break;
3240aa49c926SDag-Erling Smørgrav 
3241aa49c926SDag-Erling Smørgrav 	case 4:
3242a0ee8cc6SDag-Erling Smørgrav 		fwd->listen_host = xstrdup(fwdargs[0].arg);
3243a0ee8cc6SDag-Erling Smørgrav 		fwd->listen_port = a2port(fwdargs[1].arg);
3244a0ee8cc6SDag-Erling Smørgrav 		fwd->connect_host = xstrdup(fwdargs[2].arg);
3245a0ee8cc6SDag-Erling Smørgrav 		fwd->connect_port = a2port(fwdargs[3].arg);
3246aa49c926SDag-Erling Smørgrav 		break;
3247aa49c926SDag-Erling Smørgrav 	default:
3248aa49c926SDag-Erling Smørgrav 		i = 0; /* failure */
3249aa49c926SDag-Erling Smørgrav 	}
3250aa49c926SDag-Erling Smørgrav 
3251e4a9863fSDag-Erling Smørgrav 	free(p);
3252aa49c926SDag-Erling Smørgrav 
3253cce7d346SDag-Erling Smørgrav 	if (dynamicfwd) {
3254cce7d346SDag-Erling Smørgrav 		if (!(i == 1 || i == 2))
3255cce7d346SDag-Erling Smørgrav 			goto fail_free;
3256cce7d346SDag-Erling Smørgrav 	} else {
3257a0ee8cc6SDag-Erling Smørgrav 		if (!(i == 3 || i == 4)) {
3258a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_path == NULL &&
3259a0ee8cc6SDag-Erling Smørgrav 			    fwd->listen_path == NULL)
3260cce7d346SDag-Erling Smørgrav 				goto fail_free;
3261a0ee8cc6SDag-Erling Smørgrav 		}
3262a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3263cce7d346SDag-Erling Smørgrav 			goto fail_free;
3264cce7d346SDag-Erling Smørgrav 	}
3265cce7d346SDag-Erling Smørgrav 
3266a0ee8cc6SDag-Erling Smørgrav 	if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
3267a0ee8cc6SDag-Erling Smørgrav 	    (!remotefwd && fwd->listen_port == 0))
3268aa49c926SDag-Erling Smørgrav 		goto fail_free;
3269aa49c926SDag-Erling Smørgrav 	if (fwd->connect_host != NULL &&
3270aa49c926SDag-Erling Smørgrav 	    strlen(fwd->connect_host) >= NI_MAXHOST)
3271aa49c926SDag-Erling Smørgrav 		goto fail_free;
327219261079SEd Maste 	/*
327319261079SEd Maste 	 * XXX - if connecting to a remote socket, max sun len may not
327419261079SEd Maste 	 * match this host
327519261079SEd Maste 	 */
3276a0ee8cc6SDag-Erling Smørgrav 	if (fwd->connect_path != NULL &&
3277a0ee8cc6SDag-Erling Smørgrav 	    strlen(fwd->connect_path) >= PATH_MAX_SUN)
3278a0ee8cc6SDag-Erling Smørgrav 		goto fail_free;
3279cce7d346SDag-Erling Smørgrav 	if (fwd->listen_host != NULL &&
3280cce7d346SDag-Erling Smørgrav 	    strlen(fwd->listen_host) >= NI_MAXHOST)
3281cce7d346SDag-Erling Smørgrav 		goto fail_free;
3282a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL &&
3283a0ee8cc6SDag-Erling Smørgrav 	    strlen(fwd->listen_path) >= PATH_MAX_SUN)
3284a0ee8cc6SDag-Erling Smørgrav 		goto fail_free;
3285aa49c926SDag-Erling Smørgrav 
3286aa49c926SDag-Erling Smørgrav 	return (i);
3287aa49c926SDag-Erling Smørgrav 
3288aa49c926SDag-Erling Smørgrav  fail_free:
3289e4a9863fSDag-Erling Smørgrav 	free(fwd->connect_host);
3290cce7d346SDag-Erling Smørgrav 	fwd->connect_host = NULL;
3291a0ee8cc6SDag-Erling Smørgrav 	free(fwd->connect_path);
3292a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = NULL;
3293e4a9863fSDag-Erling Smørgrav 	free(fwd->listen_host);
3294cce7d346SDag-Erling Smørgrav 	fwd->listen_host = NULL;
3295a0ee8cc6SDag-Erling Smørgrav 	free(fwd->listen_path);
3296a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = NULL;
3297aa49c926SDag-Erling Smørgrav 	return (0);
3298aa49c926SDag-Erling Smørgrav }
3299bc5531deSDag-Erling Smørgrav 
3300076ad2f8SDag-Erling Smørgrav int
parse_jump(const char * s,Options * o,int active)3301076ad2f8SDag-Erling Smørgrav parse_jump(const char *s, Options *o, int active)
3302076ad2f8SDag-Erling Smørgrav {
3303076ad2f8SDag-Erling Smørgrav 	char *orig, *sdup, *cp;
3304076ad2f8SDag-Erling Smørgrav 	char *host = NULL, *user = NULL;
330519261079SEd Maste 	int r, ret = -1, port = -1, first;
3306076ad2f8SDag-Erling Smørgrav 
3307076ad2f8SDag-Erling Smørgrav 	active &= o->proxy_command == NULL && o->jump_host == NULL;
3308076ad2f8SDag-Erling Smørgrav 
3309076ad2f8SDag-Erling Smørgrav 	orig = sdup = xstrdup(s);
331019261079SEd Maste 
331119261079SEd Maste 	/* Remove comment and trailing whitespace */
331219261079SEd Maste 	if ((cp = strchr(orig, '#')) != NULL)
331319261079SEd Maste 		*cp = '\0';
331419261079SEd Maste 	rtrim(orig);
331519261079SEd Maste 
3316076ad2f8SDag-Erling Smørgrav 	first = active;
3317076ad2f8SDag-Erling Smørgrav 	do {
3318190cef3dSDag-Erling Smørgrav 		if (strcasecmp(s, "none") == 0)
3319190cef3dSDag-Erling Smørgrav 			break;
3320076ad2f8SDag-Erling Smørgrav 		if ((cp = strrchr(sdup, ',')) == NULL)
3321076ad2f8SDag-Erling Smørgrav 			cp = sdup; /* last */
3322076ad2f8SDag-Erling Smørgrav 		else
3323076ad2f8SDag-Erling Smørgrav 			*cp++ = '\0';
3324076ad2f8SDag-Erling Smørgrav 
3325076ad2f8SDag-Erling Smørgrav 		if (first) {
3326076ad2f8SDag-Erling Smørgrav 			/* First argument and configuration is active */
332719261079SEd Maste 			r = parse_ssh_uri(cp, &user, &host, &port);
332819261079SEd Maste 			if (r == -1 || (r == 1 &&
332919261079SEd Maste 			    parse_user_host_port(cp, &user, &host, &port) != 0))
3330076ad2f8SDag-Erling Smørgrav 				goto out;
3331076ad2f8SDag-Erling Smørgrav 		} else {
3332076ad2f8SDag-Erling Smørgrav 			/* Subsequent argument or inactive configuration */
333319261079SEd Maste 			r = parse_ssh_uri(cp, NULL, NULL, NULL);
333419261079SEd Maste 			if (r == -1 || (r == 1 &&
333519261079SEd Maste 			    parse_user_host_port(cp, NULL, NULL, NULL) != 0))
3336076ad2f8SDag-Erling Smørgrav 				goto out;
3337076ad2f8SDag-Erling Smørgrav 		}
3338076ad2f8SDag-Erling Smørgrav 		first = 0; /* only check syntax for subsequent hosts */
3339076ad2f8SDag-Erling Smørgrav 	} while (cp != sdup);
3340076ad2f8SDag-Erling Smørgrav 	/* success */
3341076ad2f8SDag-Erling Smørgrav 	if (active) {
3342190cef3dSDag-Erling Smørgrav 		if (strcasecmp(s, "none") == 0) {
3343190cef3dSDag-Erling Smørgrav 			o->jump_host = xstrdup("none");
3344190cef3dSDag-Erling Smørgrav 			o->jump_port = 0;
3345190cef3dSDag-Erling Smørgrav 		} else {
3346076ad2f8SDag-Erling Smørgrav 			o->jump_user = user;
3347076ad2f8SDag-Erling Smørgrav 			o->jump_host = host;
3348076ad2f8SDag-Erling Smørgrav 			o->jump_port = port;
3349076ad2f8SDag-Erling Smørgrav 			o->proxy_command = xstrdup("none");
3350076ad2f8SDag-Erling Smørgrav 			user = host = NULL;
3351076ad2f8SDag-Erling Smørgrav 			if ((cp = strrchr(s, ',')) != NULL && cp != s) {
3352076ad2f8SDag-Erling Smørgrav 				o->jump_extra = xstrdup(s);
3353076ad2f8SDag-Erling Smørgrav 				o->jump_extra[cp - s] = '\0';
3354076ad2f8SDag-Erling Smørgrav 			}
3355076ad2f8SDag-Erling Smørgrav 		}
3356190cef3dSDag-Erling Smørgrav 	}
3357076ad2f8SDag-Erling Smørgrav 	ret = 0;
3358076ad2f8SDag-Erling Smørgrav  out:
3359076ad2f8SDag-Erling Smørgrav 	free(orig);
3360076ad2f8SDag-Erling Smørgrav 	free(user);
3361076ad2f8SDag-Erling Smørgrav 	free(host);
3362076ad2f8SDag-Erling Smørgrav 	return ret;
3363076ad2f8SDag-Erling Smørgrav }
3364076ad2f8SDag-Erling Smørgrav 
336547dd1d1bSDag-Erling Smørgrav int
parse_ssh_uri(const char * uri,char ** userp,char ** hostp,int * portp)336647dd1d1bSDag-Erling Smørgrav parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
336747dd1d1bSDag-Erling Smørgrav {
336819261079SEd Maste 	char *user = NULL, *host = NULL, *path = NULL;
336919261079SEd Maste 	int r, port;
337047dd1d1bSDag-Erling Smørgrav 
337119261079SEd Maste 	r = parse_uri("ssh", uri, &user, &host, &port, &path);
337247dd1d1bSDag-Erling Smørgrav 	if (r == 0 && path != NULL)
337347dd1d1bSDag-Erling Smørgrav 		r = -1;		/* path not allowed */
337419261079SEd Maste 	if (r == 0) {
337519261079SEd Maste 		if (userp != NULL) {
337619261079SEd Maste 			*userp = user;
337719261079SEd Maste 			user = NULL;
337819261079SEd Maste 		}
337919261079SEd Maste 		if (hostp != NULL) {
338019261079SEd Maste 			*hostp = host;
338119261079SEd Maste 			host = NULL;
338219261079SEd Maste 		}
338319261079SEd Maste 		if (portp != NULL)
338419261079SEd Maste 			*portp = port;
338519261079SEd Maste 	}
338619261079SEd Maste 	free(user);
338719261079SEd Maste 	free(host);
338819261079SEd Maste 	free(path);
338947dd1d1bSDag-Erling Smørgrav 	return r;
339047dd1d1bSDag-Erling Smørgrav }
339147dd1d1bSDag-Erling Smørgrav 
33920fdf8faeSEd Maste /* XXX the following is a near-verbatim copy from servconf.c; refactor */
3393bc5531deSDag-Erling Smørgrav static const char *
fmt_multistate_int(int val,const struct multistate * m)3394bc5531deSDag-Erling Smørgrav fmt_multistate_int(int val, const struct multistate *m)
3395bc5531deSDag-Erling Smørgrav {
3396bc5531deSDag-Erling Smørgrav 	u_int i;
3397bc5531deSDag-Erling Smørgrav 
3398bc5531deSDag-Erling Smørgrav 	for (i = 0; m[i].key != NULL; i++) {
3399bc5531deSDag-Erling Smørgrav 		if (m[i].value == val)
3400bc5531deSDag-Erling Smørgrav 			return m[i].key;
3401bc5531deSDag-Erling Smørgrav 	}
3402bc5531deSDag-Erling Smørgrav 	return "UNKNOWN";
3403bc5531deSDag-Erling Smørgrav }
3404bc5531deSDag-Erling Smørgrav 
3405bc5531deSDag-Erling Smørgrav static const char *
fmt_intarg(OpCodes code,int val)3406bc5531deSDag-Erling Smørgrav fmt_intarg(OpCodes code, int val)
3407bc5531deSDag-Erling Smørgrav {
3408bc5531deSDag-Erling Smørgrav 	if (val == -1)
3409bc5531deSDag-Erling Smørgrav 		return "unset";
3410bc5531deSDag-Erling Smørgrav 	switch (code) {
3411bc5531deSDag-Erling Smørgrav 	case oAddressFamily:
3412bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_addressfamily);
3413bc5531deSDag-Erling Smørgrav 	case oVerifyHostKeyDNS:
3414bc5531deSDag-Erling Smørgrav 	case oUpdateHostkeys:
3415bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_yesnoask);
34164f52dfbbSDag-Erling Smørgrav 	case oStrictHostKeyChecking:
34174f52dfbbSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_strict_hostkey);
3418bc5531deSDag-Erling Smørgrav 	case oControlMaster:
3419bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_controlmaster);
3420bc5531deSDag-Erling Smørgrav 	case oTunnel:
3421bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_tunnel);
3422bc5531deSDag-Erling Smørgrav 	case oRequestTTY:
3423bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_requesttty);
342419261079SEd Maste 	case oSessionType:
342519261079SEd Maste 		return fmt_multistate_int(val, multistate_sessiontype);
3426bc5531deSDag-Erling Smørgrav 	case oCanonicalizeHostname:
3427bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_canonicalizehostname);
3428190cef3dSDag-Erling Smørgrav 	case oAddKeysToAgent:
3429190cef3dSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_yesnoaskconfirm);
34301323ec57SEd Maste 	case oPubkeyAuthentication:
34311323ec57SEd Maste 		return fmt_multistate_int(val, multistate_pubkey_auth);
3432bc5531deSDag-Erling Smørgrav 	case oFingerprintHash:
3433bc5531deSDag-Erling Smørgrav 		return ssh_digest_alg_name(val);
3434bc5531deSDag-Erling Smørgrav 	default:
3435bc5531deSDag-Erling Smørgrav 		switch (val) {
3436bc5531deSDag-Erling Smørgrav 		case 0:
3437bc5531deSDag-Erling Smørgrav 			return "no";
3438bc5531deSDag-Erling Smørgrav 		case 1:
3439bc5531deSDag-Erling Smørgrav 			return "yes";
3440bc5531deSDag-Erling Smørgrav 		default:
3441bc5531deSDag-Erling Smørgrav 			return "UNKNOWN";
3442bc5531deSDag-Erling Smørgrav 		}
3443bc5531deSDag-Erling Smørgrav 	}
3444bc5531deSDag-Erling Smørgrav }
3445bc5531deSDag-Erling Smørgrav 
3446bc5531deSDag-Erling Smørgrav static const char *
lookup_opcode_name(OpCodes code)3447bc5531deSDag-Erling Smørgrav lookup_opcode_name(OpCodes code)
3448bc5531deSDag-Erling Smørgrav {
3449bc5531deSDag-Erling Smørgrav 	u_int i;
3450bc5531deSDag-Erling Smørgrav 
3451bc5531deSDag-Erling Smørgrav 	for (i = 0; keywords[i].name != NULL; i++)
3452bc5531deSDag-Erling Smørgrav 		if (keywords[i].opcode == code)
3453bc5531deSDag-Erling Smørgrav 			return(keywords[i].name);
3454bc5531deSDag-Erling Smørgrav 	return "UNKNOWN";
3455bc5531deSDag-Erling Smørgrav }
3456bc5531deSDag-Erling Smørgrav 
3457bc5531deSDag-Erling Smørgrav static void
dump_cfg_int(OpCodes code,int val)3458bc5531deSDag-Erling Smørgrav dump_cfg_int(OpCodes code, int val)
3459bc5531deSDag-Erling Smørgrav {
3460edf85781SEd Maste 	if (code == oObscureKeystrokeTiming) {
3461edf85781SEd Maste 		if (val == 0) {
3462edf85781SEd Maste 			printf("%s no\n", lookup_opcode_name(code));
3463edf85781SEd Maste 			return;
3464edf85781SEd Maste 		} else if (val == SSH_KEYSTROKE_DEFAULT_INTERVAL_MS) {
3465edf85781SEd Maste 			printf("%s yes\n", lookup_opcode_name(code));
3466edf85781SEd Maste 			return;
3467edf85781SEd Maste 		}
3468edf85781SEd Maste 		/* FALLTHROUGH */
3469edf85781SEd Maste 	}
3470bc5531deSDag-Erling Smørgrav 	printf("%s %d\n", lookup_opcode_name(code), val);
3471bc5531deSDag-Erling Smørgrav }
3472bc5531deSDag-Erling Smørgrav 
3473bc5531deSDag-Erling Smørgrav static void
dump_cfg_fmtint(OpCodes code,int val)3474bc5531deSDag-Erling Smørgrav dump_cfg_fmtint(OpCodes code, int val)
3475bc5531deSDag-Erling Smørgrav {
3476bc5531deSDag-Erling Smørgrav 	printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
3477bc5531deSDag-Erling Smørgrav }
3478bc5531deSDag-Erling Smørgrav 
3479bc5531deSDag-Erling Smørgrav static void
dump_cfg_string(OpCodes code,const char * val)3480bc5531deSDag-Erling Smørgrav dump_cfg_string(OpCodes code, const char *val)
3481bc5531deSDag-Erling Smørgrav {
3482bc5531deSDag-Erling Smørgrav 	if (val == NULL)
3483bc5531deSDag-Erling Smørgrav 		return;
3484bc5531deSDag-Erling Smørgrav 	printf("%s %s\n", lookup_opcode_name(code), val);
3485bc5531deSDag-Erling Smørgrav }
3486bc5531deSDag-Erling Smørgrav 
3487bc5531deSDag-Erling Smørgrav static void
dump_cfg_strarray(OpCodes code,u_int count,char ** vals)3488bc5531deSDag-Erling Smørgrav dump_cfg_strarray(OpCodes code, u_int count, char **vals)
3489bc5531deSDag-Erling Smørgrav {
3490bc5531deSDag-Erling Smørgrav 	u_int i;
3491bc5531deSDag-Erling Smørgrav 
3492bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++)
3493bc5531deSDag-Erling Smørgrav 		printf("%s %s\n", lookup_opcode_name(code), vals[i]);
3494bc5531deSDag-Erling Smørgrav }
3495bc5531deSDag-Erling Smørgrav 
3496bc5531deSDag-Erling Smørgrav static void
dump_cfg_strarray_oneline(OpCodes code,u_int count,char ** vals)3497bc5531deSDag-Erling Smørgrav dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
3498bc5531deSDag-Erling Smørgrav {
3499bc5531deSDag-Erling Smørgrav 	u_int i;
3500bc5531deSDag-Erling Smørgrav 
3501bc5531deSDag-Erling Smørgrav 	printf("%s", lookup_opcode_name(code));
350219261079SEd Maste 	if (count == 0)
350319261079SEd Maste 		printf(" none");
3504bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++)
3505bc5531deSDag-Erling Smørgrav 		printf(" %s",  vals[i]);
3506bc5531deSDag-Erling Smørgrav 	printf("\n");
3507bc5531deSDag-Erling Smørgrav }
3508bc5531deSDag-Erling Smørgrav 
3509bc5531deSDag-Erling Smørgrav static void
dump_cfg_forwards(OpCodes code,u_int count,const struct Forward * fwds)3510bc5531deSDag-Erling Smørgrav dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
3511bc5531deSDag-Erling Smørgrav {
3512bc5531deSDag-Erling Smørgrav 	const struct Forward *fwd;
3513bc5531deSDag-Erling Smørgrav 	u_int i;
3514bc5531deSDag-Erling Smørgrav 
3515bc5531deSDag-Erling Smørgrav 	/* oDynamicForward */
3516bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++) {
3517bc5531deSDag-Erling Smørgrav 		fwd = &fwds[i];
3518d93a896eSDag-Erling Smørgrav 		if (code == oDynamicForward && fwd->connect_host != NULL &&
3519bc5531deSDag-Erling Smørgrav 		    strcmp(fwd->connect_host, "socks") != 0)
3520bc5531deSDag-Erling Smørgrav 			continue;
3521d93a896eSDag-Erling Smørgrav 		if (code == oLocalForward && fwd->connect_host != NULL &&
3522bc5531deSDag-Erling Smørgrav 		    strcmp(fwd->connect_host, "socks") == 0)
3523bc5531deSDag-Erling Smørgrav 			continue;
3524bc5531deSDag-Erling Smørgrav 		printf("%s", lookup_opcode_name(code));
3525bc5531deSDag-Erling Smørgrav 		if (fwd->listen_port == PORT_STREAMLOCAL)
3526bc5531deSDag-Erling Smørgrav 			printf(" %s", fwd->listen_path);
3527bc5531deSDag-Erling Smørgrav 		else if (fwd->listen_host == NULL)
3528bc5531deSDag-Erling Smørgrav 			printf(" %d", fwd->listen_port);
3529bc5531deSDag-Erling Smørgrav 		else {
3530bc5531deSDag-Erling Smørgrav 			printf(" [%s]:%d",
3531bc5531deSDag-Erling Smørgrav 			    fwd->listen_host, fwd->listen_port);
3532bc5531deSDag-Erling Smørgrav 		}
3533bc5531deSDag-Erling Smørgrav 		if (code != oDynamicForward) {
3534bc5531deSDag-Erling Smørgrav 			if (fwd->connect_port == PORT_STREAMLOCAL)
3535bc5531deSDag-Erling Smørgrav 				printf(" %s", fwd->connect_path);
3536bc5531deSDag-Erling Smørgrav 			else if (fwd->connect_host == NULL)
3537bc5531deSDag-Erling Smørgrav 				printf(" %d", fwd->connect_port);
3538bc5531deSDag-Erling Smørgrav 			else {
3539bc5531deSDag-Erling Smørgrav 				printf(" [%s]:%d",
3540bc5531deSDag-Erling Smørgrav 				    fwd->connect_host, fwd->connect_port);
3541bc5531deSDag-Erling Smørgrav 			}
3542bc5531deSDag-Erling Smørgrav 		}
3543bc5531deSDag-Erling Smørgrav 		printf("\n");
3544bc5531deSDag-Erling Smørgrav 	}
3545bc5531deSDag-Erling Smørgrav }
3546bc5531deSDag-Erling Smørgrav 
3547bc5531deSDag-Erling Smørgrav void
dump_client_config(Options * o,const char * host)3548bc5531deSDag-Erling Smørgrav dump_client_config(Options *o, const char *host)
3549bc5531deSDag-Erling Smørgrav {
355019261079SEd Maste 	int i, r;
3551190cef3dSDag-Erling Smørgrav 	char buf[8], *all_key;
3552bc5531deSDag-Erling Smørgrav 
355319261079SEd Maste 	/*
355419261079SEd Maste 	 * Expand HostKeyAlgorithms name lists. This isn't handled in
355519261079SEd Maste 	 * fill_default_options() like the other algorithm lists because
355619261079SEd Maste 	 * the host key algorithms are by default dynamically chosen based
355719261079SEd Maste 	 * on the host's keys found in known_hosts.
355819261079SEd Maste 	 */
3559190cef3dSDag-Erling Smørgrav 	all_key = sshkey_alg_list(0, 0, 1, ',');
356019261079SEd Maste 	if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(),
356119261079SEd Maste 	    all_key)) != 0)
356219261079SEd Maste 		fatal_fr(r, "expand HostKeyAlgorithms");
3563190cef3dSDag-Erling Smørgrav 	free(all_key);
3564acc1a9efSDag-Erling Smørgrav 
3565bc5531deSDag-Erling Smørgrav 	/* Most interesting options first: user, host, port */
3566f374ba41SEd Maste 	dump_cfg_string(oHost, o->host_arg);
3567bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oUser, o->user);
356819261079SEd Maste 	dump_cfg_string(oHostname, host);
3569bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oPort, o->port);
3570bc5531deSDag-Erling Smørgrav 
3571bc5531deSDag-Erling Smørgrav 	/* Flag options */
3572bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oAddressFamily, o->address_family);
3573bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oBatchMode, o->batch_mode);
3574bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
3575bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
3576bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
3577bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCompression, o->compression);
3578bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oControlMaster, o->control_master);
3579bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
3580076ad2f8SDag-Erling Smørgrav 	dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings);
3581bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
3582bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash);
3583bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oForwardX11, o->forward_x11);
3584bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
3585bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3586bc5531deSDag-Erling Smørgrav #ifdef GSSAPI
3587bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3588bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3589bc5531deSDag-Erling Smørgrav #endif /* GSSAPI */
3590bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3591bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3592bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
3593bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
3594bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
3595bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
3596bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
3597bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
3598bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
3599bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oRequestTTY, o->request_tty);
360019261079SEd Maste 	dump_cfg_fmtint(oSessionType, o->session_type);
360119261079SEd Maste 	dump_cfg_fmtint(oStdinNull, o->stdin_null);
360219261079SEd Maste 	dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication);
3603bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
3604bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
3605bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
3606bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oTunnel, o->tun_open);
3607bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
3608bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
3609bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
3610f374ba41SEd Maste 	dump_cfg_fmtint(oEnableEscapeCommandline, o->enable_escape_commandline);
3611bc5531deSDag-Erling Smørgrav 
3612bc5531deSDag-Erling Smørgrav 	/* Integer options */
3613bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
3614bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oConnectionAttempts, o->connection_attempts);
3615bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
3616bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
3617bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
3618bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
361938a52bd3SEd Maste 	dump_cfg_int(oRequiredRSASize, o->required_rsa_size);
3620edf85781SEd Maste 	dump_cfg_int(oObscureKeystrokeTiming,
3621edf85781SEd Maste 	    o->obscure_keystroke_timing_interval);
3622bc5531deSDag-Erling Smørgrav 
3623bc5531deSDag-Erling Smørgrav 	/* String options */
3624bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oBindAddress, o->bind_address);
362547dd1d1bSDag-Erling Smørgrav 	dump_cfg_string(oBindInterface, o->bind_interface);
362619261079SEd Maste 	dump_cfg_string(oCiphers, o->ciphers);
3627bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oControlPath, o->control_path);
3628acc1a9efSDag-Erling Smørgrav 	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
3629bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oHostKeyAlias, o->host_key_alias);
363019261079SEd Maste 	dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos);
3631076ad2f8SDag-Erling Smørgrav 	dump_cfg_string(oIdentityAgent, o->identity_agent);
3632190cef3dSDag-Erling Smørgrav 	dump_cfg_string(oIgnoreUnknown, o->ignored_unknown);
3633bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
363419261079SEd Maste 	dump_cfg_string(oKexAlgorithms, o->kex_algorithms);
363519261079SEd Maste 	dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms);
3636bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oLocalCommand, o->local_command);
36374f52dfbbSDag-Erling Smørgrav 	dump_cfg_string(oRemoteCommand, o->remote_command);
3638bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oLogLevel, log_level_name(o->log_level));
363919261079SEd Maste 	dump_cfg_string(oMacs, o->macs);
3640d93a896eSDag-Erling Smørgrav #ifdef ENABLE_PKCS11
3641bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
3642d93a896eSDag-Erling Smørgrav #endif
364319261079SEd Maste 	dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
3644bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
364519261079SEd Maste 	dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
3646bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
3647bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oXAuthLocation, o->xauth_location);
364819261079SEd Maste 	dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
3649535af610SEd Maste 	dump_cfg_string(oTag, o->tag);
3650bc5531deSDag-Erling Smørgrav 
3651bc5531deSDag-Erling Smørgrav 	/* Forwards */
3652bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
3653bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
3654bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
3655bc5531deSDag-Erling Smørgrav 
3656bc5531deSDag-Erling Smørgrav 	/* String array options */
3657bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
3658bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
3659190cef3dSDag-Erling Smørgrav 	dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
3660bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
3661bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
3662bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
3663190cef3dSDag-Erling Smørgrav 	dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
366419261079SEd Maste 	dump_cfg_strarray_oneline(oLogVerbose,
366519261079SEd Maste 	    o->num_log_verbose, o->log_verbose);
3666069ac184SEd Maste 	dump_cfg_strarray_oneline(oChannelTimeout,
3667069ac184SEd Maste 	    o->num_channel_timeouts, o->channel_timeouts);
3668bc5531deSDag-Erling Smørgrav 
3669bc5531deSDag-Erling Smørgrav 	/* Special cases */
3670bc5531deSDag-Erling Smørgrav 
367119261079SEd Maste 	/* PermitRemoteOpen */
367219261079SEd Maste 	if (o->num_permitted_remote_opens == 0)
367319261079SEd Maste 		printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen));
367419261079SEd Maste 	else
367519261079SEd Maste 		dump_cfg_strarray_oneline(oPermitRemoteOpen,
367619261079SEd Maste 		    o->num_permitted_remote_opens, o->permitted_remote_opens);
367719261079SEd Maste 
367819261079SEd Maste 	/* AddKeysToAgent */
367919261079SEd Maste 	if (o->add_keys_to_agent_lifespan <= 0)
368019261079SEd Maste 		dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent);
368119261079SEd Maste 	else {
368219261079SEd Maste 		printf("addkeystoagent%s %d\n",
368319261079SEd Maste 		    o->add_keys_to_agent == 3 ? " confirm" : "",
368419261079SEd Maste 		    o->add_keys_to_agent_lifespan);
368519261079SEd Maste 	}
368619261079SEd Maste 
368719261079SEd Maste 	/* oForwardAgent */
368819261079SEd Maste 	if (o->forward_agent_sock_path == NULL)
368919261079SEd Maste 		dump_cfg_fmtint(oForwardAgent, o->forward_agent);
369019261079SEd Maste 	else
369119261079SEd Maste 		dump_cfg_string(oForwardAgent, o->forward_agent_sock_path);
369219261079SEd Maste 
3693bc5531deSDag-Erling Smørgrav 	/* oConnectTimeout */
3694bc5531deSDag-Erling Smørgrav 	if (o->connection_timeout == -1)
3695bc5531deSDag-Erling Smørgrav 		printf("connecttimeout none\n");
3696bc5531deSDag-Erling Smørgrav 	else
3697bc5531deSDag-Erling Smørgrav 		dump_cfg_int(oConnectTimeout, o->connection_timeout);
3698bc5531deSDag-Erling Smørgrav 
3699bc5531deSDag-Erling Smørgrav 	/* oTunnelDevice */
3700bc5531deSDag-Erling Smørgrav 	printf("tunneldevice");
3701bc5531deSDag-Erling Smørgrav 	if (o->tun_local == SSH_TUNID_ANY)
3702bc5531deSDag-Erling Smørgrav 		printf(" any");
3703bc5531deSDag-Erling Smørgrav 	else
3704bc5531deSDag-Erling Smørgrav 		printf(" %d", o->tun_local);
3705bc5531deSDag-Erling Smørgrav 	if (o->tun_remote == SSH_TUNID_ANY)
3706bc5531deSDag-Erling Smørgrav 		printf(":any");
3707bc5531deSDag-Erling Smørgrav 	else
3708bc5531deSDag-Erling Smørgrav 		printf(":%d", o->tun_remote);
3709bc5531deSDag-Erling Smørgrav 	printf("\n");
3710bc5531deSDag-Erling Smørgrav 
3711bc5531deSDag-Erling Smørgrav 	/* oCanonicalizePermittedCNAMEs */
3712bc5531deSDag-Erling Smørgrav 	printf("canonicalizePermittedcnames");
3713e9e8876aSEd Maste 	if (o->num_permitted_cnames == 0)
3714e9e8876aSEd Maste 		printf(" none");
3715bc5531deSDag-Erling Smørgrav 	for (i = 0; i < o->num_permitted_cnames; i++) {
3716bc5531deSDag-Erling Smørgrav 		printf(" %s:%s", o->permitted_cnames[i].source_list,
3717bc5531deSDag-Erling Smørgrav 		    o->permitted_cnames[i].target_list);
3718bc5531deSDag-Erling Smørgrav 	}
3719bc5531deSDag-Erling Smørgrav 	printf("\n");
3720bc5531deSDag-Erling Smørgrav 
3721bc5531deSDag-Erling Smørgrav 	/* oControlPersist */
3722bc5531deSDag-Erling Smørgrav 	if (o->control_persist == 0 || o->control_persist_timeout == 0)
3723bc5531deSDag-Erling Smørgrav 		dump_cfg_fmtint(oControlPersist, o->control_persist);
3724bc5531deSDag-Erling Smørgrav 	else
3725bc5531deSDag-Erling Smørgrav 		dump_cfg_int(oControlPersist, o->control_persist_timeout);
3726bc5531deSDag-Erling Smørgrav 
3727bc5531deSDag-Erling Smørgrav 	/* oEscapeChar */
3728bc5531deSDag-Erling Smørgrav 	if (o->escape_char == SSH_ESCAPECHAR_NONE)
3729bc5531deSDag-Erling Smørgrav 		printf("escapechar none\n");
3730bc5531deSDag-Erling Smørgrav 	else {
3731076ad2f8SDag-Erling Smørgrav 		vis(buf, o->escape_char, VIS_WHITE, 0);
3732076ad2f8SDag-Erling Smørgrav 		printf("escapechar %s\n", buf);
3733bc5531deSDag-Erling Smørgrav 	}
3734bc5531deSDag-Erling Smørgrav 
3735bc5531deSDag-Erling Smørgrav 	/* oIPQoS */
3736bc5531deSDag-Erling Smørgrav 	printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
3737bc5531deSDag-Erling Smørgrav 	printf("%s\n", iptos2str(o->ip_qos_bulk));
3738bc5531deSDag-Erling Smørgrav 
3739bc5531deSDag-Erling Smørgrav 	/* oRekeyLimit */
3740acc1a9efSDag-Erling Smørgrav 	printf("rekeylimit %llu %d\n",
3741acc1a9efSDag-Erling Smørgrav 	    (unsigned long long)o->rekey_limit, o->rekey_interval);
3742bc5531deSDag-Erling Smørgrav 
3743bc5531deSDag-Erling Smørgrav 	/* oStreamLocalBindMask */
3744bc5531deSDag-Erling Smørgrav 	printf("streamlocalbindmask 0%o\n",
3745bc5531deSDag-Erling Smørgrav 	    o->fwd_opts.streamlocal_bind_mask);
3746076ad2f8SDag-Erling Smørgrav 
3747190cef3dSDag-Erling Smørgrav 	/* oLogFacility */
3748190cef3dSDag-Erling Smørgrav 	printf("syslogfacility %s\n", log_facility_name(o->log_facility));
3749190cef3dSDag-Erling Smørgrav 
3750076ad2f8SDag-Erling Smørgrav 	/* oProxyCommand / oProxyJump */
3751076ad2f8SDag-Erling Smørgrav 	if (o->jump_host == NULL)
3752076ad2f8SDag-Erling Smørgrav 		dump_cfg_string(oProxyCommand, o->proxy_command);
3753076ad2f8SDag-Erling Smørgrav 	else {
3754076ad2f8SDag-Erling Smørgrav 		/* Check for numeric addresses */
3755076ad2f8SDag-Erling Smørgrav 		i = strchr(o->jump_host, ':') != NULL ||
3756076ad2f8SDag-Erling Smørgrav 		    strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
3757076ad2f8SDag-Erling Smørgrav 		snprintf(buf, sizeof(buf), "%d", o->jump_port);
3758076ad2f8SDag-Erling Smørgrav 		printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
3759076ad2f8SDag-Erling Smørgrav 		    /* optional additional jump spec */
3760076ad2f8SDag-Erling Smørgrav 		    o->jump_extra == NULL ? "" : o->jump_extra,
3761076ad2f8SDag-Erling Smørgrav 		    o->jump_extra == NULL ? "" : ",",
3762076ad2f8SDag-Erling Smørgrav 		    /* optional user */
3763076ad2f8SDag-Erling Smørgrav 		    o->jump_user == NULL ? "" : o->jump_user,
3764076ad2f8SDag-Erling Smørgrav 		    o->jump_user == NULL ? "" : "@",
3765076ad2f8SDag-Erling Smørgrav 		    /* opening [ if hostname is numeric */
3766076ad2f8SDag-Erling Smørgrav 		    i ? "[" : "",
3767076ad2f8SDag-Erling Smørgrav 		    /* mandatory hostname */
3768076ad2f8SDag-Erling Smørgrav 		    o->jump_host,
3769076ad2f8SDag-Erling Smørgrav 		    /* closing ] if hostname is numeric */
3770076ad2f8SDag-Erling Smørgrav 		    i ? "]" : "",
3771076ad2f8SDag-Erling Smørgrav 		    /* optional port number */
3772076ad2f8SDag-Erling Smørgrav 		    o->jump_port <= 0 ? "" : ":",
3773076ad2f8SDag-Erling Smørgrav 		    o->jump_port <= 0 ? "" : buf);
3774076ad2f8SDag-Erling Smørgrav 	}
3775bc5531deSDag-Erling Smørgrav }
3776