xref: /freebsd/crypto/openssh/readconf.c (revision 1323ec571215a77ddd21294f0871979d5ad6b992)
1*1323ec57SEd Maste /* $OpenBSD: readconf.c,v 1.366 2022/02/08 08:59:12 dtucker 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"
16333ee039SDag-Erling Smørgrav __RCSID("$FreeBSD$");
17511b41d2SMark Murray 
18333ee039SDag-Erling Smørgrav #include <sys/types.h>
19333ee039SDag-Erling Smørgrav #include <sys/stat.h>
20333ee039SDag-Erling Smørgrav #include <sys/socket.h>
21f7167e0eSDag-Erling Smørgrav #include <sys/wait.h>
22a0ee8cc6SDag-Erling Smørgrav #include <sys/un.h>
23333ee039SDag-Erling Smørgrav 
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>
32bc5531deSDag-Erling Smørgrav #include <limits.h>
33333ee039SDag-Erling Smørgrav #include <netdb.h>
34f7167e0eSDag-Erling Smørgrav #ifdef HAVE_PATHS_H
35f7167e0eSDag-Erling Smørgrav # include <paths.h>
36f7167e0eSDag-Erling Smørgrav #endif
37f7167e0eSDag-Erling Smørgrav #include <pwd.h>
38333ee039SDag-Erling Smørgrav #include <signal.h>
39333ee039SDag-Erling Smørgrav #include <stdio.h>
40333ee039SDag-Erling Smørgrav #include <string.h>
4119261079SEd Maste #include <stdarg.h>
42333ee039SDag-Erling Smørgrav #include <unistd.h>
43076ad2f8SDag-Erling Smørgrav #ifdef USE_SYSTEM_GLOB
44076ad2f8SDag-Erling Smørgrav # include <glob.h>
45076ad2f8SDag-Erling Smørgrav #else
46076ad2f8SDag-Erling Smørgrav # include "openbsd-compat/glob.h"
47076ad2f8SDag-Erling Smørgrav #endif
48e4a9863fSDag-Erling Smørgrav #ifdef HAVE_UTIL_H
49e4a9863fSDag-Erling Smørgrav #include <util.h>
50e4a9863fSDag-Erling Smørgrav #endif
51bc5531deSDag-Erling Smørgrav #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
52bc5531deSDag-Erling Smørgrav # include <vis.h>
53bc5531deSDag-Erling Smørgrav #endif
54333ee039SDag-Erling Smørgrav 
55511b41d2SMark Murray #include "xmalloc.h"
56333ee039SDag-Erling Smørgrav #include "ssh.h"
57190cef3dSDag-Erling Smørgrav #include "ssherr.h"
58e8aafc91SKris Kennaway #include "compat.h"
59ca3176e7SBrian Feldman #include "cipher.h"
60ca3176e7SBrian Feldman #include "pathnames.h"
61ca3176e7SBrian Feldman #include "log.h"
62bc5531deSDag-Erling Smørgrav #include "sshkey.h"
63a0ee8cc6SDag-Erling Smørgrav #include "misc.h"
64ca3176e7SBrian Feldman #include "readconf.h"
65ca3176e7SBrian Feldman #include "match.h"
66ca3176e7SBrian Feldman #include "kex.h"
67ca3176e7SBrian Feldman #include "mac.h"
68f7167e0eSDag-Erling Smørgrav #include "uidswap.h"
69bc5531deSDag-Erling Smørgrav #include "myproposal.h"
70bc5531deSDag-Erling Smørgrav #include "digest.h"
71cce7d346SDag-Erling Smørgrav #include "version.h"
72511b41d2SMark Murray 
73511b41d2SMark Murray /* Format of the configuration file:
74511b41d2SMark Murray 
75511b41d2SMark Murray    # Configuration data is parsed as follows:
76511b41d2SMark Murray    #  1. command line options
77511b41d2SMark Murray    #  2. user-specific file
78511b41d2SMark Murray    #  3. system-wide file
79511b41d2SMark Murray    # Any configuration value is only changed the first time it is set.
80511b41d2SMark Murray    # Thus, host-specific definitions should be at the beginning of the
81511b41d2SMark Murray    # configuration file, and defaults at the end.
82511b41d2SMark Murray 
83511b41d2SMark Murray    # Host-specific declarations.  These may override anything above.  A single
84511b41d2SMark Murray    # host may match multiple declarations; these are processed in the order
85511b41d2SMark Murray    # that they are given in.
86511b41d2SMark Murray 
87511b41d2SMark Murray    Host *.ngs.fi ngs.fi
8880628bacSDag-Erling Smørgrav      User foo
89511b41d2SMark Murray 
90511b41d2SMark Murray    Host fake.com
9119261079SEd Maste      Hostname another.host.name.real.org
92511b41d2SMark Murray      User blaah
93511b41d2SMark Murray      Port 34289
94511b41d2SMark Murray      ForwardX11 no
95511b41d2SMark Murray      ForwardAgent no
96511b41d2SMark Murray 
97511b41d2SMark Murray    Host books.com
98511b41d2SMark Murray      RemoteForward 9999 shadows.cs.hut.fi:9999
99d93a896eSDag-Erling Smørgrav      Ciphers 3des-cbc
100511b41d2SMark Murray 
101511b41d2SMark Murray    Host fascist.blob.com
102511b41d2SMark Murray      Port 23123
103511b41d2SMark Murray      User tylonen
104511b41d2SMark Murray      PasswordAuthentication no
105511b41d2SMark Murray 
106511b41d2SMark Murray    Host puukko.hut.fi
107511b41d2SMark Murray      User t35124p
108511b41d2SMark Murray      ProxyCommand ssh-proxy %h %p
109511b41d2SMark Murray 
110511b41d2SMark Murray    Host *.fr
11180628bacSDag-Erling Smørgrav      PublicKeyAuthentication no
112511b41d2SMark Murray 
113511b41d2SMark Murray    Host *.su
114d93a896eSDag-Erling Smørgrav      Ciphers aes128-ctr
115511b41d2SMark Murray      PasswordAuthentication no
116511b41d2SMark Murray 
117b74df5b2SDag-Erling Smørgrav    Host vpn.fake.com
118b74df5b2SDag-Erling Smørgrav      Tunnel yes
119b74df5b2SDag-Erling Smørgrav      TunnelDevice 3
120b74df5b2SDag-Erling Smørgrav 
121511b41d2SMark Murray    # Defaults for various options
122511b41d2SMark Murray    Host *
123511b41d2SMark Murray      ForwardAgent no
124ca3176e7SBrian Feldman      ForwardX11 no
125511b41d2SMark Murray      PasswordAuthentication yes
126511b41d2SMark Murray      StrictHostKeyChecking yes
1271ec0d754SDag-Erling Smørgrav      TcpKeepAlive no
128511b41d2SMark Murray      IdentityFile ~/.ssh/identity
129511b41d2SMark Murray      Port 22
130511b41d2SMark Murray      EscapeChar ~
131511b41d2SMark Murray 
132511b41d2SMark Murray */
133511b41d2SMark Murray 
134076ad2f8SDag-Erling Smørgrav static int read_config_file_depth(const char *filename, struct passwd *pw,
135076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, Options *options,
13619261079SEd Maste     int flags, int *activep, int *want_final_pass, int depth);
137076ad2f8SDag-Erling Smørgrav static int process_config_line_depth(Options *options, struct passwd *pw,
138076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, char *line,
13919261079SEd Maste     const char *filename, int linenum, int *activep, int flags,
14019261079SEd Maste     int *want_final_pass, int depth);
141076ad2f8SDag-Erling Smørgrav 
142511b41d2SMark Murray /* Keyword tokens. */
143511b41d2SMark Murray 
144511b41d2SMark Murray typedef enum {
145511b41d2SMark Murray 	oBadOption,
146eccfee6eSDag-Erling Smørgrav 	oVersionAddendum,
147076ad2f8SDag-Erling Smørgrav 	oHost, oMatch, oInclude,
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,
18019261079SEd Maste 	oSecurityKeyProvider, oKnownHostsCommand,
1814f52dfbbSDag-Erling Smørgrav 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
182511b41d2SMark Murray } OpCodes;
183511b41d2SMark Murray 
184511b41d2SMark Murray /* Textual representations of the tokens. */
185511b41d2SMark Murray 
186511b41d2SMark Murray static struct {
187511b41d2SMark Murray 	const char *name;
188511b41d2SMark Murray 	OpCodes opcode;
189511b41d2SMark Murray } keywords[] = {
190d93a896eSDag-Erling Smørgrav 	/* Deprecated options */
1914f52dfbbSDag-Erling Smørgrav 	{ "protocol", oIgnore }, /* NB. silently ignored */
1924f52dfbbSDag-Erling Smørgrav 	{ "cipher", oDeprecated },
193d93a896eSDag-Erling Smørgrav 	{ "fallbacktorsh", oDeprecated },
194d93a896eSDag-Erling Smørgrav 	{ "globalknownhostsfile2", oDeprecated },
195d93a896eSDag-Erling Smørgrav 	{ "rhostsauthentication", oDeprecated },
196d93a896eSDag-Erling Smørgrav 	{ "userknownhostsfile2", oDeprecated },
197d93a896eSDag-Erling Smørgrav 	{ "useroaming", oDeprecated },
198d93a896eSDag-Erling Smørgrav 	{ "usersh", oDeprecated },
199190cef3dSDag-Erling Smørgrav 	{ "useprivilegedport", oDeprecated },
200d93a896eSDag-Erling Smørgrav 
201d93a896eSDag-Erling Smørgrav 	/* Unsupported options */
202d93a896eSDag-Erling Smørgrav 	{ "afstokenpassing", oUnsupported },
203d93a896eSDag-Erling Smørgrav 	{ "kerberosauthentication", oUnsupported },
204d93a896eSDag-Erling Smørgrav 	{ "kerberostgtpassing", oUnsupported },
20519261079SEd Maste 	{ "rsaauthentication", oUnsupported },
20619261079SEd Maste 	{ "rhostsrsaauthentication", oUnsupported },
20719261079SEd Maste 	{ "compressionlevel", oUnsupported },
208d93a896eSDag-Erling Smørgrav 
209d93a896eSDag-Erling Smørgrav 	/* Sometimes-unsupported options */
210d93a896eSDag-Erling Smørgrav #if defined(GSSAPI)
211d93a896eSDag-Erling Smørgrav 	{ "gssapiauthentication", oGssAuthentication },
212d93a896eSDag-Erling Smørgrav 	{ "gssapidelegatecredentials", oGssDelegateCreds },
213d93a896eSDag-Erling Smørgrav # else
214d93a896eSDag-Erling Smørgrav 	{ "gssapiauthentication", oUnsupported },
215d93a896eSDag-Erling Smørgrav 	{ "gssapidelegatecredentials", oUnsupported },
216d93a896eSDag-Erling Smørgrav #endif
217d93a896eSDag-Erling Smørgrav #ifdef ENABLE_PKCS11
218d93a896eSDag-Erling Smørgrav 	{ "pkcs11provider", oPKCS11Provider },
21919261079SEd Maste 	{ "smartcarddevice", oPKCS11Provider },
220d93a896eSDag-Erling Smørgrav # else
221d93a896eSDag-Erling Smørgrav 	{ "smartcarddevice", oUnsupported },
222d93a896eSDag-Erling Smørgrav 	{ "pkcs11provider", oUnsupported },
223d93a896eSDag-Erling Smørgrav #endif
224d93a896eSDag-Erling Smørgrav 
225511b41d2SMark Murray 	{ "forwardagent", oForwardAgent },
226511b41d2SMark Murray 	{ "forwardx11", oForwardX11 },
2271ec0d754SDag-Erling Smørgrav 	{ "forwardx11trusted", oForwardX11Trusted },
228e2f6069cSDag-Erling Smørgrav 	{ "forwardx11timeout", oForwardX11Timeout },
229333ee039SDag-Erling Smørgrav 	{ "exitonforwardfailure", oExitOnForwardFailure },
230c2d3a559SKris Kennaway 	{ "xauthlocation", oXAuthLocation },
231511b41d2SMark Murray 	{ "gatewayports", oGatewayPorts },
232511b41d2SMark Murray 	{ "passwordauthentication", oPasswordAuthentication },
23309958426SBrian Feldman 	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
23409958426SBrian Feldman 	{ "kbdinteractivedevices", oKbdInteractiveDevices },
23519261079SEd Maste 	{ "challengeresponseauthentication", oKbdInteractiveAuthentication }, /* alias */
23619261079SEd Maste 	{ "skeyauthentication", oKbdInteractiveAuthentication }, /* alias */
23719261079SEd Maste 	{ "tisauthentication", oKbdInteractiveAuthentication },  /* alias */
238ca3176e7SBrian Feldman 	{ "pubkeyauthentication", oPubkeyAuthentication },
239ca3176e7SBrian Feldman 	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
240ca3176e7SBrian Feldman 	{ "hostbasedauthentication", oHostbasedAuthentication },
241511b41d2SMark Murray 	{ "identityfile", oIdentityFile },
242cce7d346SDag-Erling Smørgrav 	{ "identityfile2", oIdentityFile },			/* obsolete */
2435962c0e9SDag-Erling Smørgrav 	{ "identitiesonly", oIdentitiesOnly },
244acc1a9efSDag-Erling Smørgrav 	{ "certificatefile", oCertificateFile },
245acc1a9efSDag-Erling Smørgrav 	{ "addkeystoagent", oAddKeysToAgent },
246076ad2f8SDag-Erling Smørgrav 	{ "identityagent", oIdentityAgent },
24719261079SEd Maste 	{ "hostname", oHostname },
248ca3176e7SBrian Feldman 	{ "hostkeyalias", oHostKeyAlias },
249511b41d2SMark Murray 	{ "proxycommand", oProxyCommand },
250511b41d2SMark Murray 	{ "port", oPort },
251e8aafc91SKris Kennaway 	{ "ciphers", oCiphers },
252ca3176e7SBrian Feldman 	{ "macs", oMacs },
253511b41d2SMark Murray 	{ "remoteforward", oRemoteForward },
254511b41d2SMark Murray 	{ "localforward", oLocalForward },
25519261079SEd Maste 	{ "permitremoteopen", oPermitRemoteOpen },
256511b41d2SMark Murray 	{ "user", oUser },
257511b41d2SMark Murray 	{ "host", oHost },
258f7167e0eSDag-Erling Smørgrav 	{ "match", oMatch },
259511b41d2SMark Murray 	{ "escapechar", oEscapeChar },
260511b41d2SMark Murray 	{ "globalknownhostsfile", oGlobalKnownHostsFile },
261cce7d346SDag-Erling Smørgrav 	{ "userknownhostsfile", oUserKnownHostsFile },
262511b41d2SMark Murray 	{ "connectionattempts", oConnectionAttempts },
263511b41d2SMark Murray 	{ "batchmode", oBatchMode },
264511b41d2SMark Murray 	{ "checkhostip", oCheckHostIP },
265511b41d2SMark Murray 	{ "stricthostkeychecking", oStrictHostKeyChecking },
266511b41d2SMark Murray 	{ "compression", oCompression },
2671ec0d754SDag-Erling Smørgrav 	{ "tcpkeepalive", oTCPKeepAlive },
2681ec0d754SDag-Erling Smørgrav 	{ "keepalive", oTCPKeepAlive },				/* obsolete */
269511b41d2SMark Murray 	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
2704f52dfbbSDag-Erling Smørgrav 	{ "syslogfacility", oLogFacility },
271511b41d2SMark Murray 	{ "loglevel", oLogLevel },
27219261079SEd Maste 	{ "logverbose", oLogVerbose },
273ca3176e7SBrian Feldman 	{ "dynamicforward", oDynamicForward },
274ca3176e7SBrian Feldman 	{ "preferredauthentications", oPreferredAuthentications },
275ca3176e7SBrian Feldman 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
2762f513db7SEd Maste 	{ "casignaturealgorithms", oCASignatureAlgorithms },
277af12a3e7SDag-Erling Smørgrav 	{ "bindaddress", oBindAddress },
27847dd1d1bSDag-Erling Smørgrav 	{ "bindinterface", oBindInterface },
279af12a3e7SDag-Erling Smørgrav 	{ "clearallforwardings", oClearAllForwardings },
280e73e9afaSDag-Erling Smørgrav 	{ "enablesshkeysign", oEnableSSHKeysign },
281cf2b5f3bSDag-Erling Smørgrav 	{ "verifyhostkeydns", oVerifyHostKeyDNS },
282af12a3e7SDag-Erling Smørgrav 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
283cf2b5f3bSDag-Erling Smørgrav 	{ "rekeylimit", oRekeyLimit },
284cf2b5f3bSDag-Erling Smørgrav 	{ "connecttimeout", oConnectTimeout },
285cf2b5f3bSDag-Erling Smørgrav 	{ "addressfamily", oAddressFamily },
2861ec0d754SDag-Erling Smørgrav 	{ "serveraliveinterval", oServerAliveInterval },
2871ec0d754SDag-Erling Smørgrav 	{ "serveralivecountmax", oServerAliveCountMax },
28821e764dfSDag-Erling Smørgrav 	{ "sendenv", oSendEnv },
289190cef3dSDag-Erling Smørgrav 	{ "setenv", oSetEnv },
29021e764dfSDag-Erling Smørgrav 	{ "controlpath", oControlPath },
29121e764dfSDag-Erling Smørgrav 	{ "controlmaster", oControlMaster },
292e2f6069cSDag-Erling Smørgrav 	{ "controlpersist", oControlPersist },
293aa49c926SDag-Erling Smørgrav 	{ "hashknownhosts", oHashKnownHosts },
294076ad2f8SDag-Erling Smørgrav 	{ "include", oInclude },
295b74df5b2SDag-Erling Smørgrav 	{ "tunnel", oTunnel },
296b74df5b2SDag-Erling Smørgrav 	{ "tunneldevice", oTunnelDevice },
297b74df5b2SDag-Erling Smørgrav 	{ "localcommand", oLocalCommand },
298b74df5b2SDag-Erling Smørgrav 	{ "permitlocalcommand", oPermitLocalCommand },
2994f52dfbbSDag-Erling Smørgrav 	{ "remotecommand", oRemoteCommand },
300d4af9e69SDag-Erling Smørgrav 	{ "visualhostkey", oVisualHostKey },
3014a421b63SDag-Erling Smørgrav 	{ "kexalgorithms", oKexAlgorithms },
3024a421b63SDag-Erling Smørgrav 	{ "ipqos", oIPQoS },
303e146993eSDag-Erling Smørgrav 	{ "requesttty", oRequestTTY },
30419261079SEd Maste 	{ "sessiontype", oSessionType },
30519261079SEd Maste 	{ "stdinnull", oStdinNull },
30619261079SEd Maste 	{ "forkafterauthentication", oForkAfterAuthentication },
307f7167e0eSDag-Erling Smørgrav 	{ "proxyusefdpass", oProxyUseFdpass },
308f7167e0eSDag-Erling Smørgrav 	{ "canonicaldomains", oCanonicalDomains },
309f7167e0eSDag-Erling Smørgrav 	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
310f7167e0eSDag-Erling Smørgrav 	{ "canonicalizehostname", oCanonicalizeHostname },
311f7167e0eSDag-Erling Smørgrav 	{ "canonicalizemaxdots", oCanonicalizeMaxDots },
312f7167e0eSDag-Erling Smørgrav 	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
313a0ee8cc6SDag-Erling Smørgrav 	{ "streamlocalbindmask", oStreamLocalBindMask },
314a0ee8cc6SDag-Erling Smørgrav 	{ "streamlocalbindunlink", oStreamLocalBindUnlink },
315bc5531deSDag-Erling Smørgrav 	{ "revokedhostkeys", oRevokedHostKeys },
316bc5531deSDag-Erling Smørgrav 	{ "fingerprinthash", oFingerprintHash },
317bc5531deSDag-Erling Smørgrav 	{ "updatehostkeys", oUpdateHostkeys },
31819261079SEd Maste 	{ "hostbasedacceptedalgorithms", oHostbasedAcceptedAlgorithms },
31919261079SEd Maste 	{ "hostbasedkeytypes", oHostbasedAcceptedAlgorithms }, /* obsolete */
32019261079SEd Maste 	{ "pubkeyacceptedalgorithms", oPubkeyAcceptedAlgorithms },
32119261079SEd Maste 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedAlgorithms }, /* obsolete */
322e4a9863fSDag-Erling Smørgrav 	{ "ignoreunknown", oIgnoreUnknown },
323076ad2f8SDag-Erling Smørgrav 	{ "proxyjump", oProxyJump },
32419261079SEd Maste 	{ "securitykeyprovider", oSecurityKeyProvider },
32519261079SEd Maste 	{ "knownhostscommand", oKnownHostsCommand },
326076ad2f8SDag-Erling Smørgrav 
3279860d96eSDag-Erling Smørgrav 	{ "hpndisabled", oDeprecated },
3289860d96eSDag-Erling Smørgrav 	{ "hpnbuffersize", oDeprecated },
3299860d96eSDag-Erling Smørgrav 	{ "tcprcvbufpoll", oDeprecated },
3309860d96eSDag-Erling Smørgrav 	{ "tcprcvbuf", oDeprecated },
33163620802SDag-Erling Smørgrav 	{ "noneenabled", oUnsupported },
33263620802SDag-Erling Smørgrav 	{ "noneswitch", oUnsupported },
333975616f0SDag-Erling Smørgrav 	{ "versionaddendum", oVersionAddendum },
33435762f59SEd Schouten 
335af12a3e7SDag-Erling Smørgrav 	{ NULL, oBadOption }
336511b41d2SMark Murray };
337511b41d2SMark Murray 
33819261079SEd Maste static const char *lookup_opcode_name(OpCodes code);
33919261079SEd Maste 
34019261079SEd Maste const char *
34119261079SEd Maste kex_default_pk_alg(void)
34219261079SEd Maste {
34319261079SEd Maste 	static char *pkalgs;
34419261079SEd Maste 
34519261079SEd Maste 	if (pkalgs == NULL) {
34619261079SEd Maste 		char *all_key;
34719261079SEd Maste 
34819261079SEd Maste 		all_key = sshkey_alg_list(0, 0, 1, ',');
34919261079SEd Maste 		pkalgs = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
35019261079SEd Maste 		free(all_key);
35119261079SEd Maste 	}
35219261079SEd Maste 	return pkalgs;
35319261079SEd Maste }
35419261079SEd Maste 
35519261079SEd Maste char *
35619261079SEd Maste ssh_connection_hash(const char *thishost, const char *host, const char *portstr,
35719261079SEd Maste     const char *user)
35819261079SEd Maste {
35919261079SEd Maste 	struct ssh_digest_ctx *md;
36019261079SEd Maste 	u_char conn_hash[SSH_DIGEST_MAX_LENGTH];
36119261079SEd Maste 
36219261079SEd Maste 	if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL ||
36319261079SEd Maste 	    ssh_digest_update(md, thishost, strlen(thishost)) < 0 ||
36419261079SEd Maste 	    ssh_digest_update(md, host, strlen(host)) < 0 ||
36519261079SEd Maste 	    ssh_digest_update(md, portstr, strlen(portstr)) < 0 ||
36619261079SEd Maste 	    ssh_digest_update(md, user, strlen(user)) < 0 ||
36719261079SEd Maste 	    ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0)
36819261079SEd Maste 		fatal_f("mux digest failed");
36919261079SEd Maste 	ssh_digest_free(md);
37019261079SEd Maste 	return tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
37119261079SEd Maste }
37219261079SEd Maste 
373511b41d2SMark Murray /*
374511b41d2SMark Murray  * Adds a local TCP/IP port forward to options.  Never returns if there is an
375511b41d2SMark Murray  * error.
376511b41d2SMark Murray  */
377511b41d2SMark Murray 
378511b41d2SMark Murray void
379a0ee8cc6SDag-Erling Smørgrav add_local_forward(Options *options, const struct Forward *newfwd)
380511b41d2SMark Murray {
381a0ee8cc6SDag-Erling Smørgrav 	struct Forward *fwd;
3829e14b918SEd Maste 	int i;
38303f6c5cdSDag-Erling Smørgrav 
384076ad2f8SDag-Erling Smørgrav 	/* Don't add duplicates */
385076ad2f8SDag-Erling Smørgrav 	for (i = 0; i < options->num_local_forwards; i++) {
386076ad2f8SDag-Erling Smørgrav 		if (forward_equals(newfwd, options->local_forwards + i))
387076ad2f8SDag-Erling Smørgrav 			return;
388076ad2f8SDag-Erling Smørgrav 	}
389557f75e5SDag-Erling Smørgrav 	options->local_forwards = xreallocarray(options->local_forwards,
390e2f6069cSDag-Erling Smørgrav 	    options->num_local_forwards + 1,
391e2f6069cSDag-Erling Smørgrav 	    sizeof(*options->local_forwards));
392511b41d2SMark Murray 	fwd = &options->local_forwards[options->num_local_forwards++];
393aa49c926SDag-Erling Smørgrav 
394cce7d346SDag-Erling Smørgrav 	fwd->listen_host = newfwd->listen_host;
395aa49c926SDag-Erling Smørgrav 	fwd->listen_port = newfwd->listen_port;
396a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = newfwd->listen_path;
397cce7d346SDag-Erling Smørgrav 	fwd->connect_host = newfwd->connect_host;
398aa49c926SDag-Erling Smørgrav 	fwd->connect_port = newfwd->connect_port;
399a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = newfwd->connect_path;
400511b41d2SMark Murray }
401511b41d2SMark Murray 
402511b41d2SMark Murray /*
403511b41d2SMark Murray  * Adds a remote TCP/IP port forward to options.  Never returns if there is
404511b41d2SMark Murray  * an error.
405511b41d2SMark Murray  */
406511b41d2SMark Murray 
407511b41d2SMark Murray void
408a0ee8cc6SDag-Erling Smørgrav add_remote_forward(Options *options, const struct Forward *newfwd)
409511b41d2SMark Murray {
410a0ee8cc6SDag-Erling Smørgrav 	struct Forward *fwd;
411076ad2f8SDag-Erling Smørgrav 	int i;
412e2f6069cSDag-Erling Smørgrav 
413076ad2f8SDag-Erling Smørgrav 	/* Don't add duplicates */
414076ad2f8SDag-Erling Smørgrav 	for (i = 0; i < options->num_remote_forwards; i++) {
415076ad2f8SDag-Erling Smørgrav 		if (forward_equals(newfwd, options->remote_forwards + i))
416076ad2f8SDag-Erling Smørgrav 			return;
417076ad2f8SDag-Erling Smørgrav 	}
418557f75e5SDag-Erling Smørgrav 	options->remote_forwards = xreallocarray(options->remote_forwards,
419e2f6069cSDag-Erling Smørgrav 	    options->num_remote_forwards + 1,
420e2f6069cSDag-Erling Smørgrav 	    sizeof(*options->remote_forwards));
421511b41d2SMark Murray 	fwd = &options->remote_forwards[options->num_remote_forwards++];
422aa49c926SDag-Erling Smørgrav 
423cce7d346SDag-Erling Smørgrav 	fwd->listen_host = newfwd->listen_host;
424aa49c926SDag-Erling Smørgrav 	fwd->listen_port = newfwd->listen_port;
425a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = newfwd->listen_path;
426cce7d346SDag-Erling Smørgrav 	fwd->connect_host = newfwd->connect_host;
427aa49c926SDag-Erling Smørgrav 	fwd->connect_port = newfwd->connect_port;
428a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = newfwd->connect_path;
429462c32cbSDag-Erling Smørgrav 	fwd->handle = newfwd->handle;
430e2f6069cSDag-Erling Smørgrav 	fwd->allocated_port = 0;
431511b41d2SMark Murray }
432511b41d2SMark Murray 
433af12a3e7SDag-Erling Smørgrav static void
434af12a3e7SDag-Erling Smørgrav clear_forwardings(Options *options)
435af12a3e7SDag-Erling Smørgrav {
436af12a3e7SDag-Erling Smørgrav 	int i;
437af12a3e7SDag-Erling Smørgrav 
438aa49c926SDag-Erling Smørgrav 	for (i = 0; i < options->num_local_forwards; i++) {
439e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards[i].listen_host);
440a0ee8cc6SDag-Erling Smørgrav 		free(options->local_forwards[i].listen_path);
441e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards[i].connect_host);
442a0ee8cc6SDag-Erling Smørgrav 		free(options->local_forwards[i].connect_path);
443aa49c926SDag-Erling Smørgrav 	}
444e2f6069cSDag-Erling Smørgrav 	if (options->num_local_forwards > 0) {
445e4a9863fSDag-Erling Smørgrav 		free(options->local_forwards);
446e2f6069cSDag-Erling Smørgrav 		options->local_forwards = NULL;
447e2f6069cSDag-Erling Smørgrav 	}
448af12a3e7SDag-Erling Smørgrav 	options->num_local_forwards = 0;
449aa49c926SDag-Erling Smørgrav 	for (i = 0; i < options->num_remote_forwards; i++) {
450e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards[i].listen_host);
451a0ee8cc6SDag-Erling Smørgrav 		free(options->remote_forwards[i].listen_path);
452e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards[i].connect_host);
453a0ee8cc6SDag-Erling Smørgrav 		free(options->remote_forwards[i].connect_path);
454aa49c926SDag-Erling Smørgrav 	}
455e2f6069cSDag-Erling Smørgrav 	if (options->num_remote_forwards > 0) {
456e4a9863fSDag-Erling Smørgrav 		free(options->remote_forwards);
457e2f6069cSDag-Erling Smørgrav 		options->remote_forwards = NULL;
458e2f6069cSDag-Erling Smørgrav 	}
459af12a3e7SDag-Erling Smørgrav 	options->num_remote_forwards = 0;
460b74df5b2SDag-Erling Smørgrav 	options->tun_open = SSH_TUNMODE_NO;
461af12a3e7SDag-Erling Smørgrav }
462af12a3e7SDag-Erling Smørgrav 
463fa67e83cSDag-Erling Smørgrav void
464acc1a9efSDag-Erling Smørgrav add_certificate_file(Options *options, const char *path, int userprovided)
465acc1a9efSDag-Erling Smørgrav {
466acc1a9efSDag-Erling Smørgrav 	int i;
467acc1a9efSDag-Erling Smørgrav 
468acc1a9efSDag-Erling Smørgrav 	if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES)
469acc1a9efSDag-Erling Smørgrav 		fatal("Too many certificate files specified (max %d)",
470acc1a9efSDag-Erling Smørgrav 		    SSH_MAX_CERTIFICATE_FILES);
471acc1a9efSDag-Erling Smørgrav 
472acc1a9efSDag-Erling Smørgrav 	/* Avoid registering duplicates */
473acc1a9efSDag-Erling Smørgrav 	for (i = 0; i < options->num_certificate_files; i++) {
474acc1a9efSDag-Erling Smørgrav 		if (options->certificate_file_userprovided[i] == userprovided &&
475acc1a9efSDag-Erling Smørgrav 		    strcmp(options->certificate_files[i], path) == 0) {
47619261079SEd Maste 			debug2_f("ignoring duplicate key %s", path);
477acc1a9efSDag-Erling Smørgrav 			return;
478acc1a9efSDag-Erling Smørgrav 		}
479acc1a9efSDag-Erling Smørgrav 	}
480acc1a9efSDag-Erling Smørgrav 
481acc1a9efSDag-Erling Smørgrav 	options->certificate_file_userprovided[options->num_certificate_files] =
482acc1a9efSDag-Erling Smørgrav 	    userprovided;
483acc1a9efSDag-Erling Smørgrav 	options->certificate_files[options->num_certificate_files++] =
484acc1a9efSDag-Erling Smørgrav 	    xstrdup(path);
485acc1a9efSDag-Erling Smørgrav }
486acc1a9efSDag-Erling Smørgrav 
487acc1a9efSDag-Erling Smørgrav void
488fa67e83cSDag-Erling Smørgrav add_identity_file(Options *options, const char *dir, const char *filename,
489fa67e83cSDag-Erling Smørgrav     int userprovided)
490fa67e83cSDag-Erling Smørgrav {
491fa67e83cSDag-Erling Smørgrav 	char *path;
492a0ee8cc6SDag-Erling Smørgrav 	int i;
493fa67e83cSDag-Erling Smørgrav 
494fa67e83cSDag-Erling Smørgrav 	if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
495fa67e83cSDag-Erling Smørgrav 		fatal("Too many identity files specified (max %d)",
496fa67e83cSDag-Erling Smørgrav 		    SSH_MAX_IDENTITY_FILES);
497fa67e83cSDag-Erling Smørgrav 
498fa67e83cSDag-Erling Smørgrav 	if (dir == NULL) /* no dir, filename is absolute */
499fa67e83cSDag-Erling Smørgrav 		path = xstrdup(filename);
5004f52dfbbSDag-Erling Smørgrav 	else if (xasprintf(&path, "%s%s", dir, filename) >= PATH_MAX)
5014f52dfbbSDag-Erling Smørgrav 		fatal("Identity file path %s too long", path);
502fa67e83cSDag-Erling Smørgrav 
503a0ee8cc6SDag-Erling Smørgrav 	/* Avoid registering duplicates */
504a0ee8cc6SDag-Erling Smørgrav 	for (i = 0; i < options->num_identity_files; i++) {
505a0ee8cc6SDag-Erling Smørgrav 		if (options->identity_file_userprovided[i] == userprovided &&
506a0ee8cc6SDag-Erling Smørgrav 		    strcmp(options->identity_files[i], path) == 0) {
50719261079SEd Maste 			debug2_f("ignoring duplicate key %s", path);
508a0ee8cc6SDag-Erling Smørgrav 			free(path);
509a0ee8cc6SDag-Erling Smørgrav 			return;
510a0ee8cc6SDag-Erling Smørgrav 		}
511a0ee8cc6SDag-Erling Smørgrav 	}
512a0ee8cc6SDag-Erling Smørgrav 
513fa67e83cSDag-Erling Smørgrav 	options->identity_file_userprovided[options->num_identity_files] =
514fa67e83cSDag-Erling Smørgrav 	    userprovided;
515fa67e83cSDag-Erling Smørgrav 	options->identity_files[options->num_identity_files++] = path;
516fa67e83cSDag-Erling Smørgrav }
517fa67e83cSDag-Erling Smørgrav 
518f7167e0eSDag-Erling Smørgrav int
519f7167e0eSDag-Erling Smørgrav default_ssh_port(void)
520f7167e0eSDag-Erling Smørgrav {
521f7167e0eSDag-Erling Smørgrav 	static int port;
522f7167e0eSDag-Erling Smørgrav 	struct servent *sp;
523f7167e0eSDag-Erling Smørgrav 
524f7167e0eSDag-Erling Smørgrav 	if (port == 0) {
525f7167e0eSDag-Erling Smørgrav 		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
526f7167e0eSDag-Erling Smørgrav 		port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
527f7167e0eSDag-Erling Smørgrav 	}
528f7167e0eSDag-Erling Smørgrav 	return port;
529f7167e0eSDag-Erling Smørgrav }
530f7167e0eSDag-Erling Smørgrav 
531f7167e0eSDag-Erling Smørgrav /*
532f7167e0eSDag-Erling Smørgrav  * Execute a command in a shell.
533f7167e0eSDag-Erling Smørgrav  * Return its exit status or -1 on abnormal exit.
534f7167e0eSDag-Erling Smørgrav  */
535f7167e0eSDag-Erling Smørgrav static int
536f7167e0eSDag-Erling Smørgrav execute_in_shell(const char *cmd)
537f7167e0eSDag-Erling Smørgrav {
538acc1a9efSDag-Erling Smørgrav 	char *shell;
539f7167e0eSDag-Erling Smørgrav 	pid_t pid;
54019261079SEd Maste 	int status;
541f7167e0eSDag-Erling Smørgrav 
542f7167e0eSDag-Erling Smørgrav 	if ((shell = getenv("SHELL")) == NULL)
543f7167e0eSDag-Erling Smørgrav 		shell = _PATH_BSHELL;
544f7167e0eSDag-Erling Smørgrav 
54519261079SEd Maste 	if (access(shell, X_OK) == -1) {
54619261079SEd Maste 		fatal("Shell \"%s\" is not executable: %s",
54719261079SEd Maste 		    shell, strerror(errno));
54819261079SEd Maste 	}
549f7167e0eSDag-Erling Smørgrav 
550f7167e0eSDag-Erling Smørgrav 	debug("Executing command: '%.500s'", cmd);
551f7167e0eSDag-Erling Smørgrav 
552f7167e0eSDag-Erling Smørgrav 	/* Fork and execute the command. */
553f7167e0eSDag-Erling Smørgrav 	if ((pid = fork()) == 0) {
554f7167e0eSDag-Erling Smørgrav 		char *argv[4];
555f7167e0eSDag-Erling Smørgrav 
55619261079SEd Maste 		if (stdfd_devnull(1, 1, 0) == -1)
55719261079SEd Maste 			fatal_f("stdfd_devnull failed");
558f7167e0eSDag-Erling Smørgrav 		closefrom(STDERR_FILENO + 1);
559f7167e0eSDag-Erling Smørgrav 
560f7167e0eSDag-Erling Smørgrav 		argv[0] = shell;
561f7167e0eSDag-Erling Smørgrav 		argv[1] = "-c";
562acc1a9efSDag-Erling Smørgrav 		argv[2] = xstrdup(cmd);
563f7167e0eSDag-Erling Smørgrav 		argv[3] = NULL;
564f7167e0eSDag-Erling Smørgrav 
565f7167e0eSDag-Erling Smørgrav 		execv(argv[0], argv);
566f7167e0eSDag-Erling Smørgrav 		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
567f7167e0eSDag-Erling Smørgrav 		/* Die with signal to make this error apparent to parent. */
56819261079SEd Maste 		ssh_signal(SIGTERM, SIG_DFL);
569f7167e0eSDag-Erling Smørgrav 		kill(getpid(), SIGTERM);
570f7167e0eSDag-Erling Smørgrav 		_exit(1);
571f7167e0eSDag-Erling Smørgrav 	}
572f7167e0eSDag-Erling Smørgrav 	/* Parent. */
57319261079SEd Maste 	if (pid == -1)
57419261079SEd Maste 		fatal_f("fork: %.100s", strerror(errno));
575f7167e0eSDag-Erling Smørgrav 
576f7167e0eSDag-Erling Smørgrav 	while (waitpid(pid, &status, 0) == -1) {
577f7167e0eSDag-Erling Smørgrav 		if (errno != EINTR && errno != EAGAIN)
57819261079SEd Maste 			fatal_f("waitpid: %s", strerror(errno));
579f7167e0eSDag-Erling Smørgrav 	}
580f7167e0eSDag-Erling Smørgrav 	if (!WIFEXITED(status)) {
581f7167e0eSDag-Erling Smørgrav 		error("command '%.100s' exited abnormally", cmd);
582f7167e0eSDag-Erling Smørgrav 		return -1;
583f7167e0eSDag-Erling Smørgrav 	}
584f7167e0eSDag-Erling Smørgrav 	debug3("command returned status %d", WEXITSTATUS(status));
585f7167e0eSDag-Erling Smørgrav 	return WEXITSTATUS(status);
586f7167e0eSDag-Erling Smørgrav }
587f7167e0eSDag-Erling Smørgrav 
588f7167e0eSDag-Erling Smørgrav /*
589f7167e0eSDag-Erling Smørgrav  * Parse and execute a Match directive.
590f7167e0eSDag-Erling Smørgrav  */
591f7167e0eSDag-Erling Smørgrav static int
592f7167e0eSDag-Erling Smørgrav match_cfg_line(Options *options, char **condition, struct passwd *pw,
59319261079SEd Maste     const char *host_arg, const char *original_host, int final_pass,
59419261079SEd Maste     int *want_final_pass, const char *filename, int linenum)
595f7167e0eSDag-Erling Smørgrav {
596bc5531deSDag-Erling Smørgrav 	char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria;
597f7167e0eSDag-Erling Smørgrav 	const char *ruser;
598bc5531deSDag-Erling Smørgrav 	int r, port, this_result, result = 1, attributes = 0, negate;
599f7167e0eSDag-Erling Smørgrav 	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
600190cef3dSDag-Erling Smørgrav 	char uidstr[32];
601f7167e0eSDag-Erling Smørgrav 
602f7167e0eSDag-Erling Smørgrav 	/*
603f7167e0eSDag-Erling Smørgrav 	 * Configuration is likely to be incomplete at this point so we
604f7167e0eSDag-Erling Smørgrav 	 * must be prepared to use default values.
605f7167e0eSDag-Erling Smørgrav 	 */
606f7167e0eSDag-Erling Smørgrav 	port = options->port <= 0 ? default_ssh_port() : options->port;
607f7167e0eSDag-Erling Smørgrav 	ruser = options->user == NULL ? pw->pw_name : options->user;
60819261079SEd Maste 	if (final_pass) {
609acc1a9efSDag-Erling Smørgrav 		host = xstrdup(options->hostname);
610acc1a9efSDag-Erling Smørgrav 	} else if (options->hostname != NULL) {
611f7167e0eSDag-Erling Smørgrav 		/* NB. Please keep in sync with ssh.c:main() */
612f7167e0eSDag-Erling Smørgrav 		host = percent_expand(options->hostname,
613f7167e0eSDag-Erling Smørgrav 		    "h", host_arg, (char *)NULL);
614acc1a9efSDag-Erling Smørgrav 	} else {
615f7167e0eSDag-Erling Smørgrav 		host = xstrdup(host_arg);
616acc1a9efSDag-Erling Smørgrav 	}
617f7167e0eSDag-Erling Smørgrav 
618bc5531deSDag-Erling Smørgrav 	debug2("checking match for '%s' host %s originally %s",
619bc5531deSDag-Erling Smørgrav 	    cp, host, original_host);
620bc5531deSDag-Erling Smørgrav 	while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') {
62119261079SEd Maste 		/* Terminate on comment */
62219261079SEd Maste 		if (*attrib == '#') {
62319261079SEd Maste 			cp = NULL; /* mark all arguments consumed */
62419261079SEd Maste 			break;
62519261079SEd Maste 		}
62619261079SEd Maste 		arg = criteria = NULL;
627bc5531deSDag-Erling Smørgrav 		this_result = 1;
628bc5531deSDag-Erling Smørgrav 		if ((negate = attrib[0] == '!'))
629bc5531deSDag-Erling Smørgrav 			attrib++;
63019261079SEd Maste 		/* Criterion "all" has no argument and must appear alone */
631f7167e0eSDag-Erling Smørgrav 		if (strcasecmp(attrib, "all") == 0) {
63219261079SEd Maste 			if (attributes > 1 || ((arg = strdelim(&cp)) != NULL &&
63319261079SEd Maste 			    *arg != '\0' && *arg != '#')) {
634bc5531deSDag-Erling Smørgrav 				error("%.200s line %d: '%s' cannot be combined "
635bc5531deSDag-Erling Smørgrav 				    "with other Match attributes",
636bc5531deSDag-Erling Smørgrav 				    filename, linenum, oattrib);
637f7167e0eSDag-Erling Smørgrav 				result = -1;
638f7167e0eSDag-Erling Smørgrav 				goto out;
639f7167e0eSDag-Erling Smørgrav 			}
64019261079SEd Maste 			if (arg != NULL && *arg == '#')
64119261079SEd Maste 				cp = NULL; /* mark all arguments consumed */
642bc5531deSDag-Erling Smørgrav 			if (result)
643bc5531deSDag-Erling Smørgrav 				result = negate ? 0 : 1;
644f7167e0eSDag-Erling Smørgrav 			goto out;
645f7167e0eSDag-Erling Smørgrav 		}
646bc5531deSDag-Erling Smørgrav 		attributes++;
64719261079SEd Maste 		/* criteria "final" and "canonical" have no argument */
64819261079SEd Maste 		if (strcasecmp(attrib, "canonical") == 0 ||
64919261079SEd Maste 		    strcasecmp(attrib, "final") == 0) {
65019261079SEd Maste 			/*
65119261079SEd Maste 			 * If the config requests "Match final" then remember
65219261079SEd Maste 			 * this so we can perform a second pass later.
65319261079SEd Maste 			 */
65419261079SEd Maste 			if (strcasecmp(attrib, "final") == 0 &&
65519261079SEd Maste 			    want_final_pass != NULL)
65619261079SEd Maste 				*want_final_pass = 1;
65719261079SEd Maste 			r = !!final_pass;  /* force bitmask member to boolean */
658bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
659bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
660bc5531deSDag-Erling Smørgrav 			debug3("%.200s line %d: %smatched '%s'",
661bc5531deSDag-Erling Smørgrav 			    filename, linenum,
662bc5531deSDag-Erling Smørgrav 			    this_result ? "" : "not ", oattrib);
663bc5531deSDag-Erling Smørgrav 			continue;
664bc5531deSDag-Erling Smørgrav 		}
665bc5531deSDag-Erling Smørgrav 		/* All other criteria require an argument */
66619261079SEd Maste 		if ((arg = strdelim(&cp)) == NULL ||
66719261079SEd Maste 		    *arg == '\0' || *arg == '#') {
668f7167e0eSDag-Erling Smørgrav 			error("Missing Match criteria for %s", attrib);
669f7167e0eSDag-Erling Smørgrav 			result = -1;
670f7167e0eSDag-Erling Smørgrav 			goto out;
671f7167e0eSDag-Erling Smørgrav 		}
672f7167e0eSDag-Erling Smørgrav 		if (strcasecmp(attrib, "host") == 0) {
673bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(host);
674557f75e5SDag-Erling Smørgrav 			r = match_hostname(host, arg) == 1;
675bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
676bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
677f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "originalhost") == 0) {
678bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(original_host);
679557f75e5SDag-Erling Smørgrav 			r = match_hostname(original_host, arg) == 1;
680bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
681bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
682f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "user") == 0) {
683bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(ruser);
684557f75e5SDag-Erling Smørgrav 			r = match_pattern_list(ruser, arg, 0) == 1;
685bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
686bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
687f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "localuser") == 0) {
688bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(pw->pw_name);
689557f75e5SDag-Erling Smørgrav 			r = match_pattern_list(pw->pw_name, arg, 0) == 1;
690bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
691bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
692f7167e0eSDag-Erling Smørgrav 		} else if (strcasecmp(attrib, "exec") == 0) {
69319261079SEd Maste 			char *conn_hash_hex, *keyalias;
69419261079SEd Maste 
695f7167e0eSDag-Erling Smørgrav 			if (gethostname(thishost, sizeof(thishost)) == -1)
696f7167e0eSDag-Erling Smørgrav 				fatal("gethostname: %s", strerror(errno));
697f7167e0eSDag-Erling Smørgrav 			strlcpy(shorthost, thishost, sizeof(shorthost));
698f7167e0eSDag-Erling Smørgrav 			shorthost[strcspn(thishost, ".")] = '\0';
699f7167e0eSDag-Erling Smørgrav 			snprintf(portstr, sizeof(portstr), "%d", port);
700190cef3dSDag-Erling Smørgrav 			snprintf(uidstr, sizeof(uidstr), "%llu",
701190cef3dSDag-Erling Smørgrav 			    (unsigned long long)pw->pw_uid);
70219261079SEd Maste 			conn_hash_hex = ssh_connection_hash(thishost, host,
70319261079SEd Maste 			    portstr, ruser);
70419261079SEd Maste 			keyalias = options->host_key_alias ?
70519261079SEd Maste 			    options->host_key_alias : host;
706f7167e0eSDag-Erling Smørgrav 
707f7167e0eSDag-Erling Smørgrav 			cmd = percent_expand(arg,
70819261079SEd Maste 			    "C", conn_hash_hex,
709f7167e0eSDag-Erling Smørgrav 			    "L", shorthost,
710f7167e0eSDag-Erling Smørgrav 			    "d", pw->pw_dir,
711f7167e0eSDag-Erling Smørgrav 			    "h", host,
71219261079SEd Maste 			    "k", keyalias,
713f7167e0eSDag-Erling Smørgrav 			    "l", thishost,
714bc5531deSDag-Erling Smørgrav 			    "n", original_host,
715f7167e0eSDag-Erling Smørgrav 			    "p", portstr,
716f7167e0eSDag-Erling Smørgrav 			    "r", ruser,
717f7167e0eSDag-Erling Smørgrav 			    "u", pw->pw_name,
718190cef3dSDag-Erling Smørgrav 			    "i", uidstr,
719f7167e0eSDag-Erling Smørgrav 			    (char *)NULL);
72019261079SEd Maste 			free(conn_hash_hex);
721b83788ffSDag-Erling Smørgrav 			if (result != 1) {
722b83788ffSDag-Erling Smørgrav 				/* skip execution if prior predicate failed */
723bc5531deSDag-Erling Smørgrav 				debug3("%.200s line %d: skipped exec "
724bc5531deSDag-Erling Smørgrav 				    "\"%.100s\"", filename, linenum, cmd);
725bc5531deSDag-Erling Smørgrav 				free(cmd);
726bc5531deSDag-Erling Smørgrav 				continue;
727bc5531deSDag-Erling Smørgrav 			}
728f7167e0eSDag-Erling Smørgrav 			r = execute_in_shell(cmd);
729f7167e0eSDag-Erling Smørgrav 			if (r == -1) {
730b83788ffSDag-Erling Smørgrav 				fatal("%.200s line %d: match exec "
731b83788ffSDag-Erling Smørgrav 				    "'%.100s' error", filename,
732b83788ffSDag-Erling Smørgrav 				    linenum, cmd);
733b83788ffSDag-Erling Smørgrav 			}
734bc5531deSDag-Erling Smørgrav 			criteria = xstrdup(cmd);
735f7167e0eSDag-Erling Smørgrav 			free(cmd);
736bc5531deSDag-Erling Smørgrav 			/* Force exit status to boolean */
737bc5531deSDag-Erling Smørgrav 			r = r == 0;
738bc5531deSDag-Erling Smørgrav 			if (r == (negate ? 1 : 0))
739bc5531deSDag-Erling Smørgrav 				this_result = result = 0;
740f7167e0eSDag-Erling Smørgrav 		} else {
741f7167e0eSDag-Erling Smørgrav 			error("Unsupported Match attribute %s", attrib);
742f7167e0eSDag-Erling Smørgrav 			result = -1;
743f7167e0eSDag-Erling Smørgrav 			goto out;
744f7167e0eSDag-Erling Smørgrav 		}
745bc5531deSDag-Erling Smørgrav 		debug3("%.200s line %d: %smatched '%s \"%.100s\"' ",
746bc5531deSDag-Erling Smørgrav 		    filename, linenum, this_result ? "": "not ",
747bc5531deSDag-Erling Smørgrav 		    oattrib, criteria);
748bc5531deSDag-Erling Smørgrav 		free(criteria);
749f7167e0eSDag-Erling Smørgrav 	}
750f7167e0eSDag-Erling Smørgrav 	if (attributes == 0) {
751f7167e0eSDag-Erling Smørgrav 		error("One or more attributes required for Match");
752f7167e0eSDag-Erling Smørgrav 		result = -1;
753f7167e0eSDag-Erling Smørgrav 		goto out;
754f7167e0eSDag-Erling Smørgrav 	}
755f7167e0eSDag-Erling Smørgrav  out:
756bc5531deSDag-Erling Smørgrav 	if (result != -1)
757bc5531deSDag-Erling Smørgrav 		debug2("match %sfound", result ? "" : "not ");
758bc5531deSDag-Erling Smørgrav 	*condition = cp;
759f7167e0eSDag-Erling Smørgrav 	free(host);
760f7167e0eSDag-Erling Smørgrav 	return result;
761f7167e0eSDag-Erling Smørgrav }
762f7167e0eSDag-Erling Smørgrav 
763190cef3dSDag-Erling Smørgrav /* Remove environment variable by pattern */
764190cef3dSDag-Erling Smørgrav static void
765190cef3dSDag-Erling Smørgrav rm_env(Options *options, const char *arg, const char *filename, int linenum)
766190cef3dSDag-Erling Smørgrav {
76719261079SEd Maste 	int i, j, onum_send_env = options->num_send_env;
768190cef3dSDag-Erling Smørgrav 	char *cp;
769190cef3dSDag-Erling Smørgrav 
770190cef3dSDag-Erling Smørgrav 	/* Remove an environment variable */
771190cef3dSDag-Erling Smørgrav 	for (i = 0; i < options->num_send_env; ) {
772190cef3dSDag-Erling Smørgrav 		cp = xstrdup(options->send_env[i]);
773190cef3dSDag-Erling Smørgrav 		if (!match_pattern(cp, arg + 1)) {
774190cef3dSDag-Erling Smørgrav 			free(cp);
775190cef3dSDag-Erling Smørgrav 			i++;
776190cef3dSDag-Erling Smørgrav 			continue;
777190cef3dSDag-Erling Smørgrav 		}
778190cef3dSDag-Erling Smørgrav 		debug3("%s line %d: removing environment %s",
779190cef3dSDag-Erling Smørgrav 		    filename, linenum, cp);
780190cef3dSDag-Erling Smørgrav 		free(cp);
781190cef3dSDag-Erling Smørgrav 		free(options->send_env[i]);
782190cef3dSDag-Erling Smørgrav 		options->send_env[i] = NULL;
783190cef3dSDag-Erling Smørgrav 		for (j = i; j < options->num_send_env - 1; j++) {
784190cef3dSDag-Erling Smørgrav 			options->send_env[j] = options->send_env[j + 1];
785190cef3dSDag-Erling Smørgrav 			options->send_env[j + 1] = NULL;
786190cef3dSDag-Erling Smørgrav 		}
787190cef3dSDag-Erling Smørgrav 		options->num_send_env--;
788190cef3dSDag-Erling Smørgrav 		/* NB. don't increment i */
789190cef3dSDag-Erling Smørgrav 	}
79019261079SEd Maste 	if (onum_send_env != options->num_send_env) {
79119261079SEd Maste 		options->send_env = xrecallocarray(options->send_env,
79219261079SEd Maste 		    onum_send_env, options->num_send_env,
79319261079SEd Maste 		    sizeof(*options->send_env));
79419261079SEd Maste 	}
795190cef3dSDag-Erling Smørgrav }
796190cef3dSDag-Erling Smørgrav 
797511b41d2SMark Murray /*
798ca3176e7SBrian Feldman  * Returns the number of the token pointed to by cp or oBadOption.
799511b41d2SMark Murray  */
800511b41d2SMark Murray static OpCodes
801e4a9863fSDag-Erling Smørgrav parse_token(const char *cp, const char *filename, int linenum,
802e4a9863fSDag-Erling Smørgrav     const char *ignored_unknown)
803511b41d2SMark Murray {
804e4a9863fSDag-Erling Smørgrav 	int i;
805511b41d2SMark Murray 
806511b41d2SMark Murray 	for (i = 0; keywords[i].name; i++)
807e4a9863fSDag-Erling Smørgrav 		if (strcmp(cp, keywords[i].name) == 0)
808511b41d2SMark Murray 			return keywords[i].opcode;
809557f75e5SDag-Erling Smørgrav 	if (ignored_unknown != NULL &&
810557f75e5SDag-Erling Smørgrav 	    match_pattern_list(cp, ignored_unknown, 1) == 1)
811e4a9863fSDag-Erling Smørgrav 		return oIgnoredUnknownOption;
812ca3176e7SBrian Feldman 	error("%s: line %d: Bad configuration option: %s",
813511b41d2SMark Murray 	    filename, linenum, cp);
814511b41d2SMark Murray 	return oBadOption;
815511b41d2SMark Murray }
816511b41d2SMark Murray 
817f7167e0eSDag-Erling Smørgrav /* Multistate option parsing */
818f7167e0eSDag-Erling Smørgrav struct multistate {
819f7167e0eSDag-Erling Smørgrav 	char *key;
820f7167e0eSDag-Erling Smørgrav 	int value;
821f7167e0eSDag-Erling Smørgrav };
822f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_flag[] = {
823f7167e0eSDag-Erling Smørgrav 	{ "true",			1 },
824f7167e0eSDag-Erling Smørgrav 	{ "false",			0 },
825f7167e0eSDag-Erling Smørgrav 	{ "yes",			1 },
826f7167e0eSDag-Erling Smørgrav 	{ "no",				0 },
827f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
828f7167e0eSDag-Erling Smørgrav };
829f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_yesnoask[] = {
830f7167e0eSDag-Erling Smørgrav 	{ "true",			1 },
831f7167e0eSDag-Erling Smørgrav 	{ "false",			0 },
832f7167e0eSDag-Erling Smørgrav 	{ "yes",			1 },
833f7167e0eSDag-Erling Smørgrav 	{ "no",				0 },
834f7167e0eSDag-Erling Smørgrav 	{ "ask",			2 },
835f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
836f7167e0eSDag-Erling Smørgrav };
8374f52dfbbSDag-Erling Smørgrav static const struct multistate multistate_strict_hostkey[] = {
8384f52dfbbSDag-Erling Smørgrav 	{ "true",			SSH_STRICT_HOSTKEY_YES },
8394f52dfbbSDag-Erling Smørgrav 	{ "false",			SSH_STRICT_HOSTKEY_OFF },
8404f52dfbbSDag-Erling Smørgrav 	{ "yes",			SSH_STRICT_HOSTKEY_YES },
8414f52dfbbSDag-Erling Smørgrav 	{ "no",				SSH_STRICT_HOSTKEY_OFF },
8424f52dfbbSDag-Erling Smørgrav 	{ "ask",			SSH_STRICT_HOSTKEY_ASK },
8434f52dfbbSDag-Erling Smørgrav 	{ "off",			SSH_STRICT_HOSTKEY_OFF },
8444f52dfbbSDag-Erling Smørgrav 	{ "accept-new",			SSH_STRICT_HOSTKEY_NEW },
8454f52dfbbSDag-Erling Smørgrav 	{ NULL, -1 }
8464f52dfbbSDag-Erling Smørgrav };
847acc1a9efSDag-Erling Smørgrav static const struct multistate multistate_yesnoaskconfirm[] = {
848acc1a9efSDag-Erling Smørgrav 	{ "true",			1 },
849acc1a9efSDag-Erling Smørgrav 	{ "false",			0 },
850acc1a9efSDag-Erling Smørgrav 	{ "yes",			1 },
851acc1a9efSDag-Erling Smørgrav 	{ "no",				0 },
852acc1a9efSDag-Erling Smørgrav 	{ "ask",			2 },
853acc1a9efSDag-Erling Smørgrav 	{ "confirm",			3 },
854acc1a9efSDag-Erling Smørgrav 	{ NULL, -1 }
855acc1a9efSDag-Erling Smørgrav };
856f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_addressfamily[] = {
857f7167e0eSDag-Erling Smørgrav 	{ "inet",			AF_INET },
858f7167e0eSDag-Erling Smørgrav 	{ "inet6",			AF_INET6 },
859f7167e0eSDag-Erling Smørgrav 	{ "any",			AF_UNSPEC },
860f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
861f7167e0eSDag-Erling Smørgrav };
862f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_controlmaster[] = {
863f7167e0eSDag-Erling Smørgrav 	{ "true",			SSHCTL_MASTER_YES },
864f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSHCTL_MASTER_YES },
865f7167e0eSDag-Erling Smørgrav 	{ "false",			SSHCTL_MASTER_NO },
866f7167e0eSDag-Erling Smørgrav 	{ "no",				SSHCTL_MASTER_NO },
867f7167e0eSDag-Erling Smørgrav 	{ "auto",			SSHCTL_MASTER_AUTO },
868f7167e0eSDag-Erling Smørgrav 	{ "ask",			SSHCTL_MASTER_ASK },
869f7167e0eSDag-Erling Smørgrav 	{ "autoask",			SSHCTL_MASTER_AUTO_ASK },
870f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
871f7167e0eSDag-Erling Smørgrav };
872f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_tunnel[] = {
873f7167e0eSDag-Erling Smørgrav 	{ "ethernet",			SSH_TUNMODE_ETHERNET },
874f7167e0eSDag-Erling Smørgrav 	{ "point-to-point",		SSH_TUNMODE_POINTOPOINT },
875f7167e0eSDag-Erling Smørgrav 	{ "true",			SSH_TUNMODE_DEFAULT },
876f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSH_TUNMODE_DEFAULT },
877f7167e0eSDag-Erling Smørgrav 	{ "false",			SSH_TUNMODE_NO },
878f7167e0eSDag-Erling Smørgrav 	{ "no",				SSH_TUNMODE_NO },
879f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
880f7167e0eSDag-Erling Smørgrav };
881f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_requesttty[] = {
882f7167e0eSDag-Erling Smørgrav 	{ "true",			REQUEST_TTY_YES },
883f7167e0eSDag-Erling Smørgrav 	{ "yes",			REQUEST_TTY_YES },
884f7167e0eSDag-Erling Smørgrav 	{ "false",			REQUEST_TTY_NO },
885f7167e0eSDag-Erling Smørgrav 	{ "no",				REQUEST_TTY_NO },
886f7167e0eSDag-Erling Smørgrav 	{ "force",			REQUEST_TTY_FORCE },
887f7167e0eSDag-Erling Smørgrav 	{ "auto",			REQUEST_TTY_AUTO },
888f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
889f7167e0eSDag-Erling Smørgrav };
89019261079SEd Maste static const struct multistate multistate_sessiontype[] = {
89119261079SEd Maste 	{ "none",			SESSION_TYPE_NONE },
89219261079SEd Maste 	{ "subsystem",			SESSION_TYPE_SUBSYSTEM },
89319261079SEd Maste 	{ "default",			SESSION_TYPE_DEFAULT },
89419261079SEd Maste 	{ NULL, -1 }
89519261079SEd Maste };
896f7167e0eSDag-Erling Smørgrav static const struct multistate multistate_canonicalizehostname[] = {
897f7167e0eSDag-Erling Smørgrav 	{ "true",			SSH_CANONICALISE_YES },
898f7167e0eSDag-Erling Smørgrav 	{ "false",			SSH_CANONICALISE_NO },
899f7167e0eSDag-Erling Smørgrav 	{ "yes",			SSH_CANONICALISE_YES },
900f7167e0eSDag-Erling Smørgrav 	{ "no",				SSH_CANONICALISE_NO },
901f7167e0eSDag-Erling Smørgrav 	{ "always",			SSH_CANONICALISE_ALWAYS },
902f7167e0eSDag-Erling Smørgrav 	{ NULL, -1 }
903f7167e0eSDag-Erling Smørgrav };
904*1323ec57SEd Maste static const struct multistate multistate_pubkey_auth[] = {
905*1323ec57SEd Maste 	{ "true",			SSH_PUBKEY_AUTH_ALL },
906*1323ec57SEd Maste 	{ "false",			SSH_PUBKEY_AUTH_NO },
907*1323ec57SEd Maste 	{ "yes",			SSH_PUBKEY_AUTH_ALL },
908*1323ec57SEd Maste 	{ "no",				SSH_PUBKEY_AUTH_NO },
909*1323ec57SEd Maste 	{ "unbound",			SSH_PUBKEY_AUTH_UNBOUND },
910*1323ec57SEd Maste 	{ "host-bound",			SSH_PUBKEY_AUTH_HBOUND },
911*1323ec57SEd Maste 	{ NULL, -1 }
912*1323ec57SEd Maste };
91319261079SEd Maste static const struct multistate multistate_compression[] = {
91419261079SEd Maste #ifdef WITH_ZLIB
91519261079SEd Maste 	{ "yes",			COMP_ZLIB },
91619261079SEd Maste #endif
91719261079SEd Maste 	{ "no",				COMP_NONE },
91819261079SEd Maste 	{ NULL, -1 }
91919261079SEd Maste };
92019261079SEd Maste 
92119261079SEd Maste static int
92219261079SEd Maste parse_multistate_value(const char *arg, const char *filename, int linenum,
92319261079SEd Maste     const struct multistate *multistate_ptr)
92419261079SEd Maste {
92519261079SEd Maste 	int i;
92619261079SEd Maste 
92719261079SEd Maste 	if (!arg || *arg == '\0') {
92819261079SEd Maste 		error("%s line %d: missing argument.", filename, linenum);
92919261079SEd Maste 		return -1;
93019261079SEd Maste 	}
93119261079SEd Maste 	for (i = 0; multistate_ptr[i].key != NULL; i++) {
93219261079SEd Maste 		if (strcasecmp(arg, multistate_ptr[i].key) == 0)
93319261079SEd Maste 			return multistate_ptr[i].value;
93419261079SEd Maste 	}
93519261079SEd Maste 	return -1;
93619261079SEd Maste }
937f7167e0eSDag-Erling Smørgrav 
938511b41d2SMark Murray /*
939511b41d2SMark Murray  * Processes a single option line as used in the configuration files. This
940511b41d2SMark Murray  * only sets those values that have not already been set.
941511b41d2SMark Murray  */
942511b41d2SMark Murray int
943f7167e0eSDag-Erling Smørgrav process_config_line(Options *options, struct passwd *pw, const char *host,
944bc5531deSDag-Erling Smørgrav     const char *original_host, char *line, const char *filename,
945bc5531deSDag-Erling Smørgrav     int linenum, int *activep, int flags)
946511b41d2SMark Murray {
947076ad2f8SDag-Erling Smørgrav 	return process_config_line_depth(options, pw, host, original_host,
94819261079SEd Maste 	    line, filename, linenum, activep, flags, NULL, 0);
949076ad2f8SDag-Erling Smørgrav }
950076ad2f8SDag-Erling Smørgrav 
951076ad2f8SDag-Erling Smørgrav #define WHITESPACE " \t\r\n"
952076ad2f8SDag-Erling Smørgrav static int
953076ad2f8SDag-Erling Smørgrav process_config_line_depth(Options *options, struct passwd *pw, const char *host,
954076ad2f8SDag-Erling Smørgrav     const char *original_host, char *line, const char *filename,
95519261079SEd Maste     int linenum, int *activep, int flags, int *want_final_pass, int depth)
956076ad2f8SDag-Erling Smørgrav {
957*1323ec57SEd Maste 	char *str, **charptr, *endofnumber, *keyword, *arg, *arg2, *p;
95819261079SEd Maste 	char **cpptr, ***cppptr, fwdarg[256];
95919261079SEd Maste 	u_int i, *uintptr, uvalue, max_entries = 0;
960076ad2f8SDag-Erling Smørgrav 	int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0;
9614f52dfbbSDag-Erling Smørgrav 	int remotefwd, dynamicfwd;
962d4af9e69SDag-Erling Smørgrav 	LogLevel *log_level_ptr;
9634f52dfbbSDag-Erling Smørgrav 	SyslogFacility *log_facility_ptr;
964e4a9863fSDag-Erling Smørgrav 	long long val64;
965e73e9afaSDag-Erling Smørgrav 	size_t len;
966a0ee8cc6SDag-Erling Smørgrav 	struct Forward fwd;
967f7167e0eSDag-Erling Smørgrav 	const struct multistate *multistate_ptr;
968f7167e0eSDag-Erling Smørgrav 	struct allowed_cname *cname;
969076ad2f8SDag-Erling Smørgrav 	glob_t gl;
97047dd1d1bSDag-Erling Smørgrav 	const char *errstr;
97119261079SEd Maste 	char **oav = NULL, **av;
97219261079SEd Maste 	int oac = 0, ac;
97319261079SEd Maste 	int ret = -1;
974f7167e0eSDag-Erling Smørgrav 
975f7167e0eSDag-Erling Smørgrav 	if (activep == NULL) { /* We are processing a command line directive */
976f7167e0eSDag-Erling Smørgrav 		cmdline = 1;
977f7167e0eSDag-Erling Smørgrav 		activep = &cmdline;
978f7167e0eSDag-Erling Smørgrav 	}
979511b41d2SMark Murray 
980d93a896eSDag-Erling Smørgrav 	/* Strip trailing whitespace. Allow \f (form feed) at EOL only */
981557f75e5SDag-Erling Smørgrav 	if ((len = strlen(line)) == 0)
982557f75e5SDag-Erling Smørgrav 		return 0;
983557f75e5SDag-Erling Smørgrav 	for (len--; len > 0; len--) {
984d93a896eSDag-Erling Smørgrav 		if (strchr(WHITESPACE "\f", line[len]) == NULL)
985cf2b5f3bSDag-Erling Smørgrav 			break;
986cf2b5f3bSDag-Erling Smørgrav 		line[len] = '\0';
987cf2b5f3bSDag-Erling Smørgrav 	}
988cf2b5f3bSDag-Erling Smørgrav 
98919261079SEd Maste 	str = line;
990c2d3a559SKris Kennaway 	/* Get the keyword. (Each line is supposed to begin with a keyword). */
99119261079SEd Maste 	if ((keyword = strdelim(&str)) == NULL)
992333ee039SDag-Erling Smørgrav 		return 0;
993c2d3a559SKris Kennaway 	/* Ignore leading whitespace. */
994c2d3a559SKris Kennaway 	if (*keyword == '\0')
99519261079SEd Maste 		keyword = strdelim(&str);
996ca3176e7SBrian Feldman 	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
997511b41d2SMark Murray 		return 0;
998e4a9863fSDag-Erling Smørgrav 	/* Match lowercase keyword */
999f7167e0eSDag-Erling Smørgrav 	lowercase(keyword);
1000511b41d2SMark Murray 
100119261079SEd Maste 	/* Prepare to parse remainder of line */
100219261079SEd Maste 	if (str != NULL)
100319261079SEd Maste 		str += strspn(str, WHITESPACE);
100419261079SEd Maste 	if (str == NULL || *str == '\0') {
100519261079SEd Maste 		error("%s line %d: no argument after keyword \"%s\"",
100619261079SEd Maste 		    filename, linenum, keyword);
100719261079SEd Maste 		return -1;
100819261079SEd Maste 	}
1009e4a9863fSDag-Erling Smørgrav 	opcode = parse_token(keyword, filename, linenum,
1010e4a9863fSDag-Erling Smørgrav 	    options->ignored_unknown);
101119261079SEd Maste 	if (argv_split(str, &oac, &oav, 1) != 0) {
101219261079SEd Maste 		error("%s line %d: invalid quotes", filename, linenum);
101319261079SEd Maste 		return -1;
101419261079SEd Maste 	}
101519261079SEd Maste 	ac = oac;
101619261079SEd Maste 	av = oav;
1017511b41d2SMark Murray 
1018511b41d2SMark Murray 	switch (opcode) {
1019511b41d2SMark Murray 	case oBadOption:
1020511b41d2SMark Murray 		/* don't panic, but count bad options */
102119261079SEd Maste 		goto out;
10224f52dfbbSDag-Erling Smørgrav 	case oIgnore:
102319261079SEd Maste 		argv_consume(&ac);
102419261079SEd Maste 		break;
1025e4a9863fSDag-Erling Smørgrav 	case oIgnoredUnknownOption:
1026e4a9863fSDag-Erling Smørgrav 		debug("%s line %d: Ignored unknown option \"%s\"",
1027e4a9863fSDag-Erling Smørgrav 		    filename, linenum, keyword);
102819261079SEd Maste 		argv_consume(&ac);
102919261079SEd Maste 		break;
1030cf2b5f3bSDag-Erling Smørgrav 	case oConnectTimeout:
1031cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->connection_timeout;
10321ec0d754SDag-Erling Smørgrav parse_time:
103319261079SEd Maste 		arg = argv_next(&ac, &av);
103419261079SEd Maste 		if (!arg || *arg == '\0') {
103519261079SEd Maste 			error("%s line %d: missing time value.",
1036cf2b5f3bSDag-Erling Smørgrav 			    filename, linenum);
103719261079SEd Maste 			goto out;
103819261079SEd Maste 		}
1039bc5531deSDag-Erling Smørgrav 		if (strcmp(arg, "none") == 0)
1040bc5531deSDag-Erling Smørgrav 			value = -1;
104119261079SEd Maste 		else if ((value = convtime(arg)) == -1) {
104219261079SEd Maste 			error("%s line %d: invalid time value.",
1043cf2b5f3bSDag-Erling Smørgrav 			    filename, linenum);
104419261079SEd Maste 			goto out;
104519261079SEd Maste 		}
1046d4af9e69SDag-Erling Smørgrav 		if (*activep && *intptr == -1)
1047cf2b5f3bSDag-Erling Smørgrav 			*intptr = value;
1048cf2b5f3bSDag-Erling Smørgrav 		break;
1049cf2b5f3bSDag-Erling Smørgrav 
1050511b41d2SMark Murray 	case oForwardAgent:
1051511b41d2SMark Murray 		intptr = &options->forward_agent;
105219261079SEd Maste 
105319261079SEd Maste 		arg = argv_next(&ac, &av);
105419261079SEd Maste 		if (!arg || *arg == '\0') {
105519261079SEd Maste 			error("%s line %d: missing argument.",
1056f7167e0eSDag-Erling Smørgrav 			    filename, linenum);
105719261079SEd Maste 			goto out;
105819261079SEd Maste 		}
105919261079SEd Maste 
1060f7167e0eSDag-Erling Smørgrav 		value = -1;
106119261079SEd Maste 		multistate_ptr = multistate_flag;
1062f7167e0eSDag-Erling Smørgrav 		for (i = 0; multistate_ptr[i].key != NULL; i++) {
1063f7167e0eSDag-Erling Smørgrav 			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
1064f7167e0eSDag-Erling Smørgrav 				value = multistate_ptr[i].value;
1065f7167e0eSDag-Erling Smørgrav 				break;
1066f7167e0eSDag-Erling Smørgrav 			}
1067f7167e0eSDag-Erling Smørgrav 		}
106819261079SEd Maste 		if (value != -1) {
1069511b41d2SMark Murray 			if (*activep && *intptr == -1)
1070511b41d2SMark Murray 				*intptr = value;
1071511b41d2SMark Murray 			break;
107219261079SEd Maste 		}
107319261079SEd Maste 		/* ForwardAgent wasn't 'yes' or 'no', assume a path */
107419261079SEd Maste 		if (*activep && *intptr == -1)
107519261079SEd Maste 			*intptr = 1;
107619261079SEd Maste 
107719261079SEd Maste 		charptr = &options->forward_agent_sock_path;
107819261079SEd Maste 		goto parse_agent_path;
1079511b41d2SMark Murray 
1080511b41d2SMark Murray 	case oForwardX11:
1081511b41d2SMark Murray 		intptr = &options->forward_x11;
108219261079SEd Maste  parse_flag:
108319261079SEd Maste 		multistate_ptr = multistate_flag;
108419261079SEd Maste  parse_multistate:
108519261079SEd Maste 		arg = argv_next(&ac, &av);
108619261079SEd Maste 		if ((value = parse_multistate_value(arg, filename, linenum,
108719261079SEd Maste 		    multistate_ptr)) == -1) {
108819261079SEd Maste 			error("%s line %d: unsupported option \"%s\".",
108919261079SEd Maste 			    filename, linenum, arg);
109019261079SEd Maste 			goto out;
109119261079SEd Maste 		}
109219261079SEd Maste 		if (*activep && *intptr == -1)
109319261079SEd Maste 			*intptr = value;
109419261079SEd Maste 		break;
1095511b41d2SMark Murray 
10961ec0d754SDag-Erling Smørgrav 	case oForwardX11Trusted:
10971ec0d754SDag-Erling Smørgrav 		intptr = &options->forward_x11_trusted;
10981ec0d754SDag-Erling Smørgrav 		goto parse_flag;
10991ec0d754SDag-Erling Smørgrav 
1100e2f6069cSDag-Erling Smørgrav 	case oForwardX11Timeout:
1101e2f6069cSDag-Erling Smørgrav 		intptr = &options->forward_x11_timeout;
1102e2f6069cSDag-Erling Smørgrav 		goto parse_time;
1103e2f6069cSDag-Erling Smørgrav 
1104511b41d2SMark Murray 	case oGatewayPorts:
1105a0ee8cc6SDag-Erling Smørgrav 		intptr = &options->fwd_opts.gateway_ports;
1106511b41d2SMark Murray 		goto parse_flag;
1107511b41d2SMark Murray 
1108333ee039SDag-Erling Smørgrav 	case oExitOnForwardFailure:
1109333ee039SDag-Erling Smørgrav 		intptr = &options->exit_on_forward_failure;
1110333ee039SDag-Erling Smørgrav 		goto parse_flag;
1111333ee039SDag-Erling Smørgrav 
1112511b41d2SMark Murray 	case oPasswordAuthentication:
1113511b41d2SMark Murray 		intptr = &options->password_authentication;
1114511b41d2SMark Murray 		goto parse_flag;
1115511b41d2SMark Murray 
111609958426SBrian Feldman 	case oKbdInteractiveAuthentication:
111709958426SBrian Feldman 		intptr = &options->kbd_interactive_authentication;
111809958426SBrian Feldman 		goto parse_flag;
111909958426SBrian Feldman 
112009958426SBrian Feldman 	case oKbdInteractiveDevices:
112109958426SBrian Feldman 		charptr = &options->kbd_interactive_devices;
112209958426SBrian Feldman 		goto parse_string;
112309958426SBrian Feldman 
1124ca3176e7SBrian Feldman 	case oPubkeyAuthentication:
1125*1323ec57SEd Maste 		multistate_ptr = multistate_pubkey_auth;
1126ca3176e7SBrian Feldman 		intptr = &options->pubkey_authentication;
1127*1323ec57SEd Maste 		goto parse_multistate;
1128e8aafc91SKris Kennaway 
1129ca3176e7SBrian Feldman 	case oHostbasedAuthentication:
1130ca3176e7SBrian Feldman 		intptr = &options->hostbased_authentication;
1131511b41d2SMark Murray 		goto parse_flag;
1132511b41d2SMark Murray 
1133cf2b5f3bSDag-Erling Smørgrav 	case oGssAuthentication:
1134cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->gss_authentication;
1135511b41d2SMark Murray 		goto parse_flag;
1136cf2b5f3bSDag-Erling Smørgrav 
1137cf2b5f3bSDag-Erling Smørgrav 	case oGssDelegateCreds:
1138cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->gss_deleg_creds;
1139ca3176e7SBrian Feldman 		goto parse_flag;
1140cf2b5f3bSDag-Erling Smørgrav 
1141511b41d2SMark Murray 	case oBatchMode:
1142511b41d2SMark Murray 		intptr = &options->batch_mode;
1143511b41d2SMark Murray 		goto parse_flag;
1144511b41d2SMark Murray 
1145511b41d2SMark Murray 	case oCheckHostIP:
1146511b41d2SMark Murray 		intptr = &options->check_host_ip;
1147511b41d2SMark Murray 		goto parse_flag;
1148511b41d2SMark Murray 
1149cf2b5f3bSDag-Erling Smørgrav 	case oVerifyHostKeyDNS:
1150cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->verify_host_key_dns;
1151f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_yesnoask;
1152f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1153cf2b5f3bSDag-Erling Smørgrav 
1154511b41d2SMark Murray 	case oStrictHostKeyChecking:
1155511b41d2SMark Murray 		intptr = &options->strict_host_key_checking;
11564f52dfbbSDag-Erling Smørgrav 		multistate_ptr = multistate_strict_hostkey;
1157f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1158511b41d2SMark Murray 
1159511b41d2SMark Murray 	case oCompression:
1160511b41d2SMark Murray 		intptr = &options->compression;
116119261079SEd Maste 		multistate_ptr = multistate_compression;
116219261079SEd Maste 		goto parse_multistate;
1163511b41d2SMark Murray 
11641ec0d754SDag-Erling Smørgrav 	case oTCPKeepAlive:
11651ec0d754SDag-Erling Smørgrav 		intptr = &options->tcp_keep_alive;
1166511b41d2SMark Murray 		goto parse_flag;
1167511b41d2SMark Murray 
1168af12a3e7SDag-Erling Smørgrav 	case oNoHostAuthenticationForLocalhost:
1169af12a3e7SDag-Erling Smørgrav 		intptr = &options->no_host_authentication_for_localhost;
1170af12a3e7SDag-Erling Smørgrav 		goto parse_flag;
1171af12a3e7SDag-Erling Smørgrav 
1172511b41d2SMark Murray 	case oNumberOfPasswordPrompts:
1173511b41d2SMark Murray 		intptr = &options->number_of_password_prompts;
1174511b41d2SMark Murray 		goto parse_int;
1175511b41d2SMark Murray 
1176cf2b5f3bSDag-Erling Smørgrav 	case oRekeyLimit:
117719261079SEd Maste 		arg = argv_next(&ac, &av);
117819261079SEd Maste 		if (!arg || *arg == '\0') {
117919261079SEd Maste 			error("%.200s line %d: Missing argument.", filename,
1180e4a9863fSDag-Erling Smørgrav 			    linenum);
118119261079SEd Maste 			goto out;
118219261079SEd Maste 		}
1183e4a9863fSDag-Erling Smørgrav 		if (strcmp(arg, "default") == 0) {
1184e4a9863fSDag-Erling Smørgrav 			val64 = 0;
1185e4a9863fSDag-Erling Smørgrav 		} else {
118619261079SEd Maste 			if (scan_scaled(arg, &val64) == -1) {
118719261079SEd Maste 				error("%.200s line %d: Bad number '%s': %s",
1188e4a9863fSDag-Erling Smørgrav 				    filename, linenum, arg, strerror(errno));
118919261079SEd Maste 				goto out;
119019261079SEd Maste 			}
119119261079SEd Maste 			if (val64 != 0 && val64 < 16) {
119219261079SEd Maste 				error("%.200s line %d: RekeyLimit too small",
1193333ee039SDag-Erling Smørgrav 				    filename, linenum);
119419261079SEd Maste 				goto out;
119519261079SEd Maste 			}
1196e4a9863fSDag-Erling Smørgrav 		}
1197d4af9e69SDag-Erling Smørgrav 		if (*activep && options->rekey_limit == -1)
1198acc1a9efSDag-Erling Smørgrav 			options->rekey_limit = val64;
119919261079SEd Maste 		if (ac != 0) { /* optional rekey interval present */
120019261079SEd Maste 			if (strcmp(av[0], "none") == 0) {
120119261079SEd Maste 				(void)argv_next(&ac, &av);	/* discard */
1202e4a9863fSDag-Erling Smørgrav 				break;
1203e4a9863fSDag-Erling Smørgrav 			}
1204e4a9863fSDag-Erling Smørgrav 			intptr = &options->rekey_interval;
1205e4a9863fSDag-Erling Smørgrav 			goto parse_time;
1206e4a9863fSDag-Erling Smørgrav 		}
1207cf2b5f3bSDag-Erling Smørgrav 		break;
1208cf2b5f3bSDag-Erling Smørgrav 
1209511b41d2SMark Murray 	case oIdentityFile:
121019261079SEd Maste 		arg = argv_next(&ac, &av);
121119261079SEd Maste 		if (!arg || *arg == '\0') {
121219261079SEd Maste 			error("%.200s line %d: Missing argument.",
121319261079SEd Maste 			    filename, linenum);
121419261079SEd Maste 			goto out;
121519261079SEd Maste 		}
1216511b41d2SMark Murray 		if (*activep) {
1217ca3176e7SBrian Feldman 			intptr = &options->num_identity_files;
121819261079SEd Maste 			if (*intptr >= SSH_MAX_IDENTITY_FILES) {
121919261079SEd Maste 				error("%.200s line %d: Too many identity files "
122019261079SEd Maste 				    "specified (max %d).", filename, linenum,
122119261079SEd Maste 				    SSH_MAX_IDENTITY_FILES);
122219261079SEd Maste 				goto out;
122319261079SEd Maste 			}
1224bc5531deSDag-Erling Smørgrav 			add_identity_file(options, NULL,
1225bc5531deSDag-Erling Smørgrav 			    arg, flags & SSHCONF_USERCONF);
1226511b41d2SMark Murray 		}
1227511b41d2SMark Murray 		break;
1228511b41d2SMark Murray 
1229acc1a9efSDag-Erling Smørgrav 	case oCertificateFile:
123019261079SEd Maste 		arg = argv_next(&ac, &av);
123119261079SEd Maste 		if (!arg || *arg == '\0') {
123219261079SEd Maste 			error("%.200s line %d: Missing argument.",
1233acc1a9efSDag-Erling Smørgrav 			    filename, linenum);
123419261079SEd Maste 			goto out;
123519261079SEd Maste 		}
1236acc1a9efSDag-Erling Smørgrav 		if (*activep) {
1237acc1a9efSDag-Erling Smørgrav 			intptr = &options->num_certificate_files;
1238acc1a9efSDag-Erling Smørgrav 			if (*intptr >= SSH_MAX_CERTIFICATE_FILES) {
123919261079SEd Maste 				error("%.200s line %d: Too many certificate "
1240acc1a9efSDag-Erling Smørgrav 				    "files specified (max %d).",
1241acc1a9efSDag-Erling Smørgrav 				    filename, linenum,
1242acc1a9efSDag-Erling Smørgrav 				    SSH_MAX_CERTIFICATE_FILES);
124319261079SEd Maste 				goto out;
1244acc1a9efSDag-Erling Smørgrav 			}
1245acc1a9efSDag-Erling Smørgrav 			add_certificate_file(options, arg,
1246acc1a9efSDag-Erling Smørgrav 			    flags & SSHCONF_USERCONF);
1247acc1a9efSDag-Erling Smørgrav 		}
1248acc1a9efSDag-Erling Smørgrav 		break;
1249acc1a9efSDag-Erling Smørgrav 
1250c2d3a559SKris Kennaway 	case oXAuthLocation:
1251c2d3a559SKris Kennaway 		charptr=&options->xauth_location;
1252c2d3a559SKris Kennaway 		goto parse_string;
1253c2d3a559SKris Kennaway 
1254511b41d2SMark Murray 	case oUser:
1255511b41d2SMark Murray 		charptr = &options->user;
1256511b41d2SMark Murray parse_string:
125719261079SEd Maste 		arg = argv_next(&ac, &av);
125819261079SEd Maste 		if (!arg || *arg == '\0') {
125919261079SEd Maste 			error("%.200s line %d: Missing argument.",
1260e146993eSDag-Erling Smørgrav 			    filename, linenum);
126119261079SEd Maste 			goto out;
126219261079SEd Maste 		}
1263511b41d2SMark Murray 		if (*activep && *charptr == NULL)
1264c2d3a559SKris Kennaway 			*charptr = xstrdup(arg);
1265511b41d2SMark Murray 		break;
1266511b41d2SMark Murray 
1267511b41d2SMark Murray 	case oGlobalKnownHostsFile:
1268e146993eSDag-Erling Smørgrav 		cpptr = (char **)&options->system_hostfiles;
1269e146993eSDag-Erling Smørgrav 		uintptr = &options->num_system_hostfiles;
1270e146993eSDag-Erling Smørgrav 		max_entries = SSH_MAX_HOSTS_FILES;
1271e146993eSDag-Erling Smørgrav parse_char_array:
127219261079SEd Maste 		i = 0;
127319261079SEd Maste 		value = *uintptr == 0; /* was array empty when we started? */
127419261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
127519261079SEd Maste 			if (*arg == '\0') {
127619261079SEd Maste 				error("%s line %d: keyword %s empty argument",
127719261079SEd Maste 				    filename, linenum, keyword);
127819261079SEd Maste 				goto out;
127919261079SEd Maste 			}
128019261079SEd Maste 			/* Allow "none" only in first position */
128119261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
128219261079SEd Maste 				if (i > 0 || ac > 0) {
128319261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
128419261079SEd Maste 					    "argument must appear alone.",
128519261079SEd Maste 					    filename, linenum, keyword);
128619261079SEd Maste 					goto out;
128719261079SEd Maste 				}
128819261079SEd Maste 			}
128919261079SEd Maste 			i++;
129019261079SEd Maste 			if (*activep && value) {
129119261079SEd Maste 				if ((*uintptr) >= max_entries) {
129219261079SEd Maste 					error("%s line %d: too many %s "
129319261079SEd Maste 					    "entries.", filename, linenum,
129419261079SEd Maste 					    keyword);
129519261079SEd Maste 					goto out;
129619261079SEd Maste 				}
1297e146993eSDag-Erling Smørgrav 				cpptr[(*uintptr)++] = xstrdup(arg);
1298e146993eSDag-Erling Smørgrav 			}
1299e146993eSDag-Erling Smørgrav 		}
130019261079SEd Maste 		break;
1301511b41d2SMark Murray 
1302511b41d2SMark Murray 	case oUserKnownHostsFile:
1303e146993eSDag-Erling Smørgrav 		cpptr = (char **)&options->user_hostfiles;
1304e146993eSDag-Erling Smørgrav 		uintptr = &options->num_user_hostfiles;
1305e146993eSDag-Erling Smørgrav 		max_entries = SSH_MAX_HOSTS_FILES;
1306e146993eSDag-Erling Smørgrav 		goto parse_char_array;
1307e8aafc91SKris Kennaway 
130819261079SEd Maste 	case oHostname:
1309511b41d2SMark Murray 		charptr = &options->hostname;
1310511b41d2SMark Murray 		goto parse_string;
1311511b41d2SMark Murray 
1312ca3176e7SBrian Feldman 	case oHostKeyAlias:
1313ca3176e7SBrian Feldman 		charptr = &options->host_key_alias;
1314ca3176e7SBrian Feldman 		goto parse_string;
1315ca3176e7SBrian Feldman 
1316ca3176e7SBrian Feldman 	case oPreferredAuthentications:
1317ca3176e7SBrian Feldman 		charptr = &options->preferred_authentications;
1318ca3176e7SBrian Feldman 		goto parse_string;
1319ca3176e7SBrian Feldman 
1320af12a3e7SDag-Erling Smørgrav 	case oBindAddress:
1321af12a3e7SDag-Erling Smørgrav 		charptr = &options->bind_address;
1322af12a3e7SDag-Erling Smørgrav 		goto parse_string;
1323af12a3e7SDag-Erling Smørgrav 
132447dd1d1bSDag-Erling Smørgrav 	case oBindInterface:
132547dd1d1bSDag-Erling Smørgrav 		charptr = &options->bind_interface;
132647dd1d1bSDag-Erling Smørgrav 		goto parse_string;
132747dd1d1bSDag-Erling Smørgrav 
1328b15c8340SDag-Erling Smørgrav 	case oPKCS11Provider:
1329b15c8340SDag-Erling Smørgrav 		charptr = &options->pkcs11_provider;
1330af12a3e7SDag-Erling Smørgrav 		goto parse_string;
1331af12a3e7SDag-Erling Smørgrav 
133219261079SEd Maste 	case oSecurityKeyProvider:
133319261079SEd Maste 		charptr = &options->sk_provider;
133419261079SEd Maste 		goto parse_string;
133519261079SEd Maste 
133619261079SEd Maste 	case oKnownHostsCommand:
133719261079SEd Maste 		charptr = &options->known_hosts_command;
133819261079SEd Maste 		goto parse_command;
133919261079SEd Maste 
1340511b41d2SMark Murray 	case oProxyCommand:
1341b74df5b2SDag-Erling Smørgrav 		charptr = &options->proxy_command;
1342076ad2f8SDag-Erling Smørgrav 		/* Ignore ProxyCommand if ProxyJump already specified */
1343076ad2f8SDag-Erling Smørgrav 		if (options->jump_host != NULL)
1344076ad2f8SDag-Erling Smørgrav 			charptr = &options->jump_host; /* Skip below */
1345b74df5b2SDag-Erling Smørgrav parse_command:
134619261079SEd Maste 		if (str == NULL) {
134719261079SEd Maste 			error("%.200s line %d: Missing argument.",
134819261079SEd Maste 			    filename, linenum);
134919261079SEd Maste 			goto out;
135019261079SEd Maste 		}
135119261079SEd Maste 		len = strspn(str, WHITESPACE "=");
1352511b41d2SMark Murray 		if (*activep && *charptr == NULL)
135319261079SEd Maste 			*charptr = xstrdup(str + len);
135419261079SEd Maste 		argv_consume(&ac);
135519261079SEd Maste 		break;
1356511b41d2SMark Murray 
1357076ad2f8SDag-Erling Smørgrav 	case oProxyJump:
135819261079SEd Maste 		if (str == NULL) {
135919261079SEd Maste 			error("%.200s line %d: Missing argument.",
1360076ad2f8SDag-Erling Smørgrav 			    filename, linenum);
136119261079SEd Maste 			goto out;
1362076ad2f8SDag-Erling Smørgrav 		}
136319261079SEd Maste 		len = strspn(str, WHITESPACE "=");
136419261079SEd Maste 		/* XXX use argv? */
136519261079SEd Maste 		if (parse_jump(str + len, options, *activep) == -1) {
136619261079SEd Maste 			error("%.200s line %d: Invalid ProxyJump \"%s\"",
136719261079SEd Maste 			    filename, linenum, str + len);
136819261079SEd Maste 			goto out;
1369076ad2f8SDag-Erling Smørgrav 		}
137019261079SEd Maste 		argv_consume(&ac);
137119261079SEd Maste 		break;
1372076ad2f8SDag-Erling Smørgrav 
1373511b41d2SMark Murray 	case oPort:
137419261079SEd Maste 		arg = argv_next(&ac, &av);
137519261079SEd Maste 		if (!arg || *arg == '\0') {
137619261079SEd Maste 			error("%.200s line %d: Missing argument.",
13772f513db7SEd Maste 			    filename, linenum);
137819261079SEd Maste 			goto out;
137919261079SEd Maste 		}
13802f513db7SEd Maste 		value = a2port(arg);
138119261079SEd Maste 		if (value <= 0) {
138219261079SEd Maste 			error("%.200s line %d: Bad port '%s'.",
13832f513db7SEd Maste 			    filename, linenum, arg);
138419261079SEd Maste 			goto out;
138519261079SEd Maste 		}
13862f513db7SEd Maste 		if (*activep && options->port == -1)
13872f513db7SEd Maste 			options->port = value;
13882f513db7SEd Maste 		break;
13892f513db7SEd Maste 
13902f513db7SEd Maste 	case oConnectionAttempts:
13912f513db7SEd Maste 		intptr = &options->connection_attempts;
1392511b41d2SMark Murray parse_int:
139319261079SEd Maste 		arg = argv_next(&ac, &av);
139419261079SEd Maste 		if ((errstr = atoi_err(arg, &value)) != NULL) {
139519261079SEd Maste 			error("%s line %d: integer value %s.",
139647dd1d1bSDag-Erling Smørgrav 			    filename, linenum, errstr);
139719261079SEd Maste 			goto out;
139819261079SEd Maste 		}
1399511b41d2SMark Murray 		if (*activep && *intptr == -1)
1400511b41d2SMark Murray 			*intptr = value;
1401511b41d2SMark Murray 		break;
1402511b41d2SMark Murray 
1403e8aafc91SKris Kennaway 	case oCiphers:
140419261079SEd Maste 		arg = argv_next(&ac, &av);
140519261079SEd Maste 		if (!arg || *arg == '\0') {
140619261079SEd Maste 			error("%.200s line %d: Missing argument.",
140719261079SEd Maste 			    filename, linenum);
140819261079SEd Maste 			goto out;
140919261079SEd Maste 		}
141019261079SEd Maste 		if (*arg != '-' &&
141119261079SEd Maste 		    !ciphers_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)){
141219261079SEd Maste 			error("%.200s line %d: Bad SSH2 cipher spec '%s'.",
1413c2d3a559SKris Kennaway 			    filename, linenum, arg ? arg : "<NONE>");
141419261079SEd Maste 			goto out;
141519261079SEd Maste 		}
1416e8aafc91SKris Kennaway 		if (*activep && options->ciphers == NULL)
1417c2d3a559SKris Kennaway 			options->ciphers = xstrdup(arg);
1418e8aafc91SKris Kennaway 		break;
1419e8aafc91SKris Kennaway 
1420ca3176e7SBrian Feldman 	case oMacs:
142119261079SEd Maste 		arg = argv_next(&ac, &av);
142219261079SEd Maste 		if (!arg || *arg == '\0') {
142319261079SEd Maste 			error("%.200s line %d: Missing argument.",
142419261079SEd Maste 			    filename, linenum);
142519261079SEd Maste 			goto out;
142619261079SEd Maste 		}
142719261079SEd Maste 		if (*arg != '-' &&
142819261079SEd Maste 		    !mac_valid(*arg == '+' || *arg == '^' ? arg + 1 : arg)) {
142919261079SEd Maste 			error("%.200s line %d: Bad SSH2 MAC spec '%s'.",
1430ca3176e7SBrian Feldman 			    filename, linenum, arg ? arg : "<NONE>");
143119261079SEd Maste 			goto out;
143219261079SEd Maste 		}
1433ca3176e7SBrian Feldman 		if (*activep && options->macs == NULL)
1434ca3176e7SBrian Feldman 			options->macs = xstrdup(arg);
1435ca3176e7SBrian Feldman 		break;
1436ca3176e7SBrian Feldman 
14374a421b63SDag-Erling Smørgrav 	case oKexAlgorithms:
143819261079SEd Maste 		arg = argv_next(&ac, &av);
143919261079SEd Maste 		if (!arg || *arg == '\0') {
144019261079SEd Maste 			error("%.200s line %d: Missing argument.",
14414a421b63SDag-Erling Smørgrav 			    filename, linenum);
144219261079SEd Maste 			goto out;
144319261079SEd Maste 		}
1444d93a896eSDag-Erling Smørgrav 		if (*arg != '-' &&
144519261079SEd Maste 		    !kex_names_valid(*arg == '+' || *arg == '^' ?
144619261079SEd Maste 		    arg + 1 : arg)) {
144719261079SEd Maste 			error("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
14484a421b63SDag-Erling Smørgrav 			    filename, linenum, arg ? arg : "<NONE>");
144919261079SEd Maste 			goto out;
145019261079SEd Maste 		}
14514a421b63SDag-Erling Smørgrav 		if (*activep && options->kex_algorithms == NULL)
14524a421b63SDag-Erling Smørgrav 			options->kex_algorithms = xstrdup(arg);
14534a421b63SDag-Erling Smørgrav 		break;
14544a421b63SDag-Erling Smørgrav 
1455ca3176e7SBrian Feldman 	case oHostKeyAlgorithms:
1456eccfee6eSDag-Erling Smørgrav 		charptr = &options->hostkeyalgorithms;
145719261079SEd Maste parse_pubkey_algos:
145819261079SEd Maste 		arg = argv_next(&ac, &av);
145919261079SEd Maste 		if (!arg || *arg == '\0') {
146019261079SEd Maste 			error("%.200s line %d: Missing argument.",
1461eccfee6eSDag-Erling Smørgrav 			    filename, linenum);
146219261079SEd Maste 			goto out;
146319261079SEd Maste 		}
1464d93a896eSDag-Erling Smørgrav 		if (*arg != '-' &&
146519261079SEd Maste 		    !sshkey_names_valid2(*arg == '+' || *arg == '^' ?
146619261079SEd Maste 		    arg + 1 : arg, 1)) {
146719261079SEd Maste 			error("%s line %d: Bad key types '%s'.",
1468ca3176e7SBrian Feldman 			    filename, linenum, arg ? arg : "<NONE>");
146919261079SEd Maste 			goto out;
147019261079SEd Maste 		}
1471eccfee6eSDag-Erling Smørgrav 		if (*activep && *charptr == NULL)
1472eccfee6eSDag-Erling Smørgrav 			*charptr = xstrdup(arg);
1473ca3176e7SBrian Feldman 		break;
1474ca3176e7SBrian Feldman 
14752f513db7SEd Maste 	case oCASignatureAlgorithms:
14762f513db7SEd Maste 		charptr = &options->ca_sign_algorithms;
147719261079SEd Maste 		goto parse_pubkey_algos;
14782f513db7SEd Maste 
1479511b41d2SMark Murray 	case oLogLevel:
1480d4af9e69SDag-Erling Smørgrav 		log_level_ptr = &options->log_level;
148119261079SEd Maste 		arg = argv_next(&ac, &av);
1482c2d3a559SKris Kennaway 		value = log_level_number(arg);
148319261079SEd Maste 		if (value == SYSLOG_LEVEL_NOT_SET) {
148419261079SEd Maste 			error("%.200s line %d: unsupported log level '%s'",
1485c2d3a559SKris Kennaway 			    filename, linenum, arg ? arg : "<NONE>");
148619261079SEd Maste 			goto out;
148719261079SEd Maste 		}
1488d4af9e69SDag-Erling Smørgrav 		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
1489d4af9e69SDag-Erling Smørgrav 			*log_level_ptr = (LogLevel) value;
1490511b41d2SMark Murray 		break;
1491511b41d2SMark Murray 
14924f52dfbbSDag-Erling Smørgrav 	case oLogFacility:
14934f52dfbbSDag-Erling Smørgrav 		log_facility_ptr = &options->log_facility;
149419261079SEd Maste 		arg = argv_next(&ac, &av);
14954f52dfbbSDag-Erling Smørgrav 		value = log_facility_number(arg);
149619261079SEd Maste 		if (value == SYSLOG_FACILITY_NOT_SET) {
149719261079SEd Maste 			error("%.200s line %d: unsupported log facility '%s'",
14984f52dfbbSDag-Erling Smørgrav 			    filename, linenum, arg ? arg : "<NONE>");
149919261079SEd Maste 			goto out;
150019261079SEd Maste 		}
15014f52dfbbSDag-Erling Smørgrav 		if (*log_facility_ptr == -1)
15024f52dfbbSDag-Erling Smørgrav 			*log_facility_ptr = (SyslogFacility) value;
15034f52dfbbSDag-Erling Smørgrav 		break;
15044f52dfbbSDag-Erling Smørgrav 
150519261079SEd Maste 	case oLogVerbose:
150619261079SEd Maste 		cppptr = &options->log_verbose;
150719261079SEd Maste 		uintptr = &options->num_log_verbose;
150819261079SEd Maste 		i = 0;
150919261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
151019261079SEd Maste 			if (*arg == '\0') {
151119261079SEd Maste 				error("%s line %d: keyword %s empty argument",
151219261079SEd Maste 				    filename, linenum, keyword);
151319261079SEd Maste 				goto out;
151419261079SEd Maste 			}
151519261079SEd Maste 			/* Allow "none" only in first position */
151619261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
151719261079SEd Maste 				if (i > 0 || ac > 0) {
151819261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
151919261079SEd Maste 					    "argument must appear alone.",
152019261079SEd Maste 					    filename, linenum, keyword);
152119261079SEd Maste 					goto out;
152219261079SEd Maste 				}
152319261079SEd Maste 			}
152419261079SEd Maste 			i++;
152519261079SEd Maste 			if (*activep && *uintptr == 0) {
152619261079SEd Maste 				*cppptr = xrecallocarray(*cppptr, *uintptr,
152719261079SEd Maste 				    *uintptr + 1, sizeof(**cppptr));
152819261079SEd Maste 				(*cppptr)[(*uintptr)++] = xstrdup(arg);
152919261079SEd Maste 			}
153019261079SEd Maste 		}
153119261079SEd Maste 		break;
153219261079SEd Maste 
1533af12a3e7SDag-Erling Smørgrav 	case oLocalForward:
1534511b41d2SMark Murray 	case oRemoteForward:
1535cce7d346SDag-Erling Smørgrav 	case oDynamicForward:
153619261079SEd Maste 		arg = argv_next(&ac, &av);
153719261079SEd Maste 		if (!arg || *arg == '\0') {
153819261079SEd Maste 			error("%.200s line %d: Missing argument.",
1539af12a3e7SDag-Erling Smørgrav 			    filename, linenum);
154019261079SEd Maste 			goto out;
154119261079SEd Maste 		}
1542cce7d346SDag-Erling Smørgrav 
15434f52dfbbSDag-Erling Smørgrav 		remotefwd = (opcode == oRemoteForward);
15444f52dfbbSDag-Erling Smørgrav 		dynamicfwd = (opcode == oDynamicForward);
15454f52dfbbSDag-Erling Smørgrav 
15464f52dfbbSDag-Erling Smørgrav 		if (!dynamicfwd) {
154719261079SEd Maste 			arg2 = argv_next(&ac, &av);
15484f52dfbbSDag-Erling Smørgrav 			if (arg2 == NULL || *arg2 == '\0') {
15494f52dfbbSDag-Erling Smørgrav 				if (remotefwd)
15504f52dfbbSDag-Erling Smørgrav 					dynamicfwd = 1;
155119261079SEd Maste 				else {
155219261079SEd Maste 					error("%.200s line %d: Missing target "
15534f52dfbbSDag-Erling Smørgrav 					    "argument.", filename, linenum);
155419261079SEd Maste 					goto out;
155519261079SEd Maste 				}
15564f52dfbbSDag-Erling Smørgrav 			} else {
1557aa49c926SDag-Erling Smørgrav 				/* construct a string for parse_forward */
15584f52dfbbSDag-Erling Smørgrav 				snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg,
15594f52dfbbSDag-Erling Smørgrav 				    arg2);
1560cce7d346SDag-Erling Smørgrav 			}
15614f52dfbbSDag-Erling Smørgrav 		}
15624f52dfbbSDag-Erling Smørgrav 		if (dynamicfwd)
15634f52dfbbSDag-Erling Smørgrav 			strlcpy(fwdarg, arg, sizeof(fwdarg));
1564aa49c926SDag-Erling Smørgrav 
156519261079SEd Maste 		if (parse_forward(&fwd, fwdarg, dynamicfwd, remotefwd) == 0) {
156619261079SEd Maste 			error("%.200s line %d: Bad forwarding specification.",
1567511b41d2SMark Murray 			    filename, linenum);
156819261079SEd Maste 			goto out;
156919261079SEd Maste 		}
1570aa49c926SDag-Erling Smørgrav 
1571af12a3e7SDag-Erling Smørgrav 		if (*activep) {
15724f52dfbbSDag-Erling Smørgrav 			if (remotefwd) {
1573aa49c926SDag-Erling Smørgrav 				add_remote_forward(options, &fwd);
15744f52dfbbSDag-Erling Smørgrav 			} else {
15754f52dfbbSDag-Erling Smørgrav 				add_local_forward(options, &fwd);
15764f52dfbbSDag-Erling Smørgrav 			}
1577af12a3e7SDag-Erling Smørgrav 		}
1578511b41d2SMark Murray 		break;
1579511b41d2SMark Murray 
158019261079SEd Maste 	case oPermitRemoteOpen:
158119261079SEd Maste 		uintptr = &options->num_permitted_remote_opens;
158219261079SEd Maste 		cppptr = &options->permitted_remote_opens;
158319261079SEd Maste 		arg = argv_next(&ac, &av);
158419261079SEd Maste 		if (!arg || *arg == '\0')
158519261079SEd Maste 			fatal("%s line %d: missing %s specification",
158619261079SEd Maste 			    filename, linenum, lookup_opcode_name(opcode));
158719261079SEd Maste 		uvalue = *uintptr;	/* modified later */
158819261079SEd Maste 		if (strcmp(arg, "any") == 0 || strcmp(arg, "none") == 0) {
158919261079SEd Maste 			if (*activep && uvalue == 0) {
159019261079SEd Maste 				*uintptr = 1;
159119261079SEd Maste 				*cppptr = xcalloc(1, sizeof(**cppptr));
159219261079SEd Maste 				(*cppptr)[0] = xstrdup(arg);
159319261079SEd Maste 			}
159419261079SEd Maste 			break;
159519261079SEd Maste 		}
159619261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
159719261079SEd Maste 			arg2 = xstrdup(arg);
1598*1323ec57SEd Maste 			p = hpdelim(&arg);
1599*1323ec57SEd Maste 			if (p == NULL) {
160019261079SEd Maste 				fatal("%s line %d: missing host in %s",
160119261079SEd Maste 				    filename, linenum,
160219261079SEd Maste 				    lookup_opcode_name(opcode));
160319261079SEd Maste 			}
160419261079SEd Maste 			p = cleanhostname(p);
160519261079SEd Maste 			/*
160619261079SEd Maste 			 * don't want to use permitopen_port to avoid
160719261079SEd Maste 			 * dependency on channels.[ch] here.
160819261079SEd Maste 			 */
160919261079SEd Maste 			if (arg == NULL ||
161019261079SEd Maste 			    (strcmp(arg, "*") != 0 && a2port(arg) <= 0)) {
161119261079SEd Maste 				fatal("%s line %d: bad port number in %s",
161219261079SEd Maste 				    filename, linenum,
161319261079SEd Maste 				    lookup_opcode_name(opcode));
161419261079SEd Maste 			}
161519261079SEd Maste 			if (*activep && uvalue == 0) {
161619261079SEd Maste 				opt_array_append(filename, linenum,
161719261079SEd Maste 				    lookup_opcode_name(opcode),
161819261079SEd Maste 				    cppptr, uintptr, arg2);
161919261079SEd Maste 			}
162019261079SEd Maste 			free(arg2);
162119261079SEd Maste 		}
162219261079SEd Maste 		break;
162319261079SEd Maste 
1624af12a3e7SDag-Erling Smørgrav 	case oClearAllForwardings:
1625af12a3e7SDag-Erling Smørgrav 		intptr = &options->clear_forwardings;
1626af12a3e7SDag-Erling Smørgrav 		goto parse_flag;
1627af12a3e7SDag-Erling Smørgrav 
1628511b41d2SMark Murray 	case oHost:
162919261079SEd Maste 		if (cmdline) {
163019261079SEd Maste 			error("Host directive not supported as a command-line "
1631f7167e0eSDag-Erling Smørgrav 			    "option");
163219261079SEd Maste 			goto out;
163319261079SEd Maste 		}
1634511b41d2SMark Murray 		*activep = 0;
1635e146993eSDag-Erling Smørgrav 		arg2 = NULL;
163619261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
163719261079SEd Maste 			if (*arg == '\0') {
163819261079SEd Maste 				error("%s line %d: keyword %s empty argument",
163919261079SEd Maste 				    filename, linenum, keyword);
164019261079SEd Maste 				goto out;
164119261079SEd Maste 			}
164219261079SEd Maste 			if ((flags & SSHCONF_NEVERMATCH) != 0) {
164319261079SEd Maste 				argv_consume(&ac);
1644076ad2f8SDag-Erling Smørgrav 				break;
164519261079SEd Maste 			}
1646e146993eSDag-Erling Smørgrav 			negated = *arg == '!';
1647e146993eSDag-Erling Smørgrav 			if (negated)
1648e146993eSDag-Erling Smørgrav 				arg++;
1649c2d3a559SKris Kennaway 			if (match_pattern(host, arg)) {
1650e146993eSDag-Erling Smørgrav 				if (negated) {
1651e146993eSDag-Erling Smørgrav 					debug("%.200s line %d: Skipping Host "
1652e146993eSDag-Erling Smørgrav 					    "block because of negated match "
1653e146993eSDag-Erling Smørgrav 					    "for %.100s", filename, linenum,
1654e146993eSDag-Erling Smørgrav 					    arg);
1655e146993eSDag-Erling Smørgrav 					*activep = 0;
165619261079SEd Maste 					argv_consume(&ac);
1657511b41d2SMark Murray 					break;
1658511b41d2SMark Murray 				}
1659e146993eSDag-Erling Smørgrav 				if (!*activep)
1660e146993eSDag-Erling Smørgrav 					arg2 = arg; /* logged below */
1661e146993eSDag-Erling Smørgrav 				*activep = 1;
1662e146993eSDag-Erling Smørgrav 			}
1663e146993eSDag-Erling Smørgrav 		}
1664e146993eSDag-Erling Smørgrav 		if (*activep)
1665e146993eSDag-Erling Smørgrav 			debug("%.200s line %d: Applying options for %.100s",
1666e146993eSDag-Erling Smørgrav 			    filename, linenum, arg2);
166719261079SEd Maste 		break;
1668511b41d2SMark Murray 
1669f7167e0eSDag-Erling Smørgrav 	case oMatch:
167019261079SEd Maste 		if (cmdline) {
167119261079SEd Maste 			error("Host directive not supported as a command-line "
1672f7167e0eSDag-Erling Smørgrav 			    "option");
167319261079SEd Maste 			goto out;
167419261079SEd Maste 		}
167519261079SEd Maste 		value = match_cfg_line(options, &str, pw, host, original_host,
167619261079SEd Maste 		    flags & SSHCONF_FINAL, want_final_pass,
167719261079SEd Maste 		    filename, linenum);
167819261079SEd Maste 		if (value < 0) {
167919261079SEd Maste 			error("%.200s line %d: Bad Match condition", filename,
1680f7167e0eSDag-Erling Smørgrav 			    linenum);
168119261079SEd Maste 			goto out;
168219261079SEd Maste 		}
1683076ad2f8SDag-Erling Smørgrav 		*activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value;
168419261079SEd Maste 		/*
168519261079SEd Maste 		 * If match_cfg_line() didn't consume all its arguments then
168619261079SEd Maste 		 * arrange for the extra arguments check below to fail.
168719261079SEd Maste 		 */
168819261079SEd Maste 
168919261079SEd Maste 		if (str == NULL || *str == '\0')
169019261079SEd Maste 			argv_consume(&ac);
1691f7167e0eSDag-Erling Smørgrav 		break;
1692f7167e0eSDag-Erling Smørgrav 
1693511b41d2SMark Murray 	case oEscapeChar:
1694511b41d2SMark Murray 		intptr = &options->escape_char;
169519261079SEd Maste 		arg = argv_next(&ac, &av);
169619261079SEd Maste 		if (!arg || *arg == '\0') {
169719261079SEd Maste 			error("%.200s line %d: Missing argument.",
169819261079SEd Maste 			    filename, linenum);
169919261079SEd Maste 			goto out;
170019261079SEd Maste 		}
1701557f75e5SDag-Erling Smørgrav 		if (strcmp(arg, "none") == 0)
1702557f75e5SDag-Erling Smørgrav 			value = SSH_ESCAPECHAR_NONE;
1703557f75e5SDag-Erling Smørgrav 		else if (arg[1] == '\0')
1704557f75e5SDag-Erling Smørgrav 			value = (u_char) arg[0];
1705557f75e5SDag-Erling Smørgrav 		else if (arg[0] == '^' && arg[2] == 0 &&
1706ca3176e7SBrian Feldman 		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
1707ca3176e7SBrian Feldman 			value = (u_char) arg[1] & 31;
1708511b41d2SMark Murray 		else {
170919261079SEd Maste 			error("%.200s line %d: Bad escape character.",
1710511b41d2SMark Murray 			    filename, linenum);
171119261079SEd Maste 			goto out;
1712511b41d2SMark Murray 		}
1713511b41d2SMark Murray 		if (*activep && *intptr == -1)
1714511b41d2SMark Murray 			*intptr = value;
1715511b41d2SMark Murray 		break;
1716511b41d2SMark Murray 
1717cf2b5f3bSDag-Erling Smørgrav 	case oAddressFamily:
1718cf2b5f3bSDag-Erling Smørgrav 		intptr = &options->address_family;
1719f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_addressfamily;
1720f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1721cf2b5f3bSDag-Erling Smørgrav 
1722e73e9afaSDag-Erling Smørgrav 	case oEnableSSHKeysign:
1723e73e9afaSDag-Erling Smørgrav 		intptr = &options->enable_ssh_keysign;
1724e73e9afaSDag-Erling Smørgrav 		goto parse_flag;
1725e73e9afaSDag-Erling Smørgrav 
17265962c0e9SDag-Erling Smørgrav 	case oIdentitiesOnly:
17275962c0e9SDag-Erling Smørgrav 		intptr = &options->identities_only;
17285962c0e9SDag-Erling Smørgrav 		goto parse_flag;
17295962c0e9SDag-Erling Smørgrav 
17301ec0d754SDag-Erling Smørgrav 	case oServerAliveInterval:
17311ec0d754SDag-Erling Smørgrav 		intptr = &options->server_alive_interval;
17321ec0d754SDag-Erling Smørgrav 		goto parse_time;
17331ec0d754SDag-Erling Smørgrav 
17341ec0d754SDag-Erling Smørgrav 	case oServerAliveCountMax:
17351ec0d754SDag-Erling Smørgrav 		intptr = &options->server_alive_count_max;
17361ec0d754SDag-Erling Smørgrav 		goto parse_int;
17371ec0d754SDag-Erling Smørgrav 
173821e764dfSDag-Erling Smørgrav 	case oSendEnv:
173919261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
174019261079SEd Maste 			if (*arg == '\0' || strchr(arg, '=') != NULL) {
174119261079SEd Maste 				error("%s line %d: Invalid environment name.",
174221e764dfSDag-Erling Smørgrav 				    filename, linenum);
174319261079SEd Maste 				goto out;
174419261079SEd Maste 			}
1745aa49c926SDag-Erling Smørgrav 			if (!*activep)
1746aa49c926SDag-Erling Smørgrav 				continue;
1747190cef3dSDag-Erling Smørgrav 			if (*arg == '-') {
1748190cef3dSDag-Erling Smørgrav 				/* Removing an env var */
1749190cef3dSDag-Erling Smørgrav 				rm_env(options, arg, filename, linenum);
1750190cef3dSDag-Erling Smørgrav 				continue;
1751190cef3dSDag-Erling Smørgrav 			} else {
1752190cef3dSDag-Erling Smørgrav 				/* Adding an env var */
175319261079SEd Maste 				if (options->num_send_env >= INT_MAX) {
175419261079SEd Maste 					error("%s line %d: too many send env.",
175521e764dfSDag-Erling Smørgrav 					    filename, linenum);
175619261079SEd Maste 					goto out;
175719261079SEd Maste 				}
1758190cef3dSDag-Erling Smørgrav 				options->send_env = xrecallocarray(
1759190cef3dSDag-Erling Smørgrav 				    options->send_env, options->num_send_env,
1760190cef3dSDag-Erling Smørgrav 				    options->num_send_env + 1,
1761190cef3dSDag-Erling Smørgrav 				    sizeof(*options->send_env));
176221e764dfSDag-Erling Smørgrav 				options->send_env[options->num_send_env++] =
176321e764dfSDag-Erling Smørgrav 				    xstrdup(arg);
176421e764dfSDag-Erling Smørgrav 			}
1765190cef3dSDag-Erling Smørgrav 		}
1766190cef3dSDag-Erling Smørgrav 		break;
1767190cef3dSDag-Erling Smørgrav 
1768190cef3dSDag-Erling Smørgrav 	case oSetEnv:
1769190cef3dSDag-Erling Smørgrav 		value = options->num_setenv;
177019261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
177119261079SEd Maste 			if (strchr(arg, '=') == NULL) {
177219261079SEd Maste 				error("%s line %d: Invalid SetEnv.",
1773190cef3dSDag-Erling Smørgrav 				    filename, linenum);
177419261079SEd Maste 				goto out;
177519261079SEd Maste 			}
1776190cef3dSDag-Erling Smørgrav 			if (!*activep || value != 0)
1777190cef3dSDag-Erling Smørgrav 				continue;
1778190cef3dSDag-Erling Smørgrav 			/* Adding a setenv var */
177919261079SEd Maste 			if (options->num_setenv >= INT_MAX) {
178019261079SEd Maste 				error("%s line %d: too many SetEnv.",
1781190cef3dSDag-Erling Smørgrav 				    filename, linenum);
178219261079SEd Maste 				goto out;
178319261079SEd Maste 			}
1784190cef3dSDag-Erling Smørgrav 			options->setenv = xrecallocarray(
1785190cef3dSDag-Erling Smørgrav 			    options->setenv, options->num_setenv,
1786190cef3dSDag-Erling Smørgrav 			    options->num_setenv + 1, sizeof(*options->setenv));
1787190cef3dSDag-Erling Smørgrav 			options->setenv[options->num_setenv++] = xstrdup(arg);
1788190cef3dSDag-Erling Smørgrav 		}
178921e764dfSDag-Erling Smørgrav 		break;
179021e764dfSDag-Erling Smørgrav 
179121e764dfSDag-Erling Smørgrav 	case oControlPath:
179221e764dfSDag-Erling Smørgrav 		charptr = &options->control_path;
179321e764dfSDag-Erling Smørgrav 		goto parse_string;
179421e764dfSDag-Erling Smørgrav 
179521e764dfSDag-Erling Smørgrav 	case oControlMaster:
179621e764dfSDag-Erling Smørgrav 		intptr = &options->control_master;
1797f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_controlmaster;
1798f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
179921e764dfSDag-Erling Smørgrav 
1800e2f6069cSDag-Erling Smørgrav 	case oControlPersist:
1801e2f6069cSDag-Erling Smørgrav 		/* no/false/yes/true, or a time spec */
1802e2f6069cSDag-Erling Smørgrav 		intptr = &options->control_persist;
180319261079SEd Maste 		arg = argv_next(&ac, &av);
180419261079SEd Maste 		if (!arg || *arg == '\0') {
180519261079SEd Maste 			error("%.200s line %d: Missing ControlPersist"
1806e2f6069cSDag-Erling Smørgrav 			    " argument.", filename, linenum);
180719261079SEd Maste 			goto out;
180819261079SEd Maste 		}
1809e2f6069cSDag-Erling Smørgrav 		value = 0;
1810e2f6069cSDag-Erling Smørgrav 		value2 = 0;	/* timeout */
1811e2f6069cSDag-Erling Smørgrav 		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
1812e2f6069cSDag-Erling Smørgrav 			value = 0;
1813e2f6069cSDag-Erling Smørgrav 		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
1814e2f6069cSDag-Erling Smørgrav 			value = 1;
1815e2f6069cSDag-Erling Smørgrav 		else if ((value2 = convtime(arg)) >= 0)
1816e2f6069cSDag-Erling Smørgrav 			value = 1;
181719261079SEd Maste 		else {
181819261079SEd Maste 			error("%.200s line %d: Bad ControlPersist argument.",
1819e2f6069cSDag-Erling Smørgrav 			    filename, linenum);
182019261079SEd Maste 			goto out;
182119261079SEd Maste 		}
1822e2f6069cSDag-Erling Smørgrav 		if (*activep && *intptr == -1) {
1823e2f6069cSDag-Erling Smørgrav 			*intptr = value;
1824e2f6069cSDag-Erling Smørgrav 			options->control_persist_timeout = value2;
1825e2f6069cSDag-Erling Smørgrav 		}
1826e2f6069cSDag-Erling Smørgrav 		break;
1827e2f6069cSDag-Erling Smørgrav 
1828aa49c926SDag-Erling Smørgrav 	case oHashKnownHosts:
1829aa49c926SDag-Erling Smørgrav 		intptr = &options->hash_known_hosts;
1830aa49c926SDag-Erling Smørgrav 		goto parse_flag;
1831aa49c926SDag-Erling Smørgrav 
1832b74df5b2SDag-Erling Smørgrav 	case oTunnel:
1833b74df5b2SDag-Erling Smørgrav 		intptr = &options->tun_open;
1834f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_tunnel;
1835f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1836b74df5b2SDag-Erling Smørgrav 
1837b74df5b2SDag-Erling Smørgrav 	case oTunnelDevice:
183819261079SEd Maste 		arg = argv_next(&ac, &av);
183919261079SEd Maste 		if (!arg || *arg == '\0') {
184019261079SEd Maste 			error("%.200s line %d: Missing argument.",
184119261079SEd Maste 			    filename, linenum);
184219261079SEd Maste 			goto out;
184319261079SEd Maste 		}
1844b74df5b2SDag-Erling Smørgrav 		value = a2tun(arg, &value2);
184519261079SEd Maste 		if (value == SSH_TUNID_ERR) {
184619261079SEd Maste 			error("%.200s line %d: Bad tun device.",
184719261079SEd Maste 			    filename, linenum);
184819261079SEd Maste 			goto out;
184919261079SEd Maste 		}
185019261079SEd Maste 		if (*activep && options->tun_local == -1) {
1851b74df5b2SDag-Erling Smørgrav 			options->tun_local = value;
1852b74df5b2SDag-Erling Smørgrav 			options->tun_remote = value2;
1853b74df5b2SDag-Erling Smørgrav 		}
1854b74df5b2SDag-Erling Smørgrav 		break;
1855b74df5b2SDag-Erling Smørgrav 
1856b74df5b2SDag-Erling Smørgrav 	case oLocalCommand:
1857b74df5b2SDag-Erling Smørgrav 		charptr = &options->local_command;
1858b74df5b2SDag-Erling Smørgrav 		goto parse_command;
1859b74df5b2SDag-Erling Smørgrav 
1860b74df5b2SDag-Erling Smørgrav 	case oPermitLocalCommand:
1861b74df5b2SDag-Erling Smørgrav 		intptr = &options->permit_local_command;
1862b74df5b2SDag-Erling Smørgrav 		goto parse_flag;
1863b74df5b2SDag-Erling Smørgrav 
18644f52dfbbSDag-Erling Smørgrav 	case oRemoteCommand:
18654f52dfbbSDag-Erling Smørgrav 		charptr = &options->remote_command;
18664f52dfbbSDag-Erling Smørgrav 		goto parse_command;
18674f52dfbbSDag-Erling Smørgrav 
1868d4af9e69SDag-Erling Smørgrav 	case oVisualHostKey:
1869d4af9e69SDag-Erling Smørgrav 		intptr = &options->visual_host_key;
1870d4af9e69SDag-Erling Smørgrav 		goto parse_flag;
1871d4af9e69SDag-Erling Smørgrav 
1872076ad2f8SDag-Erling Smørgrav 	case oInclude:
187319261079SEd Maste 		if (cmdline) {
187419261079SEd Maste 			error("Include directive not supported as a "
1875076ad2f8SDag-Erling Smørgrav 			    "command-line option");
187619261079SEd Maste 			goto out;
187719261079SEd Maste 		}
1878076ad2f8SDag-Erling Smørgrav 		value = 0;
187919261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
188019261079SEd Maste 			if (*arg == '\0') {
188119261079SEd Maste 				error("%s line %d: keyword %s empty argument",
188219261079SEd Maste 				    filename, linenum, keyword);
188319261079SEd Maste 				goto out;
188419261079SEd Maste 			}
1885076ad2f8SDag-Erling Smørgrav 			/*
1886076ad2f8SDag-Erling Smørgrav 			 * Ensure all paths are anchored. User configuration
1887076ad2f8SDag-Erling Smørgrav 			 * files may begin with '~/' but system configurations
1888076ad2f8SDag-Erling Smørgrav 			 * must not. If the path is relative, then treat it
1889076ad2f8SDag-Erling Smørgrav 			 * as living in ~/.ssh for user configurations or
1890076ad2f8SDag-Erling Smørgrav 			 * /etc/ssh for system ones.
1891076ad2f8SDag-Erling Smørgrav 			 */
189219261079SEd Maste 			if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) {
189319261079SEd Maste 				error("%.200s line %d: bad include path %s.",
1894076ad2f8SDag-Erling Smørgrav 				    filename, linenum, arg);
189519261079SEd Maste 				goto out;
189619261079SEd Maste 			}
189719261079SEd Maste 			if (!path_absolute(arg) && *arg != '~') {
1898076ad2f8SDag-Erling Smørgrav 				xasprintf(&arg2, "%s/%s",
1899076ad2f8SDag-Erling Smørgrav 				    (flags & SSHCONF_USERCONF) ?
1900076ad2f8SDag-Erling Smørgrav 				    "~/" _PATH_SSH_USER_DIR : SSHDIR, arg);
1901076ad2f8SDag-Erling Smørgrav 			} else
1902076ad2f8SDag-Erling Smørgrav 				arg2 = xstrdup(arg);
1903076ad2f8SDag-Erling Smørgrav 			memset(&gl, 0, sizeof(gl));
1904076ad2f8SDag-Erling Smørgrav 			r = glob(arg2, GLOB_TILDE, NULL, &gl);
1905076ad2f8SDag-Erling Smørgrav 			if (r == GLOB_NOMATCH) {
1906076ad2f8SDag-Erling Smørgrav 				debug("%.200s line %d: include %s matched no "
1907076ad2f8SDag-Erling Smørgrav 				    "files",filename, linenum, arg2);
1908d93a896eSDag-Erling Smørgrav 				free(arg2);
1909076ad2f8SDag-Erling Smørgrav 				continue;
191019261079SEd Maste 			} else if (r != 0) {
191119261079SEd Maste 				error("%.200s line %d: glob failed for %s.",
1912076ad2f8SDag-Erling Smørgrav 				    filename, linenum, arg2);
191319261079SEd Maste 				goto out;
191419261079SEd Maste 			}
1915076ad2f8SDag-Erling Smørgrav 			free(arg2);
1916076ad2f8SDag-Erling Smørgrav 			oactive = *activep;
191719261079SEd Maste 			for (i = 0; i < gl.gl_pathc; i++) {
1918076ad2f8SDag-Erling Smørgrav 				debug3("%.200s line %d: Including file %s "
1919076ad2f8SDag-Erling Smørgrav 				    "depth %d%s", filename, linenum,
1920076ad2f8SDag-Erling Smørgrav 				    gl.gl_pathv[i], depth,
1921076ad2f8SDag-Erling Smørgrav 				    oactive ? "" : " (parse only)");
1922076ad2f8SDag-Erling Smørgrav 				r = read_config_file_depth(gl.gl_pathv[i],
1923076ad2f8SDag-Erling Smørgrav 				    pw, host, original_host, options,
1924076ad2f8SDag-Erling Smørgrav 				    flags | SSHCONF_CHECKPERM |
1925076ad2f8SDag-Erling Smørgrav 				    (oactive ? 0 : SSHCONF_NEVERMATCH),
192619261079SEd Maste 				    activep, want_final_pass, depth + 1);
1927d93a896eSDag-Erling Smørgrav 				if (r != 1 && errno != ENOENT) {
192819261079SEd Maste 					error("Can't open user config file "
1929d93a896eSDag-Erling Smørgrav 					    "%.100s: %.100s", gl.gl_pathv[i],
1930d93a896eSDag-Erling Smørgrav 					    strerror(errno));
193119261079SEd Maste 					globfree(&gl);
193219261079SEd Maste 					goto out;
1933d93a896eSDag-Erling Smørgrav 				}
1934076ad2f8SDag-Erling Smørgrav 				/*
1935076ad2f8SDag-Erling Smørgrav 				 * don't let Match in includes clobber the
1936076ad2f8SDag-Erling Smørgrav 				 * containing file's Match state.
1937076ad2f8SDag-Erling Smørgrav 				 */
1938076ad2f8SDag-Erling Smørgrav 				*activep = oactive;
1939076ad2f8SDag-Erling Smørgrav 				if (r != 1)
1940076ad2f8SDag-Erling Smørgrav 					value = -1;
1941076ad2f8SDag-Erling Smørgrav 			}
1942076ad2f8SDag-Erling Smørgrav 			globfree(&gl);
1943076ad2f8SDag-Erling Smørgrav 		}
1944076ad2f8SDag-Erling Smørgrav 		if (value != 0)
194519261079SEd Maste 			ret = value;
1946076ad2f8SDag-Erling Smørgrav 		break;
1947076ad2f8SDag-Erling Smørgrav 
19484a421b63SDag-Erling Smørgrav 	case oIPQoS:
194919261079SEd Maste 		arg = argv_next(&ac, &av);
195019261079SEd Maste 		if ((value = parse_ipqos(arg)) == -1) {
195119261079SEd Maste 			error("%s line %d: Bad IPQoS value: %s",
19524a421b63SDag-Erling Smørgrav 			    filename, linenum, arg);
195319261079SEd Maste 			goto out;
195419261079SEd Maste 		}
195519261079SEd Maste 		arg = argv_next(&ac, &av);
19564a421b63SDag-Erling Smørgrav 		if (arg == NULL)
19574a421b63SDag-Erling Smørgrav 			value2 = value;
195819261079SEd Maste 		else if ((value2 = parse_ipqos(arg)) == -1) {
195919261079SEd Maste 			error("%s line %d: Bad IPQoS value: %s",
19604a421b63SDag-Erling Smørgrav 			    filename, linenum, arg);
196119261079SEd Maste 			goto out;
196219261079SEd Maste 		}
196319261079SEd Maste 		if (*activep && options->ip_qos_interactive == -1) {
19644a421b63SDag-Erling Smørgrav 			options->ip_qos_interactive = value;
19654a421b63SDag-Erling Smørgrav 			options->ip_qos_bulk = value2;
19664a421b63SDag-Erling Smørgrav 		}
19674a421b63SDag-Erling Smørgrav 		break;
19684a421b63SDag-Erling Smørgrav 
1969e146993eSDag-Erling Smørgrav 	case oRequestTTY:
1970e146993eSDag-Erling Smørgrav 		intptr = &options->request_tty;
1971f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_requesttty;
1972f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
1973975616f0SDag-Erling Smørgrav 
197419261079SEd Maste 	case oSessionType:
197519261079SEd Maste 		intptr = &options->session_type;
197619261079SEd Maste 		multistate_ptr = multistate_sessiontype;
197719261079SEd Maste 		goto parse_multistate;
197819261079SEd Maste 
197919261079SEd Maste 	case oStdinNull:
198019261079SEd Maste 		intptr = &options->stdin_null;
198119261079SEd Maste 		goto parse_flag;
198219261079SEd Maste 
198319261079SEd Maste 	case oForkAfterAuthentication:
198419261079SEd Maste 		intptr = &options->fork_after_authentication;
198519261079SEd Maste 		goto parse_flag;
198619261079SEd Maste 
1987e146993eSDag-Erling Smørgrav 	case oVersionAddendum:
198819261079SEd Maste 		if (str == NULL)
1989462c32cbSDag-Erling Smørgrav 			fatal("%.200s line %d: Missing argument.", filename,
1990462c32cbSDag-Erling Smørgrav 			    linenum);
199119261079SEd Maste 		len = strspn(str, WHITESPACE);
1992462c32cbSDag-Erling Smørgrav 		if (*activep && options->version_addendum == NULL) {
199319261079SEd Maste 			if (strcasecmp(str + len, "none") == 0)
1994462c32cbSDag-Erling Smørgrav 				options->version_addendum = xstrdup("");
199519261079SEd Maste 			else if (strchr(str + len, '\r') != NULL)
1996462c32cbSDag-Erling Smørgrav 				fatal("%.200s line %d: Invalid argument",
1997462c32cbSDag-Erling Smørgrav 				    filename, linenum);
1998462c32cbSDag-Erling Smørgrav 			else
199919261079SEd Maste 				options->version_addendum = xstrdup(str + len);
2000462c32cbSDag-Erling Smørgrav 		}
2001462c32cbSDag-Erling Smørgrav 		return 0;
2002e146993eSDag-Erling Smørgrav 
2003e4a9863fSDag-Erling Smørgrav 	case oIgnoreUnknown:
2004e4a9863fSDag-Erling Smørgrav 		charptr = &options->ignored_unknown;
2005e4a9863fSDag-Erling Smørgrav 		goto parse_string;
2006e4a9863fSDag-Erling Smørgrav 
2007f7167e0eSDag-Erling Smørgrav 	case oProxyUseFdpass:
2008f7167e0eSDag-Erling Smørgrav 		intptr = &options->proxy_use_fdpass;
2009f7167e0eSDag-Erling Smørgrav 		goto parse_flag;
2010f7167e0eSDag-Erling Smørgrav 
2011f7167e0eSDag-Erling Smørgrav 	case oCanonicalDomains:
2012f7167e0eSDag-Erling Smørgrav 		value = options->num_canonical_domains != 0;
201319261079SEd Maste 		i = 0;
201419261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
201519261079SEd Maste 			if (*arg == '\0') {
201619261079SEd Maste 				error("%s line %d: keyword %s empty argument",
201719261079SEd Maste 				    filename, linenum, keyword);
201819261079SEd Maste 				goto out;
201919261079SEd Maste 			}
202019261079SEd Maste 			/* Allow "none" only in first position */
202119261079SEd Maste 			if (strcasecmp(arg, "none") == 0) {
202219261079SEd Maste 				if (i > 0 || ac > 0) {
202319261079SEd Maste 					error("%s line %d: keyword %s \"none\" "
202419261079SEd Maste 					    "argument must appear alone.",
202519261079SEd Maste 					    filename, linenum, keyword);
202619261079SEd Maste 					goto out;
202719261079SEd Maste 				}
202819261079SEd Maste 			}
202919261079SEd Maste 			i++;
203047dd1d1bSDag-Erling Smørgrav 			if (!valid_domain(arg, 1, &errstr)) {
203119261079SEd Maste 				error("%s line %d: %s", filename, linenum,
203247dd1d1bSDag-Erling Smørgrav 				    errstr);
203319261079SEd Maste 				goto out;
203447dd1d1bSDag-Erling Smørgrav 			}
2035f7167e0eSDag-Erling Smørgrav 			if (!*activep || value)
2036f7167e0eSDag-Erling Smørgrav 				continue;
203719261079SEd Maste 			if (options->num_canonical_domains >=
203819261079SEd Maste 			    MAX_CANON_DOMAINS) {
203919261079SEd Maste 				error("%s line %d: too many hostname suffixes.",
2040f7167e0eSDag-Erling Smørgrav 				    filename, linenum);
204119261079SEd Maste 				goto out;
204219261079SEd Maste 			}
2043f7167e0eSDag-Erling Smørgrav 			options->canonical_domains[
2044f7167e0eSDag-Erling Smørgrav 			    options->num_canonical_domains++] = xstrdup(arg);
2045f7167e0eSDag-Erling Smørgrav 		}
2046f7167e0eSDag-Erling Smørgrav 		break;
2047f7167e0eSDag-Erling Smørgrav 
2048f7167e0eSDag-Erling Smørgrav 	case oCanonicalizePermittedCNAMEs:
2049f7167e0eSDag-Erling Smørgrav 		value = options->num_permitted_cnames != 0;
2050e9e8876aSEd Maste 		i = 0;
205119261079SEd Maste 		while ((arg = argv_next(&ac, &av)) != NULL) {
2052e9e8876aSEd Maste 			/*
2053e9e8876aSEd Maste 			 * Either 'none' (only in first position), '*' for
2054e9e8876aSEd Maste 			 * everything or 'list:list'
2055e9e8876aSEd Maste 			 */
2056e9e8876aSEd Maste 			if (strcasecmp(arg, "none") == 0) {
2057e9e8876aSEd Maste 				if (i > 0 || ac > 0) {
2058e9e8876aSEd Maste 					error("%s line %d: keyword %s \"none\" "
2059e9e8876aSEd Maste 					    "argument must appear alone.",
2060e9e8876aSEd Maste 					    filename, linenum, keyword);
2061e9e8876aSEd Maste 					goto out;
2062e9e8876aSEd Maste 				}
2063e9e8876aSEd Maste 				arg2 = "";
2064e9e8876aSEd Maste 			} else if (strcmp(arg, "*") == 0) {
2065f7167e0eSDag-Erling Smørgrav 				arg2 = arg;
2066e9e8876aSEd Maste 			} else {
2067f7167e0eSDag-Erling Smørgrav 				lowercase(arg);
2068f7167e0eSDag-Erling Smørgrav 				if ((arg2 = strchr(arg, ':')) == NULL ||
2069f7167e0eSDag-Erling Smørgrav 				    arg2[1] == '\0') {
207019261079SEd Maste 					error("%s line %d: "
2071f7167e0eSDag-Erling Smørgrav 					    "Invalid permitted CNAME \"%s\"",
2072f7167e0eSDag-Erling Smørgrav 					    filename, linenum, arg);
207319261079SEd Maste 					goto out;
2074f7167e0eSDag-Erling Smørgrav 				}
2075f7167e0eSDag-Erling Smørgrav 				*arg2 = '\0';
2076f7167e0eSDag-Erling Smørgrav 				arg2++;
2077f7167e0eSDag-Erling Smørgrav 			}
2078e9e8876aSEd Maste 			i++;
2079f7167e0eSDag-Erling Smørgrav 			if (!*activep || value)
2080f7167e0eSDag-Erling Smørgrav 				continue;
208119261079SEd Maste 			if (options->num_permitted_cnames >=
208219261079SEd Maste 			    MAX_CANON_DOMAINS) {
208319261079SEd Maste 				error("%s line %d: too many permitted CNAMEs.",
2084f7167e0eSDag-Erling Smørgrav 				    filename, linenum);
208519261079SEd Maste 				goto out;
208619261079SEd Maste 			}
2087f7167e0eSDag-Erling Smørgrav 			cname = options->permitted_cnames +
2088f7167e0eSDag-Erling Smørgrav 			    options->num_permitted_cnames++;
2089f7167e0eSDag-Erling Smørgrav 			cname->source_list = xstrdup(arg);
2090f7167e0eSDag-Erling Smørgrav 			cname->target_list = xstrdup(arg2);
2091f7167e0eSDag-Erling Smørgrav 		}
2092f7167e0eSDag-Erling Smørgrav 		break;
2093f7167e0eSDag-Erling Smørgrav 
2094f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeHostname:
2095f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_hostname;
2096f7167e0eSDag-Erling Smørgrav 		multistate_ptr = multistate_canonicalizehostname;
2097f7167e0eSDag-Erling Smørgrav 		goto parse_multistate;
2098f7167e0eSDag-Erling Smørgrav 
2099f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeMaxDots:
2100f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_max_dots;
2101f7167e0eSDag-Erling Smørgrav 		goto parse_int;
2102f7167e0eSDag-Erling Smørgrav 
2103f7167e0eSDag-Erling Smørgrav 	case oCanonicalizeFallbackLocal:
2104f7167e0eSDag-Erling Smørgrav 		intptr = &options->canonicalize_fallback_local;
2105f7167e0eSDag-Erling Smørgrav 		goto parse_flag;
2106f7167e0eSDag-Erling Smørgrav 
2107a0ee8cc6SDag-Erling Smørgrav 	case oStreamLocalBindMask:
210819261079SEd Maste 		arg = argv_next(&ac, &av);
210919261079SEd Maste 		if (!arg || *arg == '\0') {
211019261079SEd Maste 			error("%.200s line %d: Missing StreamLocalBindMask "
211119261079SEd Maste 			    "argument.", filename, linenum);
211219261079SEd Maste 			goto out;
211319261079SEd Maste 		}
2114a0ee8cc6SDag-Erling Smørgrav 		/* Parse mode in octal format */
2115a0ee8cc6SDag-Erling Smørgrav 		value = strtol(arg, &endofnumber, 8);
211619261079SEd Maste 		if (arg == endofnumber || value < 0 || value > 0777) {
211719261079SEd Maste 			error("%.200s line %d: Bad mask.", filename, linenum);
211819261079SEd Maste 			goto out;
211919261079SEd Maste 		}
2120a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_mask = (mode_t)value;
2121a0ee8cc6SDag-Erling Smørgrav 		break;
2122a0ee8cc6SDag-Erling Smørgrav 
2123a0ee8cc6SDag-Erling Smørgrav 	case oStreamLocalBindUnlink:
2124a0ee8cc6SDag-Erling Smørgrav 		intptr = &options->fwd_opts.streamlocal_bind_unlink;
2125a0ee8cc6SDag-Erling Smørgrav 		goto parse_flag;
2126a0ee8cc6SDag-Erling Smørgrav 
2127bc5531deSDag-Erling Smørgrav 	case oRevokedHostKeys:
2128bc5531deSDag-Erling Smørgrav 		charptr = &options->revoked_host_keys;
2129bc5531deSDag-Erling Smørgrav 		goto parse_string;
2130bc5531deSDag-Erling Smørgrav 
2131bc5531deSDag-Erling Smørgrav 	case oFingerprintHash:
2132bc5531deSDag-Erling Smørgrav 		intptr = &options->fingerprint_hash;
213319261079SEd Maste 		arg = argv_next(&ac, &av);
213419261079SEd Maste 		if (!arg || *arg == '\0') {
213519261079SEd Maste 			error("%.200s line %d: Missing argument.",
2136bc5531deSDag-Erling Smørgrav 			    filename, linenum);
213719261079SEd Maste 			goto out;
213819261079SEd Maste 		}
213919261079SEd Maste 		if ((value = ssh_digest_alg_by_name(arg)) == -1) {
214019261079SEd Maste 			error("%.200s line %d: Invalid hash algorithm \"%s\".",
2141bc5531deSDag-Erling Smørgrav 			    filename, linenum, arg);
214219261079SEd Maste 			goto out;
214319261079SEd Maste 		}
2144bc5531deSDag-Erling Smørgrav 		if (*activep && *intptr == -1)
2145bc5531deSDag-Erling Smørgrav 			*intptr = value;
2146bc5531deSDag-Erling Smørgrav 		break;
2147bc5531deSDag-Erling Smørgrav 
2148bc5531deSDag-Erling Smørgrav 	case oUpdateHostkeys:
2149bc5531deSDag-Erling Smørgrav 		intptr = &options->update_hostkeys;
2150bc5531deSDag-Erling Smørgrav 		multistate_ptr = multistate_yesnoask;
2151bc5531deSDag-Erling Smørgrav 		goto parse_multistate;
2152bc5531deSDag-Erling Smørgrav 
215319261079SEd Maste 	case oHostbasedAcceptedAlgorithms:
215419261079SEd Maste 		charptr = &options->hostbased_accepted_algos;
215519261079SEd Maste 		goto parse_pubkey_algos;
2156eccfee6eSDag-Erling Smørgrav 
215719261079SEd Maste 	case oPubkeyAcceptedAlgorithms:
215819261079SEd Maste 		charptr = &options->pubkey_accepted_algos;
215919261079SEd Maste 		goto parse_pubkey_algos;
2160bc5531deSDag-Erling Smørgrav 
2161acc1a9efSDag-Erling Smørgrav 	case oAddKeysToAgent:
216219261079SEd Maste 		arg = argv_next(&ac, &av);
216319261079SEd Maste 		arg2 = argv_next(&ac, &av);
216419261079SEd Maste 		value = parse_multistate_value(arg, filename, linenum,
216519261079SEd Maste 		    multistate_yesnoaskconfirm);
216619261079SEd Maste 		value2 = 0; /* unlimited lifespan by default */
216719261079SEd Maste 		if (value == 3 && arg2 != NULL) {
216819261079SEd Maste 			/* allow "AddKeysToAgent confirm 5m" */
216919261079SEd Maste 			if ((value2 = convtime(arg2)) == -1 ||
217019261079SEd Maste 			    value2 > INT_MAX) {
217119261079SEd Maste 				error("%s line %d: invalid time value.",
217219261079SEd Maste 				    filename, linenum);
217319261079SEd Maste 				goto out;
217419261079SEd Maste 			}
217519261079SEd Maste 		} else if (value == -1 && arg2 == NULL) {
217619261079SEd Maste 			if ((value2 = convtime(arg)) == -1 ||
217719261079SEd Maste 			    value2 > INT_MAX) {
217819261079SEd Maste 				error("%s line %d: unsupported option",
217919261079SEd Maste 				    filename, linenum);
218019261079SEd Maste 				goto out;
218119261079SEd Maste 			}
218219261079SEd Maste 			value = 1; /* yes */
218319261079SEd Maste 		} else if (value == -1 || arg2 != NULL) {
218419261079SEd Maste 			error("%s line %d: unsupported option",
218519261079SEd Maste 			    filename, linenum);
218619261079SEd Maste 			goto out;
218719261079SEd Maste 		}
218819261079SEd Maste 		if (*activep && options->add_keys_to_agent == -1) {
218919261079SEd Maste 			options->add_keys_to_agent = value;
219019261079SEd Maste 			options->add_keys_to_agent_lifespan = value2;
219119261079SEd Maste 		}
219219261079SEd Maste 		break;
2193acc1a9efSDag-Erling Smørgrav 
2194076ad2f8SDag-Erling Smørgrav 	case oIdentityAgent:
2195076ad2f8SDag-Erling Smørgrav 		charptr = &options->identity_agent;
219619261079SEd Maste 		arg = argv_next(&ac, &av);
219719261079SEd Maste 		if (!arg || *arg == '\0') {
219819261079SEd Maste 			error("%.200s line %d: Missing argument.",
21992f513db7SEd Maste 			    filename, linenum);
220019261079SEd Maste 			goto out;
220119261079SEd Maste 		}
220219261079SEd Maste   parse_agent_path:
22032f513db7SEd Maste 		/* Extra validation if the string represents an env var. */
220419261079SEd Maste 		if ((arg2 = dollar_expand(&r, arg)) == NULL || r) {
220519261079SEd Maste 			error("%.200s line %d: Invalid environment expansion "
220619261079SEd Maste 			    "%s.", filename, linenum, arg);
220719261079SEd Maste 			goto out;
220819261079SEd Maste 		}
220919261079SEd Maste 		free(arg2);
221019261079SEd Maste 		/* check for legacy environment format */
221119261079SEd Maste 		if (arg[0] == '$' && arg[1] != '{' &&
221219261079SEd Maste 		    !valid_env_name(arg + 1)) {
221319261079SEd Maste 			error("%.200s line %d: Invalid environment name %s.",
22142f513db7SEd Maste 			    filename, linenum, arg);
221519261079SEd Maste 			goto out;
22162f513db7SEd Maste 		}
22172f513db7SEd Maste 		if (*activep && *charptr == NULL)
22182f513db7SEd Maste 			*charptr = xstrdup(arg);
22192f513db7SEd Maste 		break;
2220076ad2f8SDag-Erling Smørgrav 
222180628bacSDag-Erling Smørgrav 	case oDeprecated:
222280628bacSDag-Erling Smørgrav 		debug("%s line %d: Deprecated option \"%s\"",
222380628bacSDag-Erling Smørgrav 		    filename, linenum, keyword);
222419261079SEd Maste 		argv_consume(&ac);
222519261079SEd Maste 		break;
222680628bacSDag-Erling Smørgrav 
2227cf2b5f3bSDag-Erling Smørgrav 	case oUnsupported:
2228cf2b5f3bSDag-Erling Smørgrav 		error("%s line %d: Unsupported option \"%s\"",
2229cf2b5f3bSDag-Erling Smørgrav 		    filename, linenum, keyword);
223019261079SEd Maste 		argv_consume(&ac);
223119261079SEd Maste 		break;
2232cf2b5f3bSDag-Erling Smørgrav 
2233511b41d2SMark Murray 	default:
223419261079SEd Maste 		error("%s line %d: Unimplemented opcode %d",
223519261079SEd Maste 		    filename, linenum, opcode);
223619261079SEd Maste 		goto out;
2237511b41d2SMark Murray 	}
2238511b41d2SMark Murray 
2239511b41d2SMark Murray 	/* Check that there is no garbage at end of line. */
224019261079SEd Maste 	if (ac > 0) {
224119261079SEd Maste 		error("%.200s line %d: keyword %s extra arguments "
224219261079SEd Maste 		    "at end of line", filename, linenum, keyword);
224319261079SEd Maste 		goto out;
2244c2d3a559SKris Kennaway 	}
224519261079SEd Maste 
224619261079SEd Maste 	/* success */
224719261079SEd Maste 	ret = 0;
224819261079SEd Maste  out:
224919261079SEd Maste 	argv_free(oav, oac);
225019261079SEd Maste 	return ret;
2251511b41d2SMark Murray }
2252511b41d2SMark Murray 
2253511b41d2SMark Murray /*
2254511b41d2SMark Murray  * Reads the config file and modifies the options accordingly.  Options
2255511b41d2SMark Murray  * should already be initialized before this call.  This never returns if
2256af12a3e7SDag-Erling Smørgrav  * there is an error.  If the file does not exist, this returns 0.
2257511b41d2SMark Murray  */
2258af12a3e7SDag-Erling Smørgrav int
2259f7167e0eSDag-Erling Smørgrav read_config_file(const char *filename, struct passwd *pw, const char *host,
226019261079SEd Maste     const char *original_host, Options *options, int flags,
226119261079SEd Maste     int *want_final_pass)
2262511b41d2SMark Murray {
2263076ad2f8SDag-Erling Smørgrav 	int active = 1;
2264076ad2f8SDag-Erling Smørgrav 
2265076ad2f8SDag-Erling Smørgrav 	return read_config_file_depth(filename, pw, host, original_host,
226619261079SEd Maste 	    options, flags, &active, want_final_pass, 0);
2267076ad2f8SDag-Erling Smørgrav }
2268076ad2f8SDag-Erling Smørgrav 
2269076ad2f8SDag-Erling Smørgrav #define READCONF_MAX_DEPTH	16
2270076ad2f8SDag-Erling Smørgrav static int
2271076ad2f8SDag-Erling Smørgrav read_config_file_depth(const char *filename, struct passwd *pw,
2272076ad2f8SDag-Erling Smørgrav     const char *host, const char *original_host, Options *options,
227319261079SEd Maste     int flags, int *activep, int *want_final_pass, int depth)
2274076ad2f8SDag-Erling Smørgrav {
2275511b41d2SMark Murray 	FILE *f;
2276190cef3dSDag-Erling Smørgrav 	char *line = NULL;
2277190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
2278076ad2f8SDag-Erling Smørgrav 	int linenum;
2279511b41d2SMark Murray 	int bad_options = 0;
2280511b41d2SMark Murray 
2281076ad2f8SDag-Erling Smørgrav 	if (depth < 0 || depth > READCONF_MAX_DEPTH)
2282076ad2f8SDag-Erling Smørgrav 		fatal("Too many recursive configuration includes");
2283076ad2f8SDag-Erling Smørgrav 
228421e764dfSDag-Erling Smørgrav 	if ((f = fopen(filename, "r")) == NULL)
2285af12a3e7SDag-Erling Smørgrav 		return 0;
2286511b41d2SMark Murray 
228773370613SDag-Erling Smørgrav 	if (flags & SSHCONF_CHECKPERM) {
228821e764dfSDag-Erling Smørgrav 		struct stat sb;
228921e764dfSDag-Erling Smørgrav 
229021e764dfSDag-Erling Smørgrav 		if (fstat(fileno(f), &sb) == -1)
229121e764dfSDag-Erling Smørgrav 			fatal("fstat %s: %s", filename, strerror(errno));
229221e764dfSDag-Erling Smørgrav 		if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
229321e764dfSDag-Erling Smørgrav 		    (sb.st_mode & 022) != 0))
229421e764dfSDag-Erling Smørgrav 			fatal("Bad owner or permissions on %s", filename);
229521e764dfSDag-Erling Smørgrav 	}
229621e764dfSDag-Erling Smørgrav 
2297511b41d2SMark Murray 	debug("Reading configuration data %.200s", filename);
2298511b41d2SMark Murray 
2299511b41d2SMark Murray 	/*
2300511b41d2SMark Murray 	 * Mark that we are now processing the options.  This flag is turned
2301511b41d2SMark Murray 	 * on/off by Host specifications.
2302511b41d2SMark Murray 	 */
2303511b41d2SMark Murray 	linenum = 0;
2304190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
2305511b41d2SMark Murray 		/* Update line number counter. */
2306511b41d2SMark Murray 		linenum++;
230719261079SEd Maste 		/*
230819261079SEd Maste 		 * Trim out comments and strip whitespace.
230919261079SEd Maste 		 * NB - preserve newlines, they are needed to reproduce
231019261079SEd Maste 		 * line numbers later for error messages.
231119261079SEd Maste 		 */
2312076ad2f8SDag-Erling Smørgrav 		if (process_config_line_depth(options, pw, host, original_host,
231319261079SEd Maste 		    line, filename, linenum, activep, flags, want_final_pass,
231419261079SEd Maste 		    depth) != 0)
2315511b41d2SMark Murray 			bad_options++;
2316511b41d2SMark Murray 	}
2317190cef3dSDag-Erling Smørgrav 	free(line);
2318511b41d2SMark Murray 	fclose(f);
2319511b41d2SMark Murray 	if (bad_options > 0)
2320ca3176e7SBrian Feldman 		fatal("%s: terminating, %d bad configuration options",
2321511b41d2SMark Murray 		    filename, bad_options);
2322af12a3e7SDag-Erling Smørgrav 	return 1;
2323511b41d2SMark Murray }
2324511b41d2SMark Murray 
2325b83788ffSDag-Erling Smørgrav /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
2326b83788ffSDag-Erling Smørgrav int
2327b83788ffSDag-Erling Smørgrav option_clear_or_none(const char *o)
2328b83788ffSDag-Erling Smørgrav {
2329b83788ffSDag-Erling Smørgrav 	return o == NULL || strcasecmp(o, "none") == 0;
2330b83788ffSDag-Erling Smørgrav }
2331b83788ffSDag-Erling Smørgrav 
2332511b41d2SMark Murray /*
2333e9e8876aSEd Maste  * Returns 1 if CanonicalizePermittedCNAMEs have been specified, 0 otherwise.
2334e9e8876aSEd Maste  * Allowed to be called on non-final configuration.
2335e9e8876aSEd Maste  */
2336e9e8876aSEd Maste int
2337e9e8876aSEd Maste config_has_permitted_cnames(Options *options)
2338e9e8876aSEd Maste {
2339e9e8876aSEd Maste 	if (options->num_permitted_cnames == 1 &&
2340e9e8876aSEd Maste 	    strcasecmp(options->permitted_cnames[0].source_list, "none") == 0 &&
2341e9e8876aSEd Maste 	    strcmp(options->permitted_cnames[0].target_list, "") == 0)
2342e9e8876aSEd Maste 		return 0;
2343e9e8876aSEd Maste 	return options->num_permitted_cnames > 0;
2344e9e8876aSEd Maste }
2345e9e8876aSEd Maste 
2346e9e8876aSEd Maste /*
2347511b41d2SMark Murray  * Initializes options to special values that indicate that they have not yet
2348511b41d2SMark Murray  * been set.  Read_config_file will only set options with this value. Options
2349511b41d2SMark Murray  * are processed in the following order: command line, user config file,
2350511b41d2SMark Murray  * system config file.  Last, fill_default_options is called.
2351511b41d2SMark Murray  */
2352511b41d2SMark Murray 
2353511b41d2SMark Murray void
2354511b41d2SMark Murray initialize_options(Options * options)
2355511b41d2SMark Murray {
2356511b41d2SMark Murray 	memset(options, 'X', sizeof(*options));
2357eccfee6eSDag-Erling Smørgrav 	options->version_addendum = NULL;
2358511b41d2SMark Murray 	options->forward_agent = -1;
235919261079SEd Maste 	options->forward_agent_sock_path = NULL;
2360511b41d2SMark Murray 	options->forward_x11 = -1;
23611ec0d754SDag-Erling Smørgrav 	options->forward_x11_trusted = -1;
2362e2f6069cSDag-Erling Smørgrav 	options->forward_x11_timeout = -1;
2363076ad2f8SDag-Erling Smørgrav 	options->stdio_forward_host = NULL;
2364076ad2f8SDag-Erling Smørgrav 	options->stdio_forward_port = 0;
2365076ad2f8SDag-Erling Smørgrav 	options->clear_forwardings = -1;
2366333ee039SDag-Erling Smørgrav 	options->exit_on_forward_failure = -1;
2367c2d3a559SKris Kennaway 	options->xauth_location = NULL;
2368a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.gateway_ports = -1;
2369a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
2370a0ee8cc6SDag-Erling Smørgrav 	options->fwd_opts.streamlocal_bind_unlink = -1;
2371ca3176e7SBrian Feldman 	options->pubkey_authentication = -1;
2372cf2b5f3bSDag-Erling Smørgrav 	options->gss_authentication = -1;
2373cf2b5f3bSDag-Erling Smørgrav 	options->gss_deleg_creds = -1;
2374511b41d2SMark Murray 	options->password_authentication = -1;
237509958426SBrian Feldman 	options->kbd_interactive_authentication = -1;
237609958426SBrian Feldman 	options->kbd_interactive_devices = NULL;
2377ca3176e7SBrian Feldman 	options->hostbased_authentication = -1;
2378511b41d2SMark Murray 	options->batch_mode = -1;
2379511b41d2SMark Murray 	options->check_host_ip = -1;
2380511b41d2SMark Murray 	options->strict_host_key_checking = -1;
2381511b41d2SMark Murray 	options->compression = -1;
23821ec0d754SDag-Erling Smørgrav 	options->tcp_keep_alive = -1;
2383511b41d2SMark Murray 	options->port = -1;
2384cf2b5f3bSDag-Erling Smørgrav 	options->address_family = -1;
2385511b41d2SMark Murray 	options->connection_attempts = -1;
2386cf2b5f3bSDag-Erling Smørgrav 	options->connection_timeout = -1;
2387511b41d2SMark Murray 	options->number_of_password_prompts = -1;
2388e8aafc91SKris Kennaway 	options->ciphers = NULL;
2389ca3176e7SBrian Feldman 	options->macs = NULL;
23904a421b63SDag-Erling Smørgrav 	options->kex_algorithms = NULL;
2391ca3176e7SBrian Feldman 	options->hostkeyalgorithms = NULL;
23922f513db7SEd Maste 	options->ca_sign_algorithms = NULL;
2393511b41d2SMark Murray 	options->num_identity_files = 0;
239419261079SEd Maste 	memset(options->identity_keys, 0, sizeof(options->identity_keys));
2395acc1a9efSDag-Erling Smørgrav 	options->num_certificate_files = 0;
239619261079SEd Maste 	memset(options->certificates, 0, sizeof(options->certificates));
2397511b41d2SMark Murray 	options->hostname = NULL;
2398ca3176e7SBrian Feldman 	options->host_key_alias = NULL;
2399511b41d2SMark Murray 	options->proxy_command = NULL;
2400076ad2f8SDag-Erling Smørgrav 	options->jump_user = NULL;
2401076ad2f8SDag-Erling Smørgrav 	options->jump_host = NULL;
2402076ad2f8SDag-Erling Smørgrav 	options->jump_port = -1;
2403076ad2f8SDag-Erling Smørgrav 	options->jump_extra = NULL;
2404511b41d2SMark Murray 	options->user = NULL;
2405511b41d2SMark Murray 	options->escape_char = -1;
2406e146993eSDag-Erling Smørgrav 	options->num_system_hostfiles = 0;
2407e146993eSDag-Erling Smørgrav 	options->num_user_hostfiles = 0;
2408e2f6069cSDag-Erling Smørgrav 	options->local_forwards = NULL;
2409511b41d2SMark Murray 	options->num_local_forwards = 0;
2410e2f6069cSDag-Erling Smørgrav 	options->remote_forwards = NULL;
2411511b41d2SMark Murray 	options->num_remote_forwards = 0;
241219261079SEd Maste 	options->permitted_remote_opens = NULL;
241319261079SEd Maste 	options->num_permitted_remote_opens = 0;
24144f52dfbbSDag-Erling Smørgrav 	options->log_facility = SYSLOG_FACILITY_NOT_SET;
2415af12a3e7SDag-Erling Smørgrav 	options->log_level = SYSLOG_LEVEL_NOT_SET;
241619261079SEd Maste 	options->num_log_verbose = 0;
241719261079SEd Maste 	options->log_verbose = NULL;
2418ca3176e7SBrian Feldman 	options->preferred_authentications = NULL;
2419af12a3e7SDag-Erling Smørgrav 	options->bind_address = NULL;
242047dd1d1bSDag-Erling Smørgrav 	options->bind_interface = NULL;
2421b15c8340SDag-Erling Smørgrav 	options->pkcs11_provider = NULL;
242219261079SEd Maste 	options->sk_provider = NULL;
2423e73e9afaSDag-Erling Smørgrav 	options->enable_ssh_keysign = - 1;
2424af12a3e7SDag-Erling Smørgrav 	options->no_host_authentication_for_localhost = - 1;
24255962c0e9SDag-Erling Smørgrav 	options->identities_only = - 1;
2426cf2b5f3bSDag-Erling Smørgrav 	options->rekey_limit = - 1;
2427e4a9863fSDag-Erling Smørgrav 	options->rekey_interval = -1;
2428cf2b5f3bSDag-Erling Smørgrav 	options->verify_host_key_dns = -1;
24291ec0d754SDag-Erling Smørgrav 	options->server_alive_interval = -1;
24301ec0d754SDag-Erling Smørgrav 	options->server_alive_count_max = -1;
2431190cef3dSDag-Erling Smørgrav 	options->send_env = NULL;
243221e764dfSDag-Erling Smørgrav 	options->num_send_env = 0;
2433190cef3dSDag-Erling Smørgrav 	options->setenv = NULL;
2434190cef3dSDag-Erling Smørgrav 	options->num_setenv = 0;
243521e764dfSDag-Erling Smørgrav 	options->control_path = NULL;
243621e764dfSDag-Erling Smørgrav 	options->control_master = -1;
2437e2f6069cSDag-Erling Smørgrav 	options->control_persist = -1;
2438e2f6069cSDag-Erling Smørgrav 	options->control_persist_timeout = 0;
2439aa49c926SDag-Erling Smørgrav 	options->hash_known_hosts = -1;
2440b74df5b2SDag-Erling Smørgrav 	options->tun_open = -1;
2441b74df5b2SDag-Erling Smørgrav 	options->tun_local = -1;
2442b74df5b2SDag-Erling Smørgrav 	options->tun_remote = -1;
2443b74df5b2SDag-Erling Smørgrav 	options->local_command = NULL;
2444b74df5b2SDag-Erling Smørgrav 	options->permit_local_command = -1;
24454f52dfbbSDag-Erling Smørgrav 	options->remote_command = NULL;
2446acc1a9efSDag-Erling Smørgrav 	options->add_keys_to_agent = -1;
244719261079SEd Maste 	options->add_keys_to_agent_lifespan = -1;
2448076ad2f8SDag-Erling Smørgrav 	options->identity_agent = NULL;
2449d4af9e69SDag-Erling Smørgrav 	options->visual_host_key = -1;
24504a421b63SDag-Erling Smørgrav 	options->ip_qos_interactive = -1;
24514a421b63SDag-Erling Smørgrav 	options->ip_qos_bulk = -1;
2452e146993eSDag-Erling Smørgrav 	options->request_tty = -1;
245319261079SEd Maste 	options->session_type = -1;
245419261079SEd Maste 	options->stdin_null = -1;
245519261079SEd Maste 	options->fork_after_authentication = -1;
2456f7167e0eSDag-Erling Smørgrav 	options->proxy_use_fdpass = -1;
2457e4a9863fSDag-Erling Smørgrav 	options->ignored_unknown = NULL;
2458f7167e0eSDag-Erling Smørgrav 	options->num_canonical_domains = 0;
2459f7167e0eSDag-Erling Smørgrav 	options->num_permitted_cnames = 0;
2460f7167e0eSDag-Erling Smørgrav 	options->canonicalize_max_dots = -1;
2461f7167e0eSDag-Erling Smørgrav 	options->canonicalize_fallback_local = -1;
2462f7167e0eSDag-Erling Smørgrav 	options->canonicalize_hostname = -1;
2463bc5531deSDag-Erling Smørgrav 	options->revoked_host_keys = NULL;
2464bc5531deSDag-Erling Smørgrav 	options->fingerprint_hash = -1;
2465bc5531deSDag-Erling Smørgrav 	options->update_hostkeys = -1;
246619261079SEd Maste 	options->hostbased_accepted_algos = NULL;
246719261079SEd Maste 	options->pubkey_accepted_algos = NULL;
246819261079SEd Maste 	options->known_hosts_command = NULL;
2469511b41d2SMark Murray }
2470511b41d2SMark Murray 
2471511b41d2SMark Murray /*
2472b83788ffSDag-Erling Smørgrav  * A petite version of fill_default_options() that just fills the options
2473b83788ffSDag-Erling Smørgrav  * needed for hostname canonicalization to proceed.
2474b83788ffSDag-Erling Smørgrav  */
2475b83788ffSDag-Erling Smørgrav void
2476b83788ffSDag-Erling Smørgrav fill_default_options_for_canonicalization(Options *options)
2477b83788ffSDag-Erling Smørgrav {
2478b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_max_dots == -1)
2479b83788ffSDag-Erling Smørgrav 		options->canonicalize_max_dots = 1;
2480b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_fallback_local == -1)
2481b83788ffSDag-Erling Smørgrav 		options->canonicalize_fallback_local = 1;
2482b83788ffSDag-Erling Smørgrav 	if (options->canonicalize_hostname == -1)
2483b83788ffSDag-Erling Smørgrav 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
2484b83788ffSDag-Erling Smørgrav }
2485b83788ffSDag-Erling Smørgrav 
2486b83788ffSDag-Erling Smørgrav /*
2487511b41d2SMark Murray  * Called after processing other sources of option data, this fills those
2488511b41d2SMark Murray  * options for which no value has been specified with their default values.
2489511b41d2SMark Murray  */
249019261079SEd Maste int
2491511b41d2SMark Murray fill_default_options(Options * options)
2492511b41d2SMark Murray {
24932f513db7SEd Maste 	char *all_cipher, *all_mac, *all_kex, *all_key, *all_sig;
249419261079SEd Maste 	char *def_cipher, *def_mac, *def_kex, *def_key, *def_sig;
249519261079SEd Maste 	int ret = 0, r;
2496190cef3dSDag-Erling Smørgrav 
2497511b41d2SMark Murray 	if (options->forward_agent == -1)
2498db1cb46cSKris Kennaway 		options->forward_agent = 0;
2499511b41d2SMark Murray 	if (options->forward_x11 == -1)
25005dc73ebeSBrian Feldman 		options->forward_x11 = 0;
25011ec0d754SDag-Erling Smørgrav 	if (options->forward_x11_trusted == -1)
25021ec0d754SDag-Erling Smørgrav 		options->forward_x11_trusted = 0;
2503e2f6069cSDag-Erling Smørgrav 	if (options->forward_x11_timeout == -1)
2504e2f6069cSDag-Erling Smørgrav 		options->forward_x11_timeout = 1200;
2505076ad2f8SDag-Erling Smørgrav 	/*
2506076ad2f8SDag-Erling Smørgrav 	 * stdio forwarding (-W) changes the default for these but we defer
2507076ad2f8SDag-Erling Smørgrav 	 * setting the values so they can be overridden.
2508076ad2f8SDag-Erling Smørgrav 	 */
2509333ee039SDag-Erling Smørgrav 	if (options->exit_on_forward_failure == -1)
2510076ad2f8SDag-Erling Smørgrav 		options->exit_on_forward_failure =
2511076ad2f8SDag-Erling Smørgrav 		    options->stdio_forward_host != NULL ? 1 : 0;
2512076ad2f8SDag-Erling Smørgrav 	if (options->clear_forwardings == -1)
2513076ad2f8SDag-Erling Smørgrav 		options->clear_forwardings =
2514076ad2f8SDag-Erling Smørgrav 		    options->stdio_forward_host != NULL ? 1 : 0;
2515076ad2f8SDag-Erling Smørgrav 	if (options->clear_forwardings == 1)
2516076ad2f8SDag-Erling Smørgrav 		clear_forwardings(options);
2517076ad2f8SDag-Erling Smørgrav 
2518c2d3a559SKris Kennaway 	if (options->xauth_location == NULL)
251919261079SEd Maste 		options->xauth_location = xstrdup(_PATH_XAUTH);
2520a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.gateway_ports == -1)
2521a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.gateway_ports = 0;
2522a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
2523a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_mask = 0177;
2524a0ee8cc6SDag-Erling Smørgrav 	if (options->fwd_opts.streamlocal_bind_unlink == -1)
2525a0ee8cc6SDag-Erling Smørgrav 		options->fwd_opts.streamlocal_bind_unlink = 0;
2526ca3176e7SBrian Feldman 	if (options->pubkey_authentication == -1)
2527*1323ec57SEd Maste 		options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
2528cf2b5f3bSDag-Erling Smørgrav 	if (options->gss_authentication == -1)
25291ec0d754SDag-Erling Smørgrav 		options->gss_authentication = 0;
2530cf2b5f3bSDag-Erling Smørgrav 	if (options->gss_deleg_creds == -1)
2531cf2b5f3bSDag-Erling Smørgrav 		options->gss_deleg_creds = 0;
2532511b41d2SMark Murray 	if (options->password_authentication == -1)
2533511b41d2SMark Murray 		options->password_authentication = 1;
253409958426SBrian Feldman 	if (options->kbd_interactive_authentication == -1)
2535ca3176e7SBrian Feldman 		options->kbd_interactive_authentication = 1;
2536ca3176e7SBrian Feldman 	if (options->hostbased_authentication == -1)
2537ca3176e7SBrian Feldman 		options->hostbased_authentication = 0;
2538511b41d2SMark Murray 	if (options->batch_mode == -1)
2539511b41d2SMark Murray 		options->batch_mode = 0;
2540511b41d2SMark Murray 	if (options->check_host_ip == -1)
2541975616f0SDag-Erling Smørgrav 		options->check_host_ip = 0;
2542511b41d2SMark Murray 	if (options->strict_host_key_checking == -1)
25434f52dfbbSDag-Erling Smørgrav 		options->strict_host_key_checking = SSH_STRICT_HOSTKEY_ASK;
2544511b41d2SMark Murray 	if (options->compression == -1)
2545511b41d2SMark Murray 		options->compression = 0;
25461ec0d754SDag-Erling Smørgrav 	if (options->tcp_keep_alive == -1)
25471ec0d754SDag-Erling Smørgrav 		options->tcp_keep_alive = 1;
2548511b41d2SMark Murray 	if (options->port == -1)
2549511b41d2SMark Murray 		options->port = 0;	/* Filled in ssh_connect. */
2550cf2b5f3bSDag-Erling Smørgrav 	if (options->address_family == -1)
2551cf2b5f3bSDag-Erling Smørgrav 		options->address_family = AF_UNSPEC;
2552511b41d2SMark Murray 	if (options->connection_attempts == -1)
2553af12a3e7SDag-Erling Smørgrav 		options->connection_attempts = 1;
2554511b41d2SMark Murray 	if (options->number_of_password_prompts == -1)
2555511b41d2SMark Murray 		options->number_of_password_prompts = 3;
2556ca3176e7SBrian Feldman 	/* options->hostkeyalgorithms, default set in myproposals.h */
255719261079SEd Maste 	if (options->add_keys_to_agent == -1) {
2558acc1a9efSDag-Erling Smørgrav 		options->add_keys_to_agent = 0;
255919261079SEd Maste 		options->add_keys_to_agent_lifespan = 0;
256019261079SEd Maste 	}
2561511b41d2SMark Murray 	if (options->num_identity_files == 0) {
25624f52dfbbSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0);
25634a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
25644f52dfbbSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0);
256519261079SEd Maste 		add_identity_file(options, "~/",
256619261079SEd Maste 		    _PATH_SSH_CLIENT_ID_ECDSA_SK, 0);
25674a421b63SDag-Erling Smørgrav #endif
2568f7167e0eSDag-Erling Smørgrav 		add_identity_file(options, "~/",
2569f7167e0eSDag-Erling Smørgrav 		    _PATH_SSH_CLIENT_ID_ED25519, 0);
257019261079SEd Maste 		add_identity_file(options, "~/",
257119261079SEd Maste 		    _PATH_SSH_CLIENT_ID_ED25519_SK, 0);
257247dd1d1bSDag-Erling Smørgrav 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0);
2573*1323ec57SEd Maste 		add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0);
2574ca3176e7SBrian Feldman 	}
2575511b41d2SMark Murray 	if (options->escape_char == -1)
2576511b41d2SMark Murray 		options->escape_char = '~';
2577e146993eSDag-Erling Smørgrav 	if (options->num_system_hostfiles == 0) {
2578e146993eSDag-Erling Smørgrav 		options->system_hostfiles[options->num_system_hostfiles++] =
2579e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
2580e146993eSDag-Erling Smørgrav 		options->system_hostfiles[options->num_system_hostfiles++] =
2581e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
2582e146993eSDag-Erling Smørgrav 	}
258319261079SEd Maste 	if (options->update_hostkeys == -1) {
258419261079SEd Maste 		if (options->verify_host_key_dns <= 0 &&
258519261079SEd Maste 		    (options->num_user_hostfiles == 0 ||
258619261079SEd Maste 		    (options->num_user_hostfiles == 1 && strcmp(options->
258719261079SEd Maste 		    user_hostfiles[0], _PATH_SSH_USER_HOSTFILE) == 0)))
258819261079SEd Maste 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_YES;
258919261079SEd Maste 		else
259019261079SEd Maste 			options->update_hostkeys = SSH_UPDATE_HOSTKEYS_NO;
259119261079SEd Maste 	}
2592e146993eSDag-Erling Smørgrav 	if (options->num_user_hostfiles == 0) {
2593e146993eSDag-Erling Smørgrav 		options->user_hostfiles[options->num_user_hostfiles++] =
2594e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_USER_HOSTFILE);
2595e146993eSDag-Erling Smørgrav 		options->user_hostfiles[options->num_user_hostfiles++] =
2596e146993eSDag-Erling Smørgrav 		    xstrdup(_PATH_SSH_USER_HOSTFILE2);
2597e146993eSDag-Erling Smørgrav 	}
2598af12a3e7SDag-Erling Smørgrav 	if (options->log_level == SYSLOG_LEVEL_NOT_SET)
2599511b41d2SMark Murray 		options->log_level = SYSLOG_LEVEL_INFO;
26004f52dfbbSDag-Erling Smørgrav 	if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
26014f52dfbbSDag-Erling Smørgrav 		options->log_facility = SYSLOG_FACILITY_USER;
2602af12a3e7SDag-Erling Smørgrav 	if (options->no_host_authentication_for_localhost == - 1)
2603af12a3e7SDag-Erling Smørgrav 		options->no_host_authentication_for_localhost = 0;
26045962c0e9SDag-Erling Smørgrav 	if (options->identities_only == -1)
26055962c0e9SDag-Erling Smørgrav 		options->identities_only = 0;
2606e73e9afaSDag-Erling Smørgrav 	if (options->enable_ssh_keysign == -1)
2607e73e9afaSDag-Erling Smørgrav 		options->enable_ssh_keysign = 0;
2608cf2b5f3bSDag-Erling Smørgrav 	if (options->rekey_limit == -1)
2609cf2b5f3bSDag-Erling Smørgrav 		options->rekey_limit = 0;
2610e4a9863fSDag-Erling Smørgrav 	if (options->rekey_interval == -1)
2611e4a9863fSDag-Erling Smørgrav 		options->rekey_interval = 0;
261283c6a524SDag-Erling Smørgrav #if HAVE_LDNS
261383c6a524SDag-Erling Smørgrav 	if (options->verify_host_key_dns == -1)
261483c6a524SDag-Erling Smørgrav 		/* automatically trust a verified SSHFP record */
261583c6a524SDag-Erling Smørgrav 		options->verify_host_key_dns = 1;
261683c6a524SDag-Erling Smørgrav #else
2617cf2b5f3bSDag-Erling Smørgrav 	if (options->verify_host_key_dns == -1)
2618cf2b5f3bSDag-Erling Smørgrav 		options->verify_host_key_dns = 0;
261983c6a524SDag-Erling Smørgrav #endif
26201ec0d754SDag-Erling Smørgrav 	if (options->server_alive_interval == -1)
26211ec0d754SDag-Erling Smørgrav 		options->server_alive_interval = 0;
26221ec0d754SDag-Erling Smørgrav 	if (options->server_alive_count_max == -1)
26231ec0d754SDag-Erling Smørgrav 		options->server_alive_count_max = 3;
262421e764dfSDag-Erling Smørgrav 	if (options->control_master == -1)
262521e764dfSDag-Erling Smørgrav 		options->control_master = 0;
2626e2f6069cSDag-Erling Smørgrav 	if (options->control_persist == -1) {
2627e2f6069cSDag-Erling Smørgrav 		options->control_persist = 0;
2628e2f6069cSDag-Erling Smørgrav 		options->control_persist_timeout = 0;
2629e2f6069cSDag-Erling Smørgrav 	}
2630aa49c926SDag-Erling Smørgrav 	if (options->hash_known_hosts == -1)
2631aa49c926SDag-Erling Smørgrav 		options->hash_known_hosts = 0;
2632b74df5b2SDag-Erling Smørgrav 	if (options->tun_open == -1)
2633b74df5b2SDag-Erling Smørgrav 		options->tun_open = SSH_TUNMODE_NO;
2634b74df5b2SDag-Erling Smørgrav 	if (options->tun_local == -1)
2635b74df5b2SDag-Erling Smørgrav 		options->tun_local = SSH_TUNID_ANY;
2636b74df5b2SDag-Erling Smørgrav 	if (options->tun_remote == -1)
2637b74df5b2SDag-Erling Smørgrav 		options->tun_remote = SSH_TUNID_ANY;
2638b74df5b2SDag-Erling Smørgrav 	if (options->permit_local_command == -1)
2639b74df5b2SDag-Erling Smørgrav 		options->permit_local_command = 0;
2640d4af9e69SDag-Erling Smørgrav 	if (options->visual_host_key == -1)
2641d4af9e69SDag-Erling Smørgrav 		options->visual_host_key = 0;
26424a421b63SDag-Erling Smørgrav 	if (options->ip_qos_interactive == -1)
2643190cef3dSDag-Erling Smørgrav 		options->ip_qos_interactive = IPTOS_DSCP_AF21;
26444a421b63SDag-Erling Smørgrav 	if (options->ip_qos_bulk == -1)
2645190cef3dSDag-Erling Smørgrav 		options->ip_qos_bulk = IPTOS_DSCP_CS1;
2646e146993eSDag-Erling Smørgrav 	if (options->request_tty == -1)
2647e146993eSDag-Erling Smørgrav 		options->request_tty = REQUEST_TTY_AUTO;
264819261079SEd Maste 	if (options->session_type == -1)
264919261079SEd Maste 		options->session_type = SESSION_TYPE_DEFAULT;
265019261079SEd Maste 	if (options->stdin_null == -1)
265119261079SEd Maste 		options->stdin_null = 0;
265219261079SEd Maste 	if (options->fork_after_authentication == -1)
265319261079SEd Maste 		options->fork_after_authentication = 0;
2654f7167e0eSDag-Erling Smørgrav 	if (options->proxy_use_fdpass == -1)
2655f7167e0eSDag-Erling Smørgrav 		options->proxy_use_fdpass = 0;
2656f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_max_dots == -1)
2657f7167e0eSDag-Erling Smørgrav 		options->canonicalize_max_dots = 1;
2658f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_fallback_local == -1)
2659f7167e0eSDag-Erling Smørgrav 		options->canonicalize_fallback_local = 1;
2660f7167e0eSDag-Erling Smørgrav 	if (options->canonicalize_hostname == -1)
2661f7167e0eSDag-Erling Smørgrav 		options->canonicalize_hostname = SSH_CANONICALISE_NO;
2662bc5531deSDag-Erling Smørgrav 	if (options->fingerprint_hash == -1)
2663bc5531deSDag-Erling Smørgrav 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
266419261079SEd Maste #ifdef ENABLE_SK_INTERNAL
266519261079SEd Maste 	if (options->sk_provider == NULL)
266619261079SEd Maste 		options->sk_provider = xstrdup("internal");
266719261079SEd Maste #else
266819261079SEd Maste 	if (options->sk_provider == NULL)
266919261079SEd Maste 		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
267019261079SEd Maste #endif
2671190cef3dSDag-Erling Smørgrav 
2672190cef3dSDag-Erling Smørgrav 	/* Expand KEX name lists */
2673190cef3dSDag-Erling Smørgrav 	all_cipher = cipher_alg_list(',', 0);
2674190cef3dSDag-Erling Smørgrav 	all_mac = mac_alg_list(',');
2675190cef3dSDag-Erling Smørgrav 	all_kex = kex_alg_list(',');
2676190cef3dSDag-Erling Smørgrav 	all_key = sshkey_alg_list(0, 0, 1, ',');
26772f513db7SEd Maste 	all_sig = sshkey_alg_list(0, 1, 1, ',');
267819261079SEd Maste 	/* remove unsupported algos from default lists */
267919261079SEd Maste 	def_cipher = match_filter_allowlist(KEX_CLIENT_ENCRYPT, all_cipher);
268019261079SEd Maste 	def_mac = match_filter_allowlist(KEX_CLIENT_MAC, all_mac);
268119261079SEd Maste 	def_kex = match_filter_allowlist(KEX_CLIENT_KEX, all_kex);
268219261079SEd Maste 	def_key = match_filter_allowlist(KEX_DEFAULT_PK_ALG, all_key);
268319261079SEd Maste 	def_sig = match_filter_allowlist(SSH_ALLOWED_CA_SIGALGS, all_sig);
2684190cef3dSDag-Erling Smørgrav #define ASSEMBLE(what, defaults, all) \
2685190cef3dSDag-Erling Smørgrav 	do { \
2686190cef3dSDag-Erling Smørgrav 		if ((r = kex_assemble_names(&options->what, \
268719261079SEd Maste 		    defaults, all)) != 0) { \
268819261079SEd Maste 			error_fr(r, "%s", #what); \
268919261079SEd Maste 			goto fail; \
269019261079SEd Maste 		} \
2691190cef3dSDag-Erling Smørgrav 	} while (0)
269219261079SEd Maste 	ASSEMBLE(ciphers, def_cipher, all_cipher);
269319261079SEd Maste 	ASSEMBLE(macs, def_mac, all_mac);
269419261079SEd Maste 	ASSEMBLE(kex_algorithms, def_kex, all_kex);
269519261079SEd Maste 	ASSEMBLE(hostbased_accepted_algos, def_key, all_key);
269619261079SEd Maste 	ASSEMBLE(pubkey_accepted_algos, def_key, all_key);
269719261079SEd Maste 	ASSEMBLE(ca_sign_algorithms, def_sig, all_sig);
2698190cef3dSDag-Erling Smørgrav #undef ASSEMBLE
2699bc5531deSDag-Erling Smørgrav 
2700f7167e0eSDag-Erling Smørgrav #define CLEAR_ON_NONE(v) \
2701f7167e0eSDag-Erling Smørgrav 	do { \
2702b83788ffSDag-Erling Smørgrav 		if (option_clear_or_none(v)) { \
2703f7167e0eSDag-Erling Smørgrav 			free(v); \
2704f7167e0eSDag-Erling Smørgrav 			v = NULL; \
2705f7167e0eSDag-Erling Smørgrav 		} \
2706f7167e0eSDag-Erling Smørgrav 	} while(0)
2707f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->local_command);
27084f52dfbbSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->remote_command);
2709f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->proxy_command);
2710f7167e0eSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->control_path);
2711bc5531deSDag-Erling Smørgrav 	CLEAR_ON_NONE(options->revoked_host_keys);
271219261079SEd Maste 	CLEAR_ON_NONE(options->pkcs11_provider);
271319261079SEd Maste 	CLEAR_ON_NONE(options->sk_provider);
271419261079SEd Maste 	CLEAR_ON_NONE(options->known_hosts_command);
2715190cef3dSDag-Erling Smørgrav 	if (options->jump_host != NULL &&
2716190cef3dSDag-Erling Smørgrav 	    strcmp(options->jump_host, "none") == 0 &&
2717190cef3dSDag-Erling Smørgrav 	    options->jump_port == 0 && options->jump_user == NULL) {
2718190cef3dSDag-Erling Smørgrav 		free(options->jump_host);
2719190cef3dSDag-Erling Smørgrav 		options->jump_host = NULL;
2720190cef3dSDag-Erling Smørgrav 	}
2721e9e8876aSEd Maste 	if (options->num_permitted_cnames == 1 &&
2722e9e8876aSEd Maste 	    !config_has_permitted_cnames(options)) {
2723e9e8876aSEd Maste 		/* clean up CanonicalizePermittedCNAMEs=none */
2724e9e8876aSEd Maste 		free(options->permitted_cnames[0].source_list);
2725e9e8876aSEd Maste 		free(options->permitted_cnames[0].target_list);
2726e9e8876aSEd Maste 		memset(options->permitted_cnames, '\0',
2727e9e8876aSEd Maste 		    sizeof(*options->permitted_cnames));
2728e9e8876aSEd Maste 		options->num_permitted_cnames = 0;
2729e9e8876aSEd Maste 	}
2730076ad2f8SDag-Erling Smørgrav 	/* options->identity_agent distinguishes NULL from 'none' */
2731511b41d2SMark Murray 	/* options->user will be set in the main program if appropriate */
2732511b41d2SMark Murray 	/* options->hostname will be set in the main program if appropriate */
2733ca3176e7SBrian Feldman 	/* options->host_key_alias should not be set by default */
2734ca3176e7SBrian Feldman 	/* options->preferred_authentications will be set in ssh */
2735462c32cbSDag-Erling Smørgrav 	if (options->version_addendum == NULL)
2736462c32cbSDag-Erling Smørgrav 		options->version_addendum = xstrdup(SSH_VERSION_FREEBSD);
273719261079SEd Maste 
273819261079SEd Maste 	/* success */
273919261079SEd Maste 	ret = 0;
274019261079SEd Maste  fail:
274119261079SEd Maste 	free(all_cipher);
274219261079SEd Maste 	free(all_mac);
274319261079SEd Maste 	free(all_kex);
274419261079SEd Maste 	free(all_key);
274519261079SEd Maste 	free(all_sig);
274619261079SEd Maste 	free(def_cipher);
274719261079SEd Maste 	free(def_mac);
274819261079SEd Maste 	free(def_kex);
274919261079SEd Maste 	free(def_key);
275019261079SEd Maste 	free(def_sig);
275119261079SEd Maste 	return ret;
275219261079SEd Maste }
275319261079SEd Maste 
275419261079SEd Maste void
275519261079SEd Maste free_options(Options *o)
275619261079SEd Maste {
275719261079SEd Maste 	int i;
275819261079SEd Maste 
275919261079SEd Maste 	if (o == NULL)
276019261079SEd Maste 		return;
276119261079SEd Maste 
276219261079SEd Maste #define FREE_ARRAY(type, n, a) \
276319261079SEd Maste 	do { \
276419261079SEd Maste 		type _i; \
276519261079SEd Maste 		for (_i = 0; _i < (n); _i++) \
276619261079SEd Maste 			free((a)[_i]); \
276719261079SEd Maste 	} while (0)
276819261079SEd Maste 
276919261079SEd Maste 	free(o->forward_agent_sock_path);
277019261079SEd Maste 	free(o->xauth_location);
277119261079SEd Maste 	FREE_ARRAY(u_int, o->num_log_verbose, o->log_verbose);
277219261079SEd Maste 	free(o->log_verbose);
277319261079SEd Maste 	free(o->ciphers);
277419261079SEd Maste 	free(o->macs);
277519261079SEd Maste 	free(o->hostkeyalgorithms);
277619261079SEd Maste 	free(o->kex_algorithms);
277719261079SEd Maste 	free(o->ca_sign_algorithms);
277819261079SEd Maste 	free(o->hostname);
277919261079SEd Maste 	free(o->host_key_alias);
278019261079SEd Maste 	free(o->proxy_command);
278119261079SEd Maste 	free(o->user);
278219261079SEd Maste 	FREE_ARRAY(u_int, o->num_system_hostfiles, o->system_hostfiles);
278319261079SEd Maste 	FREE_ARRAY(u_int, o->num_user_hostfiles, o->user_hostfiles);
278419261079SEd Maste 	free(o->preferred_authentications);
278519261079SEd Maste 	free(o->bind_address);
278619261079SEd Maste 	free(o->bind_interface);
278719261079SEd Maste 	free(o->pkcs11_provider);
278819261079SEd Maste 	free(o->sk_provider);
278919261079SEd Maste 	for (i = 0; i < o->num_identity_files; i++) {
279019261079SEd Maste 		free(o->identity_files[i]);
279119261079SEd Maste 		sshkey_free(o->identity_keys[i]);
279219261079SEd Maste 	}
279319261079SEd Maste 	for (i = 0; i < o->num_certificate_files; i++) {
279419261079SEd Maste 		free(o->certificate_files[i]);
279519261079SEd Maste 		sshkey_free(o->certificates[i]);
279619261079SEd Maste 	}
279719261079SEd Maste 	free(o->identity_agent);
279819261079SEd Maste 	for (i = 0; i < o->num_local_forwards; i++) {
279919261079SEd Maste 		free(o->local_forwards[i].listen_host);
280019261079SEd Maste 		free(o->local_forwards[i].listen_path);
280119261079SEd Maste 		free(o->local_forwards[i].connect_host);
280219261079SEd Maste 		free(o->local_forwards[i].connect_path);
280319261079SEd Maste 	}
280419261079SEd Maste 	free(o->local_forwards);
280519261079SEd Maste 	for (i = 0; i < o->num_remote_forwards; i++) {
280619261079SEd Maste 		free(o->remote_forwards[i].listen_host);
280719261079SEd Maste 		free(o->remote_forwards[i].listen_path);
280819261079SEd Maste 		free(o->remote_forwards[i].connect_host);
280919261079SEd Maste 		free(o->remote_forwards[i].connect_path);
281019261079SEd Maste 	}
281119261079SEd Maste 	free(o->remote_forwards);
281219261079SEd Maste 	free(o->stdio_forward_host);
281319261079SEd Maste 	FREE_ARRAY(int, o->num_send_env, o->send_env);
281419261079SEd Maste 	free(o->send_env);
281519261079SEd Maste 	FREE_ARRAY(int, o->num_setenv, o->setenv);
281619261079SEd Maste 	free(o->setenv);
281719261079SEd Maste 	free(o->control_path);
281819261079SEd Maste 	free(o->local_command);
281919261079SEd Maste 	free(o->remote_command);
282019261079SEd Maste 	FREE_ARRAY(int, o->num_canonical_domains, o->canonical_domains);
282119261079SEd Maste 	for (i = 0; i < o->num_permitted_cnames; i++) {
282219261079SEd Maste 		free(o->permitted_cnames[i].source_list);
282319261079SEd Maste 		free(o->permitted_cnames[i].target_list);
282419261079SEd Maste 	}
282519261079SEd Maste 	free(o->revoked_host_keys);
282619261079SEd Maste 	free(o->hostbased_accepted_algos);
282719261079SEd Maste 	free(o->pubkey_accepted_algos);
282819261079SEd Maste 	free(o->jump_user);
282919261079SEd Maste 	free(o->jump_host);
283019261079SEd Maste 	free(o->jump_extra);
283119261079SEd Maste 	free(o->ignored_unknown);
283219261079SEd Maste 	explicit_bzero(o, sizeof(*o));
283319261079SEd Maste #undef FREE_ARRAY
2834511b41d2SMark Murray }
2835aa49c926SDag-Erling Smørgrav 
2836a0ee8cc6SDag-Erling Smørgrav struct fwdarg {
2837a0ee8cc6SDag-Erling Smørgrav 	char *arg;
2838a0ee8cc6SDag-Erling Smørgrav 	int ispath;
2839a0ee8cc6SDag-Erling Smørgrav };
2840a0ee8cc6SDag-Erling Smørgrav 
2841a0ee8cc6SDag-Erling Smørgrav /*
2842a0ee8cc6SDag-Erling Smørgrav  * parse_fwd_field
2843a0ee8cc6SDag-Erling Smørgrav  * parses the next field in a port forwarding specification.
2844a0ee8cc6SDag-Erling Smørgrav  * sets fwd to the parsed field and advances p past the colon
2845a0ee8cc6SDag-Erling Smørgrav  * or sets it to NULL at end of string.
2846a0ee8cc6SDag-Erling Smørgrav  * returns 0 on success, else non-zero.
2847a0ee8cc6SDag-Erling Smørgrav  */
2848a0ee8cc6SDag-Erling Smørgrav static int
2849a0ee8cc6SDag-Erling Smørgrav parse_fwd_field(char **p, struct fwdarg *fwd)
2850a0ee8cc6SDag-Erling Smørgrav {
2851a0ee8cc6SDag-Erling Smørgrav 	char *ep, *cp = *p;
2852a0ee8cc6SDag-Erling Smørgrav 	int ispath = 0;
2853a0ee8cc6SDag-Erling Smørgrav 
2854a0ee8cc6SDag-Erling Smørgrav 	if (*cp == '\0') {
2855a0ee8cc6SDag-Erling Smørgrav 		*p = NULL;
2856a0ee8cc6SDag-Erling Smørgrav 		return -1;	/* end of string */
2857a0ee8cc6SDag-Erling Smørgrav 	}
2858a0ee8cc6SDag-Erling Smørgrav 
2859a0ee8cc6SDag-Erling Smørgrav 	/*
2860a0ee8cc6SDag-Erling Smørgrav 	 * A field escaped with square brackets is used literally.
2861a0ee8cc6SDag-Erling Smørgrav 	 * XXX - allow ']' to be escaped via backslash?
2862a0ee8cc6SDag-Erling Smørgrav 	 */
2863a0ee8cc6SDag-Erling Smørgrav 	if (*cp == '[') {
2864a0ee8cc6SDag-Erling Smørgrav 		/* find matching ']' */
2865a0ee8cc6SDag-Erling Smørgrav 		for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) {
2866a0ee8cc6SDag-Erling Smørgrav 			if (*ep == '/')
2867a0ee8cc6SDag-Erling Smørgrav 				ispath = 1;
2868a0ee8cc6SDag-Erling Smørgrav 		}
2869a0ee8cc6SDag-Erling Smørgrav 		/* no matching ']' or not at end of field. */
2870a0ee8cc6SDag-Erling Smørgrav 		if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0'))
2871a0ee8cc6SDag-Erling Smørgrav 			return -1;
2872a0ee8cc6SDag-Erling Smørgrav 		/* NUL terminate the field and advance p past the colon */
2873a0ee8cc6SDag-Erling Smørgrav 		*ep++ = '\0';
2874a0ee8cc6SDag-Erling Smørgrav 		if (*ep != '\0')
2875a0ee8cc6SDag-Erling Smørgrav 			*ep++ = '\0';
2876a0ee8cc6SDag-Erling Smørgrav 		fwd->arg = cp + 1;
2877a0ee8cc6SDag-Erling Smørgrav 		fwd->ispath = ispath;
2878a0ee8cc6SDag-Erling Smørgrav 		*p = ep;
2879a0ee8cc6SDag-Erling Smørgrav 		return 0;
2880a0ee8cc6SDag-Erling Smørgrav 	}
2881a0ee8cc6SDag-Erling Smørgrav 
2882a0ee8cc6SDag-Erling Smørgrav 	for (cp = *p; *cp != '\0'; cp++) {
2883a0ee8cc6SDag-Erling Smørgrav 		switch (*cp) {
2884a0ee8cc6SDag-Erling Smørgrav 		case '\\':
2885a0ee8cc6SDag-Erling Smørgrav 			memmove(cp, cp + 1, strlen(cp + 1) + 1);
2886557f75e5SDag-Erling Smørgrav 			if (*cp == '\0')
2887557f75e5SDag-Erling Smørgrav 				return -1;
2888a0ee8cc6SDag-Erling Smørgrav 			break;
2889a0ee8cc6SDag-Erling Smørgrav 		case '/':
2890a0ee8cc6SDag-Erling Smørgrav 			ispath = 1;
2891a0ee8cc6SDag-Erling Smørgrav 			break;
2892a0ee8cc6SDag-Erling Smørgrav 		case ':':
2893a0ee8cc6SDag-Erling Smørgrav 			*cp++ = '\0';
2894a0ee8cc6SDag-Erling Smørgrav 			goto done;
2895a0ee8cc6SDag-Erling Smørgrav 		}
2896a0ee8cc6SDag-Erling Smørgrav 	}
2897a0ee8cc6SDag-Erling Smørgrav done:
2898a0ee8cc6SDag-Erling Smørgrav 	fwd->arg = *p;
2899a0ee8cc6SDag-Erling Smørgrav 	fwd->ispath = ispath;
2900a0ee8cc6SDag-Erling Smørgrav 	*p = cp;
2901a0ee8cc6SDag-Erling Smørgrav 	return 0;
2902a0ee8cc6SDag-Erling Smørgrav }
2903a0ee8cc6SDag-Erling Smørgrav 
2904aa49c926SDag-Erling Smørgrav /*
2905aa49c926SDag-Erling Smørgrav  * parse_forward
2906aa49c926SDag-Erling Smørgrav  * parses a string containing a port forwarding specification of the form:
2907cce7d346SDag-Erling Smørgrav  *   dynamicfwd == 0
2908a0ee8cc6SDag-Erling Smørgrav  *	[listenhost:]listenport|listenpath:connecthost:connectport|connectpath
2909a0ee8cc6SDag-Erling Smørgrav  *	listenpath:connectpath
2910cce7d346SDag-Erling Smørgrav  *   dynamicfwd == 1
2911cce7d346SDag-Erling Smørgrav  *	[listenhost:]listenport
2912aa49c926SDag-Erling Smørgrav  * returns number of arguments parsed or zero on error
2913aa49c926SDag-Erling Smørgrav  */
2914aa49c926SDag-Erling Smørgrav int
2915a0ee8cc6SDag-Erling Smørgrav parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
2916aa49c926SDag-Erling Smørgrav {
2917a0ee8cc6SDag-Erling Smørgrav 	struct fwdarg fwdargs[4];
2918a0ee8cc6SDag-Erling Smørgrav 	char *p, *cp;
291919261079SEd Maste 	int i, err;
2920aa49c926SDag-Erling Smørgrav 
2921a0ee8cc6SDag-Erling Smørgrav 	memset(fwd, 0, sizeof(*fwd));
2922a0ee8cc6SDag-Erling Smørgrav 	memset(fwdargs, 0, sizeof(fwdargs));
2923aa49c926SDag-Erling Smørgrav 
292419261079SEd Maste 	/*
292519261079SEd Maste 	 * We expand environment variables before checking if we think they're
292619261079SEd Maste 	 * paths so that if ${VAR} expands to a fully qualified path it is
292719261079SEd Maste 	 * treated as a path.
292819261079SEd Maste 	 */
292919261079SEd Maste 	cp = p = dollar_expand(&err, fwdspec);
293019261079SEd Maste 	if (p == NULL || err)
293119261079SEd Maste 		return 0;
2932aa49c926SDag-Erling Smørgrav 
2933aa49c926SDag-Erling Smørgrav 	/* skip leading spaces */
2934f7167e0eSDag-Erling Smørgrav 	while (isspace((u_char)*cp))
2935aa49c926SDag-Erling Smørgrav 		cp++;
2936aa49c926SDag-Erling Smørgrav 
2937a0ee8cc6SDag-Erling Smørgrav 	for (i = 0; i < 4; ++i) {
2938a0ee8cc6SDag-Erling Smørgrav 		if (parse_fwd_field(&cp, &fwdargs[i]) != 0)
2939aa49c926SDag-Erling Smørgrav 			break;
2940a0ee8cc6SDag-Erling Smørgrav 	}
2941aa49c926SDag-Erling Smørgrav 
2942cce7d346SDag-Erling Smørgrav 	/* Check for trailing garbage */
2943a0ee8cc6SDag-Erling Smørgrav 	if (cp != NULL && *cp != '\0') {
2944aa49c926SDag-Erling Smørgrav 		i = 0;	/* failure */
2945a0ee8cc6SDag-Erling Smørgrav 	}
2946aa49c926SDag-Erling Smørgrav 
2947aa49c926SDag-Erling Smørgrav 	switch (i) {
2948cce7d346SDag-Erling Smørgrav 	case 1:
2949a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath) {
2950a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
2951a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
2952a0ee8cc6SDag-Erling Smørgrav 		} else {
2953cce7d346SDag-Erling Smørgrav 			fwd->listen_host = NULL;
2954a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
2955a0ee8cc6SDag-Erling Smørgrav 		}
2956cce7d346SDag-Erling Smørgrav 		fwd->connect_host = xstrdup("socks");
2957cce7d346SDag-Erling Smørgrav 		break;
2958cce7d346SDag-Erling Smørgrav 
2959cce7d346SDag-Erling Smørgrav 	case 2:
2960a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath && fwdargs[1].ispath) {
2961a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
2962a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
2963a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[1].arg);
2964a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
2965a0ee8cc6SDag-Erling Smørgrav 		} else if (fwdargs[1].ispath) {
2966a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = NULL;
2967a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
2968a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[1].arg);
2969a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
2970a0ee8cc6SDag-Erling Smørgrav 		} else {
2971a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = xstrdup(fwdargs[0].arg);
2972a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[1].arg);
2973cce7d346SDag-Erling Smørgrav 			fwd->connect_host = xstrdup("socks");
2974a0ee8cc6SDag-Erling Smørgrav 		}
2975cce7d346SDag-Erling Smørgrav 		break;
2976cce7d346SDag-Erling Smørgrav 
2977aa49c926SDag-Erling Smørgrav 	case 3:
2978a0ee8cc6SDag-Erling Smørgrav 		if (fwdargs[0].ispath) {
2979a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_path = xstrdup(fwdargs[0].arg);
2980a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = PORT_STREAMLOCAL;
2981a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_host = xstrdup(fwdargs[1].arg);
2982a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = a2port(fwdargs[2].arg);
2983a0ee8cc6SDag-Erling Smørgrav 		} else if (fwdargs[2].ispath) {
2984a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_host = xstrdup(fwdargs[0].arg);
2985a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[1].arg);
2986a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_path = xstrdup(fwdargs[2].arg);
2987a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = PORT_STREAMLOCAL;
2988a0ee8cc6SDag-Erling Smørgrav 		} else {
2989aa49c926SDag-Erling Smørgrav 			fwd->listen_host = NULL;
2990a0ee8cc6SDag-Erling Smørgrav 			fwd->listen_port = a2port(fwdargs[0].arg);
2991a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_host = xstrdup(fwdargs[1].arg);
2992a0ee8cc6SDag-Erling Smørgrav 			fwd->connect_port = a2port(fwdargs[2].arg);
2993a0ee8cc6SDag-Erling Smørgrav 		}
2994aa49c926SDag-Erling Smørgrav 		break;
2995aa49c926SDag-Erling Smørgrav 
2996aa49c926SDag-Erling Smørgrav 	case 4:
2997a0ee8cc6SDag-Erling Smørgrav 		fwd->listen_host = xstrdup(fwdargs[0].arg);
2998a0ee8cc6SDag-Erling Smørgrav 		fwd->listen_port = a2port(fwdargs[1].arg);
2999a0ee8cc6SDag-Erling Smørgrav 		fwd->connect_host = xstrdup(fwdargs[2].arg);
3000a0ee8cc6SDag-Erling Smørgrav 		fwd->connect_port = a2port(fwdargs[3].arg);
3001aa49c926SDag-Erling Smørgrav 		break;
3002aa49c926SDag-Erling Smørgrav 	default:
3003aa49c926SDag-Erling Smørgrav 		i = 0; /* failure */
3004aa49c926SDag-Erling Smørgrav 	}
3005aa49c926SDag-Erling Smørgrav 
3006e4a9863fSDag-Erling Smørgrav 	free(p);
3007aa49c926SDag-Erling Smørgrav 
3008cce7d346SDag-Erling Smørgrav 	if (dynamicfwd) {
3009cce7d346SDag-Erling Smørgrav 		if (!(i == 1 || i == 2))
3010cce7d346SDag-Erling Smørgrav 			goto fail_free;
3011cce7d346SDag-Erling Smørgrav 	} else {
3012a0ee8cc6SDag-Erling Smørgrav 		if (!(i == 3 || i == 4)) {
3013a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_path == NULL &&
3014a0ee8cc6SDag-Erling Smørgrav 			    fwd->listen_path == NULL)
3015cce7d346SDag-Erling Smørgrav 				goto fail_free;
3016a0ee8cc6SDag-Erling Smørgrav 		}
3017a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_port <= 0 && fwd->connect_path == NULL)
3018cce7d346SDag-Erling Smørgrav 			goto fail_free;
3019cce7d346SDag-Erling Smørgrav 	}
3020cce7d346SDag-Erling Smørgrav 
3021a0ee8cc6SDag-Erling Smørgrav 	if ((fwd->listen_port < 0 && fwd->listen_path == NULL) ||
3022a0ee8cc6SDag-Erling Smørgrav 	    (!remotefwd && fwd->listen_port == 0))
3023aa49c926SDag-Erling Smørgrav 		goto fail_free;
3024aa49c926SDag-Erling Smørgrav 	if (fwd->connect_host != NULL &&
3025aa49c926SDag-Erling Smørgrav 	    strlen(fwd->connect_host) >= NI_MAXHOST)
3026aa49c926SDag-Erling Smørgrav 		goto fail_free;
302719261079SEd Maste 	/*
302819261079SEd Maste 	 * XXX - if connecting to a remote socket, max sun len may not
302919261079SEd Maste 	 * match this host
303019261079SEd Maste 	 */
3031a0ee8cc6SDag-Erling Smørgrav 	if (fwd->connect_path != NULL &&
3032a0ee8cc6SDag-Erling Smørgrav 	    strlen(fwd->connect_path) >= PATH_MAX_SUN)
3033a0ee8cc6SDag-Erling Smørgrav 		goto fail_free;
3034cce7d346SDag-Erling Smørgrav 	if (fwd->listen_host != NULL &&
3035cce7d346SDag-Erling Smørgrav 	    strlen(fwd->listen_host) >= NI_MAXHOST)
3036cce7d346SDag-Erling Smørgrav 		goto fail_free;
3037a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL &&
3038a0ee8cc6SDag-Erling Smørgrav 	    strlen(fwd->listen_path) >= PATH_MAX_SUN)
3039a0ee8cc6SDag-Erling Smørgrav 		goto fail_free;
3040aa49c926SDag-Erling Smørgrav 
3041aa49c926SDag-Erling Smørgrav 	return (i);
3042aa49c926SDag-Erling Smørgrav 
3043aa49c926SDag-Erling Smørgrav  fail_free:
3044e4a9863fSDag-Erling Smørgrav 	free(fwd->connect_host);
3045cce7d346SDag-Erling Smørgrav 	fwd->connect_host = NULL;
3046a0ee8cc6SDag-Erling Smørgrav 	free(fwd->connect_path);
3047a0ee8cc6SDag-Erling Smørgrav 	fwd->connect_path = NULL;
3048e4a9863fSDag-Erling Smørgrav 	free(fwd->listen_host);
3049cce7d346SDag-Erling Smørgrav 	fwd->listen_host = NULL;
3050a0ee8cc6SDag-Erling Smørgrav 	free(fwd->listen_path);
3051a0ee8cc6SDag-Erling Smørgrav 	fwd->listen_path = NULL;
3052aa49c926SDag-Erling Smørgrav 	return (0);
3053aa49c926SDag-Erling Smørgrav }
3054bc5531deSDag-Erling Smørgrav 
3055076ad2f8SDag-Erling Smørgrav int
3056076ad2f8SDag-Erling Smørgrav parse_jump(const char *s, Options *o, int active)
3057076ad2f8SDag-Erling Smørgrav {
3058076ad2f8SDag-Erling Smørgrav 	char *orig, *sdup, *cp;
3059076ad2f8SDag-Erling Smørgrav 	char *host = NULL, *user = NULL;
306019261079SEd Maste 	int r, ret = -1, port = -1, first;
3061076ad2f8SDag-Erling Smørgrav 
3062076ad2f8SDag-Erling Smørgrav 	active &= o->proxy_command == NULL && o->jump_host == NULL;
3063076ad2f8SDag-Erling Smørgrav 
3064076ad2f8SDag-Erling Smørgrav 	orig = sdup = xstrdup(s);
306519261079SEd Maste 
306619261079SEd Maste 	/* Remove comment and trailing whitespace */
306719261079SEd Maste 	if ((cp = strchr(orig, '#')) != NULL)
306819261079SEd Maste 		*cp = '\0';
306919261079SEd Maste 	rtrim(orig);
307019261079SEd Maste 
3071076ad2f8SDag-Erling Smørgrav 	first = active;
3072076ad2f8SDag-Erling Smørgrav 	do {
3073190cef3dSDag-Erling Smørgrav 		if (strcasecmp(s, "none") == 0)
3074190cef3dSDag-Erling Smørgrav 			break;
3075076ad2f8SDag-Erling Smørgrav 		if ((cp = strrchr(sdup, ',')) == NULL)
3076076ad2f8SDag-Erling Smørgrav 			cp = sdup; /* last */
3077076ad2f8SDag-Erling Smørgrav 		else
3078076ad2f8SDag-Erling Smørgrav 			*cp++ = '\0';
3079076ad2f8SDag-Erling Smørgrav 
3080076ad2f8SDag-Erling Smørgrav 		if (first) {
3081076ad2f8SDag-Erling Smørgrav 			/* First argument and configuration is active */
308219261079SEd Maste 			r = parse_ssh_uri(cp, &user, &host, &port);
308319261079SEd Maste 			if (r == -1 || (r == 1 &&
308419261079SEd Maste 			    parse_user_host_port(cp, &user, &host, &port) != 0))
3085076ad2f8SDag-Erling Smørgrav 				goto out;
3086076ad2f8SDag-Erling Smørgrav 		} else {
3087076ad2f8SDag-Erling Smørgrav 			/* Subsequent argument or inactive configuration */
308819261079SEd Maste 			r = parse_ssh_uri(cp, NULL, NULL, NULL);
308919261079SEd Maste 			if (r == -1 || (r == 1 &&
309019261079SEd Maste 			    parse_user_host_port(cp, NULL, NULL, NULL) != 0))
3091076ad2f8SDag-Erling Smørgrav 				goto out;
3092076ad2f8SDag-Erling Smørgrav 		}
3093076ad2f8SDag-Erling Smørgrav 		first = 0; /* only check syntax for subsequent hosts */
3094076ad2f8SDag-Erling Smørgrav 	} while (cp != sdup);
3095076ad2f8SDag-Erling Smørgrav 	/* success */
3096076ad2f8SDag-Erling Smørgrav 	if (active) {
3097190cef3dSDag-Erling Smørgrav 		if (strcasecmp(s, "none") == 0) {
3098190cef3dSDag-Erling Smørgrav 			o->jump_host = xstrdup("none");
3099190cef3dSDag-Erling Smørgrav 			o->jump_port = 0;
3100190cef3dSDag-Erling Smørgrav 		} else {
3101076ad2f8SDag-Erling Smørgrav 			o->jump_user = user;
3102076ad2f8SDag-Erling Smørgrav 			o->jump_host = host;
3103076ad2f8SDag-Erling Smørgrav 			o->jump_port = port;
3104076ad2f8SDag-Erling Smørgrav 			o->proxy_command = xstrdup("none");
3105076ad2f8SDag-Erling Smørgrav 			user = host = NULL;
3106076ad2f8SDag-Erling Smørgrav 			if ((cp = strrchr(s, ',')) != NULL && cp != s) {
3107076ad2f8SDag-Erling Smørgrav 				o->jump_extra = xstrdup(s);
3108076ad2f8SDag-Erling Smørgrav 				o->jump_extra[cp - s] = '\0';
3109076ad2f8SDag-Erling Smørgrav 			}
3110076ad2f8SDag-Erling Smørgrav 		}
3111190cef3dSDag-Erling Smørgrav 	}
3112076ad2f8SDag-Erling Smørgrav 	ret = 0;
3113076ad2f8SDag-Erling Smørgrav  out:
3114076ad2f8SDag-Erling Smørgrav 	free(orig);
3115076ad2f8SDag-Erling Smørgrav 	free(user);
3116076ad2f8SDag-Erling Smørgrav 	free(host);
3117076ad2f8SDag-Erling Smørgrav 	return ret;
3118076ad2f8SDag-Erling Smørgrav }
3119076ad2f8SDag-Erling Smørgrav 
312047dd1d1bSDag-Erling Smørgrav int
312147dd1d1bSDag-Erling Smørgrav parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
312247dd1d1bSDag-Erling Smørgrav {
312319261079SEd Maste 	char *user = NULL, *host = NULL, *path = NULL;
312419261079SEd Maste 	int r, port;
312547dd1d1bSDag-Erling Smørgrav 
312619261079SEd Maste 	r = parse_uri("ssh", uri, &user, &host, &port, &path);
312747dd1d1bSDag-Erling Smørgrav 	if (r == 0 && path != NULL)
312847dd1d1bSDag-Erling Smørgrav 		r = -1;		/* path not allowed */
312919261079SEd Maste 	if (r == 0) {
313019261079SEd Maste 		if (userp != NULL) {
313119261079SEd Maste 			*userp = user;
313219261079SEd Maste 			user = NULL;
313319261079SEd Maste 		}
313419261079SEd Maste 		if (hostp != NULL) {
313519261079SEd Maste 			*hostp = host;
313619261079SEd Maste 			host = NULL;
313719261079SEd Maste 		}
313819261079SEd Maste 		if (portp != NULL)
313919261079SEd Maste 			*portp = port;
314019261079SEd Maste 	}
314119261079SEd Maste 	free(user);
314219261079SEd Maste 	free(host);
314319261079SEd Maste 	free(path);
314447dd1d1bSDag-Erling Smørgrav 	return r;
314547dd1d1bSDag-Erling Smørgrav }
314647dd1d1bSDag-Erling Smørgrav 
3147bc5531deSDag-Erling Smørgrav /* XXX the following is a near-vebatim copy from servconf.c; refactor */
3148bc5531deSDag-Erling Smørgrav static const char *
3149bc5531deSDag-Erling Smørgrav fmt_multistate_int(int val, const struct multistate *m)
3150bc5531deSDag-Erling Smørgrav {
3151bc5531deSDag-Erling Smørgrav 	u_int i;
3152bc5531deSDag-Erling Smørgrav 
3153bc5531deSDag-Erling Smørgrav 	for (i = 0; m[i].key != NULL; i++) {
3154bc5531deSDag-Erling Smørgrav 		if (m[i].value == val)
3155bc5531deSDag-Erling Smørgrav 			return m[i].key;
3156bc5531deSDag-Erling Smørgrav 	}
3157bc5531deSDag-Erling Smørgrav 	return "UNKNOWN";
3158bc5531deSDag-Erling Smørgrav }
3159bc5531deSDag-Erling Smørgrav 
3160bc5531deSDag-Erling Smørgrav static const char *
3161bc5531deSDag-Erling Smørgrav fmt_intarg(OpCodes code, int val)
3162bc5531deSDag-Erling Smørgrav {
3163bc5531deSDag-Erling Smørgrav 	if (val == -1)
3164bc5531deSDag-Erling Smørgrav 		return "unset";
3165bc5531deSDag-Erling Smørgrav 	switch (code) {
3166bc5531deSDag-Erling Smørgrav 	case oAddressFamily:
3167bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_addressfamily);
3168bc5531deSDag-Erling Smørgrav 	case oVerifyHostKeyDNS:
3169bc5531deSDag-Erling Smørgrav 	case oUpdateHostkeys:
3170bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_yesnoask);
31714f52dfbbSDag-Erling Smørgrav 	case oStrictHostKeyChecking:
31724f52dfbbSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_strict_hostkey);
3173bc5531deSDag-Erling Smørgrav 	case oControlMaster:
3174bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_controlmaster);
3175bc5531deSDag-Erling Smørgrav 	case oTunnel:
3176bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_tunnel);
3177bc5531deSDag-Erling Smørgrav 	case oRequestTTY:
3178bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_requesttty);
317919261079SEd Maste 	case oSessionType:
318019261079SEd Maste 		return fmt_multistate_int(val, multistate_sessiontype);
3181bc5531deSDag-Erling Smørgrav 	case oCanonicalizeHostname:
3182bc5531deSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_canonicalizehostname);
3183190cef3dSDag-Erling Smørgrav 	case oAddKeysToAgent:
3184190cef3dSDag-Erling Smørgrav 		return fmt_multistate_int(val, multistate_yesnoaskconfirm);
3185*1323ec57SEd Maste 	case oPubkeyAuthentication:
3186*1323ec57SEd Maste 		return fmt_multistate_int(val, multistate_pubkey_auth);
3187bc5531deSDag-Erling Smørgrav 	case oFingerprintHash:
3188bc5531deSDag-Erling Smørgrav 		return ssh_digest_alg_name(val);
3189bc5531deSDag-Erling Smørgrav 	default:
3190bc5531deSDag-Erling Smørgrav 		switch (val) {
3191bc5531deSDag-Erling Smørgrav 		case 0:
3192bc5531deSDag-Erling Smørgrav 			return "no";
3193bc5531deSDag-Erling Smørgrav 		case 1:
3194bc5531deSDag-Erling Smørgrav 			return "yes";
3195bc5531deSDag-Erling Smørgrav 		default:
3196bc5531deSDag-Erling Smørgrav 			return "UNKNOWN";
3197bc5531deSDag-Erling Smørgrav 		}
3198bc5531deSDag-Erling Smørgrav 	}
3199bc5531deSDag-Erling Smørgrav }
3200bc5531deSDag-Erling Smørgrav 
3201bc5531deSDag-Erling Smørgrav static const char *
3202bc5531deSDag-Erling Smørgrav lookup_opcode_name(OpCodes code)
3203bc5531deSDag-Erling Smørgrav {
3204bc5531deSDag-Erling Smørgrav 	u_int i;
3205bc5531deSDag-Erling Smørgrav 
3206bc5531deSDag-Erling Smørgrav 	for (i = 0; keywords[i].name != NULL; i++)
3207bc5531deSDag-Erling Smørgrav 		if (keywords[i].opcode == code)
3208bc5531deSDag-Erling Smørgrav 			return(keywords[i].name);
3209bc5531deSDag-Erling Smørgrav 	return "UNKNOWN";
3210bc5531deSDag-Erling Smørgrav }
3211bc5531deSDag-Erling Smørgrav 
3212bc5531deSDag-Erling Smørgrav static void
3213bc5531deSDag-Erling Smørgrav dump_cfg_int(OpCodes code, int val)
3214bc5531deSDag-Erling Smørgrav {
3215bc5531deSDag-Erling Smørgrav 	printf("%s %d\n", lookup_opcode_name(code), val);
3216bc5531deSDag-Erling Smørgrav }
3217bc5531deSDag-Erling Smørgrav 
3218bc5531deSDag-Erling Smørgrav static void
3219bc5531deSDag-Erling Smørgrav dump_cfg_fmtint(OpCodes code, int val)
3220bc5531deSDag-Erling Smørgrav {
3221bc5531deSDag-Erling Smørgrav 	printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val));
3222bc5531deSDag-Erling Smørgrav }
3223bc5531deSDag-Erling Smørgrav 
3224bc5531deSDag-Erling Smørgrav static void
3225bc5531deSDag-Erling Smørgrav dump_cfg_string(OpCodes code, const char *val)
3226bc5531deSDag-Erling Smørgrav {
3227bc5531deSDag-Erling Smørgrav 	if (val == NULL)
3228bc5531deSDag-Erling Smørgrav 		return;
3229bc5531deSDag-Erling Smørgrav 	printf("%s %s\n", lookup_opcode_name(code), val);
3230bc5531deSDag-Erling Smørgrav }
3231bc5531deSDag-Erling Smørgrav 
3232bc5531deSDag-Erling Smørgrav static void
3233bc5531deSDag-Erling Smørgrav dump_cfg_strarray(OpCodes code, u_int count, char **vals)
3234bc5531deSDag-Erling Smørgrav {
3235bc5531deSDag-Erling Smørgrav 	u_int i;
3236bc5531deSDag-Erling Smørgrav 
3237bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++)
3238bc5531deSDag-Erling Smørgrav 		printf("%s %s\n", lookup_opcode_name(code), vals[i]);
3239bc5531deSDag-Erling Smørgrav }
3240bc5531deSDag-Erling Smørgrav 
3241bc5531deSDag-Erling Smørgrav static void
3242bc5531deSDag-Erling Smørgrav dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals)
3243bc5531deSDag-Erling Smørgrav {
3244bc5531deSDag-Erling Smørgrav 	u_int i;
3245bc5531deSDag-Erling Smørgrav 
3246bc5531deSDag-Erling Smørgrav 	printf("%s", lookup_opcode_name(code));
324719261079SEd Maste 	if (count == 0)
324819261079SEd Maste 		printf(" none");
3249bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++)
3250bc5531deSDag-Erling Smørgrav 		printf(" %s",  vals[i]);
3251bc5531deSDag-Erling Smørgrav 	printf("\n");
3252bc5531deSDag-Erling Smørgrav }
3253bc5531deSDag-Erling Smørgrav 
3254bc5531deSDag-Erling Smørgrav static void
3255bc5531deSDag-Erling Smørgrav dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds)
3256bc5531deSDag-Erling Smørgrav {
3257bc5531deSDag-Erling Smørgrav 	const struct Forward *fwd;
3258bc5531deSDag-Erling Smørgrav 	u_int i;
3259bc5531deSDag-Erling Smørgrav 
3260bc5531deSDag-Erling Smørgrav 	/* oDynamicForward */
3261bc5531deSDag-Erling Smørgrav 	for (i = 0; i < count; i++) {
3262bc5531deSDag-Erling Smørgrav 		fwd = &fwds[i];
3263d93a896eSDag-Erling Smørgrav 		if (code == oDynamicForward && fwd->connect_host != NULL &&
3264bc5531deSDag-Erling Smørgrav 		    strcmp(fwd->connect_host, "socks") != 0)
3265bc5531deSDag-Erling Smørgrav 			continue;
3266d93a896eSDag-Erling Smørgrav 		if (code == oLocalForward && fwd->connect_host != NULL &&
3267bc5531deSDag-Erling Smørgrav 		    strcmp(fwd->connect_host, "socks") == 0)
3268bc5531deSDag-Erling Smørgrav 			continue;
3269bc5531deSDag-Erling Smørgrav 		printf("%s", lookup_opcode_name(code));
3270bc5531deSDag-Erling Smørgrav 		if (fwd->listen_port == PORT_STREAMLOCAL)
3271bc5531deSDag-Erling Smørgrav 			printf(" %s", fwd->listen_path);
3272bc5531deSDag-Erling Smørgrav 		else if (fwd->listen_host == NULL)
3273bc5531deSDag-Erling Smørgrav 			printf(" %d", fwd->listen_port);
3274bc5531deSDag-Erling Smørgrav 		else {
3275bc5531deSDag-Erling Smørgrav 			printf(" [%s]:%d",
3276bc5531deSDag-Erling Smørgrav 			    fwd->listen_host, fwd->listen_port);
3277bc5531deSDag-Erling Smørgrav 		}
3278bc5531deSDag-Erling Smørgrav 		if (code != oDynamicForward) {
3279bc5531deSDag-Erling Smørgrav 			if (fwd->connect_port == PORT_STREAMLOCAL)
3280bc5531deSDag-Erling Smørgrav 				printf(" %s", fwd->connect_path);
3281bc5531deSDag-Erling Smørgrav 			else if (fwd->connect_host == NULL)
3282bc5531deSDag-Erling Smørgrav 				printf(" %d", fwd->connect_port);
3283bc5531deSDag-Erling Smørgrav 			else {
3284bc5531deSDag-Erling Smørgrav 				printf(" [%s]:%d",
3285bc5531deSDag-Erling Smørgrav 				    fwd->connect_host, fwd->connect_port);
3286bc5531deSDag-Erling Smørgrav 			}
3287bc5531deSDag-Erling Smørgrav 		}
3288bc5531deSDag-Erling Smørgrav 		printf("\n");
3289bc5531deSDag-Erling Smørgrav 	}
3290bc5531deSDag-Erling Smørgrav }
3291bc5531deSDag-Erling Smørgrav 
3292bc5531deSDag-Erling Smørgrav void
3293bc5531deSDag-Erling Smørgrav dump_client_config(Options *o, const char *host)
3294bc5531deSDag-Erling Smørgrav {
329519261079SEd Maste 	int i, r;
3296190cef3dSDag-Erling Smørgrav 	char buf[8], *all_key;
3297bc5531deSDag-Erling Smørgrav 
329819261079SEd Maste 	/*
329919261079SEd Maste 	 * Expand HostKeyAlgorithms name lists. This isn't handled in
330019261079SEd Maste 	 * fill_default_options() like the other algorithm lists because
330119261079SEd Maste 	 * the host key algorithms are by default dynamically chosen based
330219261079SEd Maste 	 * on the host's keys found in known_hosts.
330319261079SEd Maste 	 */
3304190cef3dSDag-Erling Smørgrav 	all_key = sshkey_alg_list(0, 0, 1, ',');
330519261079SEd Maste 	if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(),
330619261079SEd Maste 	    all_key)) != 0)
330719261079SEd Maste 		fatal_fr(r, "expand HostKeyAlgorithms");
3308190cef3dSDag-Erling Smørgrav 	free(all_key);
3309acc1a9efSDag-Erling Smørgrav 
3310bc5531deSDag-Erling Smørgrav 	/* Most interesting options first: user, host, port */
3311bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oUser, o->user);
331219261079SEd Maste 	dump_cfg_string(oHostname, host);
3313bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oPort, o->port);
3314bc5531deSDag-Erling Smørgrav 
3315bc5531deSDag-Erling Smørgrav 	/* Flag options */
3316bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oAddressFamily, o->address_family);
3317bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oBatchMode, o->batch_mode);
3318bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local);
3319bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname);
3320bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCheckHostIP, o->check_host_ip);
3321bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oCompression, o->compression);
3322bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oControlMaster, o->control_master);
3323bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign);
3324076ad2f8SDag-Erling Smørgrav 	dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings);
3325bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure);
3326bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash);
3327bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oForwardX11, o->forward_x11);
3328bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted);
3329bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports);
3330bc5531deSDag-Erling Smørgrav #ifdef GSSAPI
3331bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGssAuthentication, o->gss_authentication);
3332bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds);
3333bc5531deSDag-Erling Smørgrav #endif /* GSSAPI */
3334bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts);
3335bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication);
3336bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oIdentitiesOnly, o->identities_only);
3337bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication);
3338bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost);
3339bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication);
3340bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command);
3341bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass);
3342bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication);
3343bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oRequestTTY, o->request_tty);
334419261079SEd Maste 	dump_cfg_fmtint(oSessionType, o->session_type);
334519261079SEd Maste 	dump_cfg_fmtint(oStdinNull, o->stdin_null);
334619261079SEd Maste 	dump_cfg_fmtint(oForkAfterAuthentication, o->fork_after_authentication);
3347bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
3348bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking);
3349bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive);
3350bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oTunnel, o->tun_open);
3351bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
3352bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
3353bc5531deSDag-Erling Smørgrav 	dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
3354bc5531deSDag-Erling Smørgrav 
3355bc5531deSDag-Erling Smørgrav 	/* Integer options */
3356bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
3357bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oConnectionAttempts, o->connection_attempts);
3358bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout);
3359bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
3360bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
3361bc5531deSDag-Erling Smørgrav 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
3362bc5531deSDag-Erling Smørgrav 
3363bc5531deSDag-Erling Smørgrav 	/* String options */
3364bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oBindAddress, o->bind_address);
336547dd1d1bSDag-Erling Smørgrav 	dump_cfg_string(oBindInterface, o->bind_interface);
336619261079SEd Maste 	dump_cfg_string(oCiphers, o->ciphers);
3367bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oControlPath, o->control_path);
3368acc1a9efSDag-Erling Smørgrav 	dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms);
3369bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oHostKeyAlias, o->host_key_alias);
337019261079SEd Maste 	dump_cfg_string(oHostbasedAcceptedAlgorithms, o->hostbased_accepted_algos);
3371076ad2f8SDag-Erling Smørgrav 	dump_cfg_string(oIdentityAgent, o->identity_agent);
3372190cef3dSDag-Erling Smørgrav 	dump_cfg_string(oIgnoreUnknown, o->ignored_unknown);
3373bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices);
337419261079SEd Maste 	dump_cfg_string(oKexAlgorithms, o->kex_algorithms);
337519261079SEd Maste 	dump_cfg_string(oCASignatureAlgorithms, o->ca_sign_algorithms);
3376bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oLocalCommand, o->local_command);
33774f52dfbbSDag-Erling Smørgrav 	dump_cfg_string(oRemoteCommand, o->remote_command);
3378bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oLogLevel, log_level_name(o->log_level));
337919261079SEd Maste 	dump_cfg_string(oMacs, o->macs);
3380d93a896eSDag-Erling Smørgrav #ifdef ENABLE_PKCS11
3381bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oPKCS11Provider, o->pkcs11_provider);
3382d93a896eSDag-Erling Smørgrav #endif
338319261079SEd Maste 	dump_cfg_string(oSecurityKeyProvider, o->sk_provider);
3384bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oPreferredAuthentications, o->preferred_authentications);
338519261079SEd Maste 	dump_cfg_string(oPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
3386bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys);
3387bc5531deSDag-Erling Smørgrav 	dump_cfg_string(oXAuthLocation, o->xauth_location);
338819261079SEd Maste 	dump_cfg_string(oKnownHostsCommand, o->known_hosts_command);
3389bc5531deSDag-Erling Smørgrav 
3390bc5531deSDag-Erling Smørgrav 	/* Forwards */
3391bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards);
3392bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards);
3393bc5531deSDag-Erling Smørgrav 	dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards);
3394bc5531deSDag-Erling Smørgrav 
3395bc5531deSDag-Erling Smørgrav 	/* String array options */
3396bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files);
3397bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains);
3398190cef3dSDag-Erling Smørgrav 	dump_cfg_strarray(oCertificateFile, o->num_certificate_files, o->certificate_files);
3399bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles);
3400bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles);
3401bc5531deSDag-Erling Smørgrav 	dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env);
3402190cef3dSDag-Erling Smørgrav 	dump_cfg_strarray(oSetEnv, o->num_setenv, o->setenv);
340319261079SEd Maste 	dump_cfg_strarray_oneline(oLogVerbose,
340419261079SEd Maste 	    o->num_log_verbose, o->log_verbose);
3405bc5531deSDag-Erling Smørgrav 
3406bc5531deSDag-Erling Smørgrav 	/* Special cases */
3407bc5531deSDag-Erling Smørgrav 
340819261079SEd Maste 	/* PermitRemoteOpen */
340919261079SEd Maste 	if (o->num_permitted_remote_opens == 0)
341019261079SEd Maste 		printf("%s any\n", lookup_opcode_name(oPermitRemoteOpen));
341119261079SEd Maste 	else
341219261079SEd Maste 		dump_cfg_strarray_oneline(oPermitRemoteOpen,
341319261079SEd Maste 		    o->num_permitted_remote_opens, o->permitted_remote_opens);
341419261079SEd Maste 
341519261079SEd Maste 	/* AddKeysToAgent */
341619261079SEd Maste 	if (o->add_keys_to_agent_lifespan <= 0)
341719261079SEd Maste 		dump_cfg_fmtint(oAddKeysToAgent, o->add_keys_to_agent);
341819261079SEd Maste 	else {
341919261079SEd Maste 		printf("addkeystoagent%s %d\n",
342019261079SEd Maste 		    o->add_keys_to_agent == 3 ? " confirm" : "",
342119261079SEd Maste 		    o->add_keys_to_agent_lifespan);
342219261079SEd Maste 	}
342319261079SEd Maste 
342419261079SEd Maste 	/* oForwardAgent */
342519261079SEd Maste 	if (o->forward_agent_sock_path == NULL)
342619261079SEd Maste 		dump_cfg_fmtint(oForwardAgent, o->forward_agent);
342719261079SEd Maste 	else
342819261079SEd Maste 		dump_cfg_string(oForwardAgent, o->forward_agent_sock_path);
342919261079SEd Maste 
3430bc5531deSDag-Erling Smørgrav 	/* oConnectTimeout */
3431bc5531deSDag-Erling Smørgrav 	if (o->connection_timeout == -1)
3432bc5531deSDag-Erling Smørgrav 		printf("connecttimeout none\n");
3433bc5531deSDag-Erling Smørgrav 	else
3434bc5531deSDag-Erling Smørgrav 		dump_cfg_int(oConnectTimeout, o->connection_timeout);
3435bc5531deSDag-Erling Smørgrav 
3436bc5531deSDag-Erling Smørgrav 	/* oTunnelDevice */
3437bc5531deSDag-Erling Smørgrav 	printf("tunneldevice");
3438bc5531deSDag-Erling Smørgrav 	if (o->tun_local == SSH_TUNID_ANY)
3439bc5531deSDag-Erling Smørgrav 		printf(" any");
3440bc5531deSDag-Erling Smørgrav 	else
3441bc5531deSDag-Erling Smørgrav 		printf(" %d", o->tun_local);
3442bc5531deSDag-Erling Smørgrav 	if (o->tun_remote == SSH_TUNID_ANY)
3443bc5531deSDag-Erling Smørgrav 		printf(":any");
3444bc5531deSDag-Erling Smørgrav 	else
3445bc5531deSDag-Erling Smørgrav 		printf(":%d", o->tun_remote);
3446bc5531deSDag-Erling Smørgrav 	printf("\n");
3447bc5531deSDag-Erling Smørgrav 
3448bc5531deSDag-Erling Smørgrav 	/* oCanonicalizePermittedCNAMEs */
3449bc5531deSDag-Erling Smørgrav 	printf("canonicalizePermittedcnames");
3450e9e8876aSEd Maste 	if (o->num_permitted_cnames == 0)
3451e9e8876aSEd Maste 		printf(" none");
3452bc5531deSDag-Erling Smørgrav 	for (i = 0; i < o->num_permitted_cnames; i++) {
3453bc5531deSDag-Erling Smørgrav 		printf(" %s:%s", o->permitted_cnames[i].source_list,
3454bc5531deSDag-Erling Smørgrav 		    o->permitted_cnames[i].target_list);
3455bc5531deSDag-Erling Smørgrav 	}
3456bc5531deSDag-Erling Smørgrav 	printf("\n");
3457bc5531deSDag-Erling Smørgrav 
3458bc5531deSDag-Erling Smørgrav 	/* oControlPersist */
3459bc5531deSDag-Erling Smørgrav 	if (o->control_persist == 0 || o->control_persist_timeout == 0)
3460bc5531deSDag-Erling Smørgrav 		dump_cfg_fmtint(oControlPersist, o->control_persist);
3461bc5531deSDag-Erling Smørgrav 	else
3462bc5531deSDag-Erling Smørgrav 		dump_cfg_int(oControlPersist, o->control_persist_timeout);
3463bc5531deSDag-Erling Smørgrav 
3464bc5531deSDag-Erling Smørgrav 	/* oEscapeChar */
3465bc5531deSDag-Erling Smørgrav 	if (o->escape_char == SSH_ESCAPECHAR_NONE)
3466bc5531deSDag-Erling Smørgrav 		printf("escapechar none\n");
3467bc5531deSDag-Erling Smørgrav 	else {
3468076ad2f8SDag-Erling Smørgrav 		vis(buf, o->escape_char, VIS_WHITE, 0);
3469076ad2f8SDag-Erling Smørgrav 		printf("escapechar %s\n", buf);
3470bc5531deSDag-Erling Smørgrav 	}
3471bc5531deSDag-Erling Smørgrav 
3472bc5531deSDag-Erling Smørgrav 	/* oIPQoS */
3473bc5531deSDag-Erling Smørgrav 	printf("ipqos %s ", iptos2str(o->ip_qos_interactive));
3474bc5531deSDag-Erling Smørgrav 	printf("%s\n", iptos2str(o->ip_qos_bulk));
3475bc5531deSDag-Erling Smørgrav 
3476bc5531deSDag-Erling Smørgrav 	/* oRekeyLimit */
3477acc1a9efSDag-Erling Smørgrav 	printf("rekeylimit %llu %d\n",
3478acc1a9efSDag-Erling Smørgrav 	    (unsigned long long)o->rekey_limit, o->rekey_interval);
3479bc5531deSDag-Erling Smørgrav 
3480bc5531deSDag-Erling Smørgrav 	/* oStreamLocalBindMask */
3481bc5531deSDag-Erling Smørgrav 	printf("streamlocalbindmask 0%o\n",
3482bc5531deSDag-Erling Smørgrav 	    o->fwd_opts.streamlocal_bind_mask);
3483076ad2f8SDag-Erling Smørgrav 
3484190cef3dSDag-Erling Smørgrav 	/* oLogFacility */
3485190cef3dSDag-Erling Smørgrav 	printf("syslogfacility %s\n", log_facility_name(o->log_facility));
3486190cef3dSDag-Erling Smørgrav 
3487076ad2f8SDag-Erling Smørgrav 	/* oProxyCommand / oProxyJump */
3488076ad2f8SDag-Erling Smørgrav 	if (o->jump_host == NULL)
3489076ad2f8SDag-Erling Smørgrav 		dump_cfg_string(oProxyCommand, o->proxy_command);
3490076ad2f8SDag-Erling Smørgrav 	else {
3491076ad2f8SDag-Erling Smørgrav 		/* Check for numeric addresses */
3492076ad2f8SDag-Erling Smørgrav 		i = strchr(o->jump_host, ':') != NULL ||
3493076ad2f8SDag-Erling Smørgrav 		    strspn(o->jump_host, "1234567890.") == strlen(o->jump_host);
3494076ad2f8SDag-Erling Smørgrav 		snprintf(buf, sizeof(buf), "%d", o->jump_port);
3495076ad2f8SDag-Erling Smørgrav 		printf("proxyjump %s%s%s%s%s%s%s%s%s\n",
3496076ad2f8SDag-Erling Smørgrav 		    /* optional additional jump spec */
3497076ad2f8SDag-Erling Smørgrav 		    o->jump_extra == NULL ? "" : o->jump_extra,
3498076ad2f8SDag-Erling Smørgrav 		    o->jump_extra == NULL ? "" : ",",
3499076ad2f8SDag-Erling Smørgrav 		    /* optional user */
3500076ad2f8SDag-Erling Smørgrav 		    o->jump_user == NULL ? "" : o->jump_user,
3501076ad2f8SDag-Erling Smørgrav 		    o->jump_user == NULL ? "" : "@",
3502076ad2f8SDag-Erling Smørgrav 		    /* opening [ if hostname is numeric */
3503076ad2f8SDag-Erling Smørgrav 		    i ? "[" : "",
3504076ad2f8SDag-Erling Smørgrav 		    /* mandatory hostname */
3505076ad2f8SDag-Erling Smørgrav 		    o->jump_host,
3506076ad2f8SDag-Erling Smørgrav 		    /* closing ] if hostname is numeric */
3507076ad2f8SDag-Erling Smørgrav 		    i ? "]" : "",
3508076ad2f8SDag-Erling Smørgrav 		    /* optional port number */
3509076ad2f8SDag-Erling Smørgrav 		    o->jump_port <= 0 ? "" : ":",
3510076ad2f8SDag-Erling Smørgrav 		    o->jump_port <= 0 ? "" : buf);
3511076ad2f8SDag-Erling Smørgrav 	}
3512bc5531deSDag-Erling Smørgrav }
3513