1264377Sdes/* $OpenBSD: readconf.c,v 1.218 2014/02/23 20:11:36 djm Exp $ */
2224638Sbrooks/* $FreeBSD$ */
357429Smarkm/*
457429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
557429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
657429Smarkm *                    All rights reserved
757429Smarkm * Functions for reading the configuration files.
860576Skris *
965674Skris * As far as I am concerned, the code I have written for this software
1065674Skris * can be used freely for any purpose.  Any derived versions of this
1165674Skris * software must be clearly marked as such, and if the derived work is
1265674Skris * incompatible with the protocol description in the RFC file, it must be
1365674Skris * called by a name other than "ssh" or "Secure Shell".
1457429Smarkm */
1557429Smarkm
1657429Smarkm#include "includes.h"
17162856Sdes__RCSID("$FreeBSD$");
1857429Smarkm
19162856Sdes#include <sys/types.h>
20162856Sdes#include <sys/stat.h>
21162856Sdes#include <sys/socket.h>
22181918Sdes#include <sys/sysctl.h>
23262566Sdes#include <sys/wait.h>
24162856Sdes
25162856Sdes#include <netinet/in.h>
26221420Sdes#include <netinet/in_systm.h>
27221420Sdes#include <netinet/ip.h>
28264377Sdes#include <arpa/inet.h>
29162856Sdes
30162856Sdes#include <ctype.h>
31162856Sdes#include <errno.h>
32262566Sdes#include <fcntl.h>
33162856Sdes#include <netdb.h>
34262566Sdes#ifdef HAVE_PATHS_H
35262566Sdes# include <paths.h>
36262566Sdes#endif
37262566Sdes#include <pwd.h>
38162856Sdes#include <signal.h>
39162856Sdes#include <stdarg.h>
40162856Sdes#include <stdio.h>
41162856Sdes#include <string.h>
42162856Sdes#include <unistd.h>
43255767Sdes#ifdef HAVE_UTIL_H
44255767Sdes#include <util.h>
45255767Sdes#endif
46162856Sdes
47162856Sdes#include "xmalloc.h"
4857429Smarkm#include "ssh.h"
4976262Sgreen#include "compat.h"
5076262Sgreen#include "cipher.h"
5176262Sgreen#include "pathnames.h"
5276262Sgreen#include "log.h"
53162856Sdes#include "key.h"
5457429Smarkm#include "readconf.h"
5560576Skris#include "match.h"
5676262Sgreen#include "misc.h"
57162856Sdes#include "buffer.h"
5876262Sgreen#include "kex.h"
5976262Sgreen#include "mac.h"
60262566Sdes#include "uidswap.h"
61192595Sdes#include "version.h"
6257429Smarkm
6357429Smarkm/* Format of the configuration file:
6457429Smarkm
6557429Smarkm   # Configuration data is parsed as follows:
6657429Smarkm   #  1. command line options
6757429Smarkm   #  2. user-specific file
6857429Smarkm   #  3. system-wide file
6957429Smarkm   # Any configuration value is only changed the first time it is set.
7057429Smarkm   # Thus, host-specific definitions should be at the beginning of the
7157429Smarkm   # configuration file, and defaults at the end.
7257429Smarkm
7357429Smarkm   # Host-specific declarations.  These may override anything above.  A single
7457429Smarkm   # host may match multiple declarations; these are processed in the order
7557429Smarkm   # that they are given in.
7657429Smarkm
7757429Smarkm   Host *.ngs.fi ngs.fi
7898684Sdes     User foo
7957429Smarkm
8057429Smarkm   Host fake.com
8157429Smarkm     HostName another.host.name.real.org
8257429Smarkm     User blaah
8357429Smarkm     Port 34289
8457429Smarkm     ForwardX11 no
8557429Smarkm     ForwardAgent no
8657429Smarkm
8757429Smarkm   Host books.com
8857429Smarkm     RemoteForward 9999 shadows.cs.hut.fi:9999
8957429Smarkm     Cipher 3des
9057429Smarkm
9157429Smarkm   Host fascist.blob.com
9257429Smarkm     Port 23123
9357429Smarkm     User tylonen
9457429Smarkm     PasswordAuthentication no
9557429Smarkm
9657429Smarkm   Host puukko.hut.fi
9757429Smarkm     User t35124p
9857429Smarkm     ProxyCommand ssh-proxy %h %p
9957429Smarkm
10057429Smarkm   Host *.fr
10198684Sdes     PublicKeyAuthentication no
10257429Smarkm
10357429Smarkm   Host *.su
10457429Smarkm     Cipher none
10557429Smarkm     PasswordAuthentication no
10657429Smarkm
107157019Sdes   Host vpn.fake.com
108157019Sdes     Tunnel yes
109157019Sdes     TunnelDevice 3
110157019Sdes
11157429Smarkm   # Defaults for various options
11257429Smarkm   Host *
11357429Smarkm     ForwardAgent no
11476262Sgreen     ForwardX11 no
11557429Smarkm     PasswordAuthentication yes
11657429Smarkm     RSAAuthentication yes
11757429Smarkm     RhostsRSAAuthentication yes
11857429Smarkm     StrictHostKeyChecking yes
119126277Sdes     TcpKeepAlive no
12057429Smarkm     IdentityFile ~/.ssh/identity
12157429Smarkm     Port 22
12257429Smarkm     EscapeChar ~
12357429Smarkm
12457429Smarkm*/
12557429Smarkm
12657429Smarkm/* Keyword tokens. */
12757429Smarkm
12857429Smarkmtypedef enum {
12957429Smarkm	oBadOption,
130262566Sdes	oHost, oMatch,
131215116Sdes	oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
132215116Sdes	oGatewayPorts, oExitOnForwardFailure,
13398684Sdes	oPasswordAuthentication, oRSAAuthentication,
13476262Sgreen	oChallengeResponseAuthentication, oXAuthLocation,
13557429Smarkm	oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
136262566Sdes	oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
13757429Smarkm	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
13857429Smarkm	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
139126277Sdes	oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
14076262Sgreen	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
14176262Sgreen	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
14276262Sgreen	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
14376262Sgreen	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
144204917Sdes	oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
14593698Sdes	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
146124211Sdes	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
147124211Sdes	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
148128461Sdes	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
149215116Sdes	oSendEnv, oControlPath, oControlMaster, oControlPersist,
150215116Sdes	oHashKnownHosts,
151157019Sdes	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
152264377Sdes	oVisualHostKey, oUseRoaming,
153262566Sdes	oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
154262566Sdes	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
155262566Sdes	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
156255767Sdes	oIgnoredUnknownOption,
157224638Sbrooks	oHPNDisabled, oHPNBufferSize, oTcpRcvBufPoll, oTcpRcvBuf,
158224638Sbrooks#ifdef NONE_CIPHER_ENABLED
159224638Sbrooks	oNoneEnabled, oNoneSwitch,
160224638Sbrooks#endif
161255767Sdes	oVersionAddendum, oDeprecated, oUnsupported
16257429Smarkm} OpCodes;
16357429Smarkm
16457429Smarkm/* Textual representations of the tokens. */
16557429Smarkm
16657429Smarkmstatic struct {
16757429Smarkm	const char *name;
16857429Smarkm	OpCodes opcode;
16957429Smarkm} keywords[] = {
17057429Smarkm	{ "forwardagent", oForwardAgent },
17157429Smarkm	{ "forwardx11", oForwardX11 },
172126277Sdes	{ "forwardx11trusted", oForwardX11Trusted },
173215116Sdes	{ "forwardx11timeout", oForwardX11Timeout },
174162856Sdes	{ "exitonforwardfailure", oExitOnForwardFailure },
17565674Skris	{ "xauthlocation", oXAuthLocation },
17657429Smarkm	{ "gatewayports", oGatewayPorts },
17757429Smarkm	{ "useprivilegedport", oUsePrivilegedPort },
178124211Sdes	{ "rhostsauthentication", oDeprecated },
17957429Smarkm	{ "passwordauthentication", oPasswordAuthentication },
18069591Sgreen	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
18169591Sgreen	{ "kbdinteractivedevices", oKbdInteractiveDevices },
18257429Smarkm	{ "rsaauthentication", oRSAAuthentication },
18376262Sgreen	{ "pubkeyauthentication", oPubkeyAuthentication },
18476262Sgreen	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
18576262Sgreen	{ "rhostsrsaauthentication", oRhostsRSAAuthentication },
18676262Sgreen	{ "hostbasedauthentication", oHostbasedAuthentication },
18776262Sgreen	{ "challengeresponseauthentication", oChallengeResponseAuthentication },
18876262Sgreen	{ "skeyauthentication", oChallengeResponseAuthentication }, /* alias */
18976262Sgreen	{ "tisauthentication", oChallengeResponseAuthentication },  /* alias */
190124211Sdes	{ "kerberosauthentication", oUnsupported },
191124211Sdes	{ "kerberostgtpassing", oUnsupported },
192124211Sdes	{ "afstokenpassing", oUnsupported },
193124211Sdes#if defined(GSSAPI)
194124211Sdes	{ "gssapiauthentication", oGssAuthentication },
195124211Sdes	{ "gssapidelegatecredentials", oGssDelegateCreds },
196124211Sdes#else
197124211Sdes	{ "gssapiauthentication", oUnsupported },
198124211Sdes	{ "gssapidelegatecredentials", oUnsupported },
19992559Sdes#endif
20098684Sdes	{ "fallbacktorsh", oDeprecated },
20198684Sdes	{ "usersh", oDeprecated },
20257429Smarkm	{ "identityfile", oIdentityFile },
203192595Sdes	{ "identityfile2", oIdentityFile },			/* obsolete */
204128460Sdes	{ "identitiesonly", oIdentitiesOnly },
20557429Smarkm	{ "hostname", oHostName },
20676262Sgreen	{ "hostkeyalias", oHostKeyAlias },
20757429Smarkm	{ "proxycommand", oProxyCommand },
20857429Smarkm	{ "port", oPort },
20957429Smarkm	{ "cipher", oCipher },
21060576Skris	{ "ciphers", oCiphers },
21176262Sgreen	{ "macs", oMacs },
21260576Skris	{ "protocol", oProtocol },
21357429Smarkm	{ "remoteforward", oRemoteForward },
21457429Smarkm	{ "localforward", oLocalForward },
21557429Smarkm	{ "user", oUser },
21657429Smarkm	{ "host", oHost },
217262566Sdes	{ "match", oMatch },
21857429Smarkm	{ "escapechar", oEscapeChar },
21957429Smarkm	{ "globalknownhostsfile", oGlobalKnownHostsFile },
220226046Sdes	{ "globalknownhostsfile2", oDeprecated },
221192595Sdes	{ "userknownhostsfile", oUserKnownHostsFile },
222226046Sdes	{ "userknownhostsfile2", oDeprecated },
22357429Smarkm	{ "connectionattempts", oConnectionAttempts },
22457429Smarkm	{ "batchmode", oBatchMode },
22557429Smarkm	{ "checkhostip", oCheckHostIP },
22657429Smarkm	{ "stricthostkeychecking", oStrictHostKeyChecking },
22757429Smarkm	{ "compression", oCompression },
22857429Smarkm	{ "compressionlevel", oCompressionLevel },
229126277Sdes	{ "tcpkeepalive", oTCPKeepAlive },
230126277Sdes	{ "keepalive", oTCPKeepAlive },				/* obsolete */
23157429Smarkm	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
23257429Smarkm	{ "loglevel", oLogLevel },
23376262Sgreen	{ "dynamicforward", oDynamicForward },
23476262Sgreen	{ "preferredauthentications", oPreferredAuthentications },
23576262Sgreen	{ "hostkeyalgorithms", oHostKeyAlgorithms },
23692559Sdes	{ "bindaddress", oBindAddress },
237204917Sdes#ifdef ENABLE_PKCS11
238204917Sdes	{ "smartcarddevice", oPKCS11Provider },
239204917Sdes	{ "pkcs11provider", oPKCS11Provider },
240124211Sdes#else
241124211Sdes	{ "smartcarddevice", oUnsupported },
242204917Sdes	{ "pkcs11provider", oUnsupported },
243124211Sdes#endif
24492559Sdes	{ "clearallforwardings", oClearAllForwardings },
245113911Sdes	{ "enablesshkeysign", oEnableSSHKeysign },
246124211Sdes	{ "verifyhostkeydns", oVerifyHostKeyDNS },
24792559Sdes	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
248124211Sdes	{ "rekeylimit", oRekeyLimit },
249124211Sdes	{ "connecttimeout", oConnectTimeout },
250124211Sdes	{ "addressfamily", oAddressFamily },
251126277Sdes	{ "serveraliveinterval", oServerAliveInterval },
252126277Sdes	{ "serveralivecountmax", oServerAliveCountMax },
253137019Sdes	{ "sendenv", oSendEnv },
254137019Sdes	{ "controlpath", oControlPath },
255137019Sdes	{ "controlmaster", oControlMaster },
256215116Sdes	{ "controlpersist", oControlPersist },
257147005Sdes	{ "hashknownhosts", oHashKnownHosts },
258157019Sdes	{ "tunnel", oTunnel },
259157019Sdes	{ "tunneldevice", oTunnelDevice },
260157019Sdes	{ "localcommand", oLocalCommand },
261157019Sdes	{ "permitlocalcommand", oPermitLocalCommand },
262181111Sdes	{ "visualhostkey", oVisualHostKey },
263197679Sdes	{ "useroaming", oUseRoaming },
264221420Sdes	{ "kexalgorithms", oKexAlgorithms },
265221420Sdes	{ "ipqos", oIPQoS },
266226046Sdes	{ "requesttty", oRequestTTY },
267262566Sdes	{ "proxyusefdpass", oProxyUseFdpass },
268262566Sdes	{ "canonicaldomains", oCanonicalDomains },
269262566Sdes	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
270262566Sdes	{ "canonicalizehostname", oCanonicalizeHostname },
271262566Sdes	{ "canonicalizemaxdots", oCanonicalizeMaxDots },
272262566Sdes	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
273255767Sdes	{ "ignoreunknown", oIgnoreUnknown },
274224638Sbrooks	{ "hpndisabled", oHPNDisabled },
275224638Sbrooks	{ "hpnbuffersize", oHPNBufferSize },
276224638Sbrooks	{ "tcprcvbufpoll", oTcpRcvBufPoll },
277224638Sbrooks	{ "tcprcvbuf", oTcpRcvBuf },
278224638Sbrooks#ifdef	NONE_CIPHER_ENABLED
279224638Sbrooks	{ "noneenabled", oNoneEnabled },
280224638Sbrooks	{ "noneswitch", oNoneSwitch },
281224638Sbrooks#endif
28299048Sdes	{ "versionaddendum", oVersionAddendum },
283231584Sed
28492559Sdes	{ NULL, oBadOption }
28557429Smarkm};
28657429Smarkm
28757429Smarkm/*
28857429Smarkm * Adds a local TCP/IP port forward to options.  Never returns if there is an
28957429Smarkm * error.
29057429Smarkm */
29157429Smarkm
29260576Skrisvoid
293147005Sdesadd_local_forward(Options *options, const Forward *newfwd)
29457429Smarkm{
29557429Smarkm	Forward *fwd;
296106130Sdes#ifndef NO_IPPORT_RESERVED_CONCEPT
29757429Smarkm	extern uid_t original_real_uid;
298181918Sdes	int ipport_reserved;
299181918Sdes#ifdef __FreeBSD__
300181918Sdes	size_t len_ipport_reserved = sizeof(ipport_reserved);
301181918Sdes
302181918Sdes	if (sysctlbyname("net.inet.ip.portrange.reservedhigh",
303181918Sdes	    &ipport_reserved, &len_ipport_reserved, NULL, 0) != 0)
304181918Sdes		ipport_reserved = IPPORT_RESERVED;
305181918Sdes	else
306181918Sdes		ipport_reserved++;
307181918Sdes#else
308181918Sdes	ipport_reserved = IPPORT_RESERVED;
309181918Sdes#endif
310181918Sdes	if (newfwd->listen_port < ipport_reserved && original_real_uid != 0)
31176262Sgreen		fatal("Privileged ports can only be forwarded by root.");
31298941Sdes#endif
313215116Sdes	options->local_forwards = xrealloc(options->local_forwards,
314215116Sdes	    options->num_local_forwards + 1,
315215116Sdes	    sizeof(*options->local_forwards));
31657429Smarkm	fwd = &options->local_forwards[options->num_local_forwards++];
317147005Sdes
318192595Sdes	fwd->listen_host = newfwd->listen_host;
319147005Sdes	fwd->listen_port = newfwd->listen_port;
320192595Sdes	fwd->connect_host = newfwd->connect_host;
321147005Sdes	fwd->connect_port = newfwd->connect_port;
32257429Smarkm}
32357429Smarkm
32457429Smarkm/*
32557429Smarkm * Adds a remote TCP/IP port forward to options.  Never returns if there is
32657429Smarkm * an error.
32757429Smarkm */
32857429Smarkm
32960576Skrisvoid
330147005Sdesadd_remote_forward(Options *options, const Forward *newfwd)
33157429Smarkm{
33257429Smarkm	Forward *fwd;
333215116Sdes
334215116Sdes	options->remote_forwards = xrealloc(options->remote_forwards,
335215116Sdes	    options->num_remote_forwards + 1,
336215116Sdes	    sizeof(*options->remote_forwards));
33757429Smarkm	fwd = &options->remote_forwards[options->num_remote_forwards++];
338147005Sdes
339192595Sdes	fwd->listen_host = newfwd->listen_host;
340147005Sdes	fwd->listen_port = newfwd->listen_port;
341192595Sdes	fwd->connect_host = newfwd->connect_host;
342147005Sdes	fwd->connect_port = newfwd->connect_port;
343240075Sdes	fwd->handle = newfwd->handle;
344215116Sdes	fwd->allocated_port = 0;
34557429Smarkm}
34657429Smarkm
34792559Sdesstatic void
34892559Sdesclear_forwardings(Options *options)
34992559Sdes{
35092559Sdes	int i;
35192559Sdes
352147005Sdes	for (i = 0; i < options->num_local_forwards; i++) {
353255767Sdes		free(options->local_forwards[i].listen_host);
354255767Sdes		free(options->local_forwards[i].connect_host);
355147005Sdes	}
356215116Sdes	if (options->num_local_forwards > 0) {
357255767Sdes		free(options->local_forwards);
358215116Sdes		options->local_forwards = NULL;
359215116Sdes	}
36092559Sdes	options->num_local_forwards = 0;
361147005Sdes	for (i = 0; i < options->num_remote_forwards; i++) {
362255767Sdes		free(options->remote_forwards[i].listen_host);
363255767Sdes		free(options->remote_forwards[i].connect_host);
364147005Sdes	}
365215116Sdes	if (options->num_remote_forwards > 0) {
366255767Sdes		free(options->remote_forwards);
367215116Sdes		options->remote_forwards = NULL;
368215116Sdes	}
36992559Sdes	options->num_remote_forwards = 0;
370157019Sdes	options->tun_open = SSH_TUNMODE_NO;
37192559Sdes}
37292559Sdes
373249016Sdesvoid
374249016Sdesadd_identity_file(Options *options, const char *dir, const char *filename,
375249016Sdes    int userprovided)
376249016Sdes{
377249016Sdes	char *path;
378249016Sdes
379249016Sdes	if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
380249016Sdes		fatal("Too many identity files specified (max %d)",
381249016Sdes		    SSH_MAX_IDENTITY_FILES);
382249016Sdes
383249016Sdes	if (dir == NULL) /* no dir, filename is absolute */
384249016Sdes		path = xstrdup(filename);
385249016Sdes	else
386249016Sdes		(void)xasprintf(&path, "%.100s%.100s", dir, filename);
387249016Sdes
388249016Sdes	options->identity_file_userprovided[options->num_identity_files] =
389249016Sdes	    userprovided;
390249016Sdes	options->identity_files[options->num_identity_files++] = path;
391249016Sdes}
392249016Sdes
393262566Sdesint
394262566Sdesdefault_ssh_port(void)
395262566Sdes{
396262566Sdes	static int port;
397262566Sdes	struct servent *sp;
398262566Sdes
399262566Sdes	if (port == 0) {
400262566Sdes		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
401262566Sdes		port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
402262566Sdes	}
403262566Sdes	return port;
404262566Sdes}
405262566Sdes
40657429Smarkm/*
407262566Sdes * Execute a command in a shell.
408262566Sdes * Return its exit status or -1 on abnormal exit.
409262566Sdes */
410262566Sdesstatic int
411262566Sdesexecute_in_shell(const char *cmd)
412262566Sdes{
413262566Sdes	char *shell, *command_string;
414262566Sdes	pid_t pid;
415262566Sdes	int devnull, status;
416262566Sdes	extern uid_t original_real_uid;
417262566Sdes
418262566Sdes	if ((shell = getenv("SHELL")) == NULL)
419262566Sdes		shell = _PATH_BSHELL;
420262566Sdes
421262566Sdes	/*
422262566Sdes	 * Use "exec" to avoid "sh -c" processes on some platforms
423262566Sdes	 * (e.g. Solaris)
424262566Sdes	 */
425262566Sdes	xasprintf(&command_string, "exec %s", cmd);
426262566Sdes
427262566Sdes	/* Need this to redirect subprocess stdin/out */
428262566Sdes	if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
429262566Sdes		fatal("open(/dev/null): %s", strerror(errno));
430262566Sdes
431262566Sdes	debug("Executing command: '%.500s'", cmd);
432262566Sdes
433262566Sdes	/* Fork and execute the command. */
434262566Sdes	if ((pid = fork()) == 0) {
435262566Sdes		char *argv[4];
436262566Sdes
437262566Sdes		/* Child.  Permanently give up superuser privileges. */
438262566Sdes		permanently_drop_suid(original_real_uid);
439262566Sdes
440262566Sdes		/* Redirect child stdin and stdout. Leave stderr */
441262566Sdes		if (dup2(devnull, STDIN_FILENO) == -1)
442262566Sdes			fatal("dup2: %s", strerror(errno));
443262566Sdes		if (dup2(devnull, STDOUT_FILENO) == -1)
444262566Sdes			fatal("dup2: %s", strerror(errno));
445262566Sdes		if (devnull > STDERR_FILENO)
446262566Sdes			close(devnull);
447262566Sdes		closefrom(STDERR_FILENO + 1);
448262566Sdes
449262566Sdes		argv[0] = shell;
450262566Sdes		argv[1] = "-c";
451262566Sdes		argv[2] = command_string;
452262566Sdes		argv[3] = NULL;
453262566Sdes
454262566Sdes		execv(argv[0], argv);
455262566Sdes		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
456262566Sdes		/* Die with signal to make this error apparent to parent. */
457262566Sdes		signal(SIGTERM, SIG_DFL);
458262566Sdes		kill(getpid(), SIGTERM);
459262566Sdes		_exit(1);
460262566Sdes	}
461262566Sdes	/* Parent. */
462262566Sdes	if (pid < 0)
463262566Sdes		fatal("%s: fork: %.100s", __func__, strerror(errno));
464262566Sdes
465262566Sdes	close(devnull);
466262566Sdes	free(command_string);
467262566Sdes
468262566Sdes	while (waitpid(pid, &status, 0) == -1) {
469262566Sdes		if (errno != EINTR && errno != EAGAIN)
470262566Sdes			fatal("%s: waitpid: %s", __func__, strerror(errno));
471262566Sdes	}
472262566Sdes	if (!WIFEXITED(status)) {
473262566Sdes		error("command '%.100s' exited abnormally", cmd);
474262566Sdes		return -1;
475262566Sdes	}
476262566Sdes	debug3("command returned status %d", WEXITSTATUS(status));
477262566Sdes	return WEXITSTATUS(status);
478262566Sdes}
479262566Sdes
480262566Sdes/*
481262566Sdes * Parse and execute a Match directive.
482262566Sdes */
483262566Sdesstatic int
484262566Sdesmatch_cfg_line(Options *options, char **condition, struct passwd *pw,
485262566Sdes    const char *host_arg, const char *filename, int linenum)
486262566Sdes{
487262566Sdes	char *arg, *attrib, *cmd, *cp = *condition, *host;
488262566Sdes	const char *ruser;
489262566Sdes	int r, port, result = 1, attributes = 0;
490262566Sdes	size_t len;
491262566Sdes	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];
492262566Sdes
493262566Sdes	/*
494262566Sdes	 * Configuration is likely to be incomplete at this point so we
495262566Sdes	 * must be prepared to use default values.
496262566Sdes	 */
497262566Sdes	port = options->port <= 0 ? default_ssh_port() : options->port;
498262566Sdes	ruser = options->user == NULL ? pw->pw_name : options->user;
499262566Sdes	if (options->hostname != NULL) {
500262566Sdes		/* NB. Please keep in sync with ssh.c:main() */
501262566Sdes		host = percent_expand(options->hostname,
502262566Sdes		    "h", host_arg, (char *)NULL);
503262566Sdes	} else
504262566Sdes		host = xstrdup(host_arg);
505262566Sdes
506262566Sdes	debug3("checking match for '%s' host %s", cp, host);
507262566Sdes	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
508262566Sdes		attributes++;
509262566Sdes		if (strcasecmp(attrib, "all") == 0) {
510262566Sdes			if (attributes != 1 ||
511262566Sdes			    ((arg = strdelim(&cp)) != NULL && *arg != '\0')) {
512262566Sdes				error("'all' cannot be combined with other "
513262566Sdes				    "Match attributes");
514262566Sdes				result = -1;
515262566Sdes				goto out;
516262566Sdes			}
517262566Sdes			*condition = cp;
518262566Sdes			result = 1;
519262566Sdes			goto out;
520262566Sdes		}
521262566Sdes		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
522262566Sdes			error("Missing Match criteria for %s", attrib);
523262566Sdes			result = -1;
524262566Sdes			goto out;
525262566Sdes		}
526262566Sdes		len = strlen(arg);
527262566Sdes		if (strcasecmp(attrib, "host") == 0) {
528262566Sdes			if (match_hostname(host, arg, len) != 1)
529262566Sdes				result = 0;
530262566Sdes			else
531262566Sdes				debug("%.200s line %d: matched 'Host %.100s' ",
532262566Sdes				    filename, linenum, host);
533262566Sdes		} else if (strcasecmp(attrib, "originalhost") == 0) {
534262566Sdes			if (match_hostname(host_arg, arg, len) != 1)
535262566Sdes				result = 0;
536262566Sdes			else
537262566Sdes				debug("%.200s line %d: matched "
538262566Sdes				    "'OriginalHost %.100s' ",
539262566Sdes				    filename, linenum, host_arg);
540262566Sdes		} else if (strcasecmp(attrib, "user") == 0) {
541262566Sdes			if (match_pattern_list(ruser, arg, len, 0) != 1)
542262566Sdes				result = 0;
543262566Sdes			else
544262566Sdes				debug("%.200s line %d: matched 'User %.100s' ",
545262566Sdes				    filename, linenum, ruser);
546262566Sdes		} else if (strcasecmp(attrib, "localuser") == 0) {
547262566Sdes			if (match_pattern_list(pw->pw_name, arg, len, 0) != 1)
548262566Sdes				result = 0;
549262566Sdes			else
550262566Sdes				debug("%.200s line %d: matched "
551262566Sdes				    "'LocalUser %.100s' ",
552262566Sdes				    filename, linenum, pw->pw_name);
553262566Sdes		} else if (strcasecmp(attrib, "exec") == 0) {
554262566Sdes			if (gethostname(thishost, sizeof(thishost)) == -1)
555262566Sdes				fatal("gethostname: %s", strerror(errno));
556262566Sdes			strlcpy(shorthost, thishost, sizeof(shorthost));
557262566Sdes			shorthost[strcspn(thishost, ".")] = '\0';
558262566Sdes			snprintf(portstr, sizeof(portstr), "%d", port);
559262566Sdes
560262566Sdes			cmd = percent_expand(arg,
561262566Sdes			    "L", shorthost,
562262566Sdes			    "d", pw->pw_dir,
563262566Sdes			    "h", host,
564262566Sdes			    "l", thishost,
565262566Sdes			    "n", host_arg,
566262566Sdes			    "p", portstr,
567262566Sdes			    "r", ruser,
568262566Sdes			    "u", pw->pw_name,
569262566Sdes			    (char *)NULL);
570264377Sdes			if (result != 1) {
571264377Sdes				/* skip execution if prior predicate failed */
572264377Sdes				debug("%.200s line %d: skipped exec \"%.100s\"",
573262566Sdes				    filename, linenum, cmd);
574264377Sdes			} else {
575264377Sdes				r = execute_in_shell(cmd);
576264377Sdes				if (r == -1) {
577264377Sdes					fatal("%.200s line %d: match exec "
578264377Sdes					    "'%.100s' error", filename,
579264377Sdes					    linenum, cmd);
580264377Sdes				} else if (r == 0) {
581264377Sdes					debug("%.200s line %d: matched "
582264377Sdes					    "'exec \"%.100s\"'", filename,
583264377Sdes					    linenum, cmd);
584264377Sdes				} else {
585264377Sdes					debug("%.200s line %d: no match "
586264377Sdes					    "'exec \"%.100s\"'", filename,
587264377Sdes					    linenum, cmd);
588264377Sdes					result = 0;
589264377Sdes				}
590264377Sdes			}
591262566Sdes			free(cmd);
592262566Sdes		} else {
593262566Sdes			error("Unsupported Match attribute %s", attrib);
594262566Sdes			result = -1;
595262566Sdes			goto out;
596262566Sdes		}
597262566Sdes	}
598262566Sdes	if (attributes == 0) {
599262566Sdes		error("One or more attributes required for Match");
600262566Sdes		result = -1;
601262566Sdes		goto out;
602262566Sdes	}
603262566Sdes	debug3("match %sfound", result ? "" : "not ");
604262566Sdes	*condition = cp;
605262566Sdes out:
606262566Sdes	free(host);
607262566Sdes	return result;
608262566Sdes}
609262566Sdes
610262566Sdes/* Check and prepare a domain name: removes trailing '.' and lowercases */
611262566Sdesstatic void
612262566Sdesvalid_domain(char *name, const char *filename, int linenum)
613262566Sdes{
614262566Sdes	size_t i, l = strlen(name);
615262566Sdes	u_char c, last = '\0';
616262566Sdes
617262566Sdes	if (l == 0)
618262566Sdes		fatal("%s line %d: empty hostname suffix", filename, linenum);
619262566Sdes	if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
620262566Sdes		fatal("%s line %d: hostname suffix \"%.100s\" "
621262566Sdes		    "starts with invalid character", filename, linenum, name);
622262566Sdes	for (i = 0; i < l; i++) {
623262566Sdes		c = tolower((u_char)name[i]);
624262566Sdes		name[i] = (char)c;
625262566Sdes		if (last == '.' && c == '.')
626262566Sdes			fatal("%s line %d: hostname suffix \"%.100s\" contains "
627262566Sdes			    "consecutive separators", filename, linenum, name);
628262566Sdes		if (c != '.' && c != '-' && !isalnum(c) &&
629262566Sdes		    c != '_') /* technically invalid, but common */
630262566Sdes			fatal("%s line %d: hostname suffix \"%.100s\" contains "
631262566Sdes			    "invalid characters", filename, linenum, name);
632262566Sdes		last = c;
633262566Sdes	}
634262566Sdes	if (name[l - 1] == '.')
635262566Sdes		name[l - 1] = '\0';
636262566Sdes}
637262566Sdes
638262566Sdes/*
63976262Sgreen * Returns the number of the token pointed to by cp or oBadOption.
64057429Smarkm */
64160576Skrisstatic OpCodes
642255767Sdesparse_token(const char *cp, const char *filename, int linenum,
643255767Sdes    const char *ignored_unknown)
64457429Smarkm{
645255767Sdes	int i;
64657429Smarkm
64757429Smarkm	for (i = 0; keywords[i].name; i++)
648255767Sdes		if (strcmp(cp, keywords[i].name) == 0)
64957429Smarkm			return keywords[i].opcode;
650255767Sdes	if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown,
651255767Sdes	    strlen(ignored_unknown), 1) == 1)
652255767Sdes		return oIgnoredUnknownOption;
65376262Sgreen	error("%s: line %d: Bad configuration option: %s",
65476262Sgreen	    filename, linenum, cp);
65557429Smarkm	return oBadOption;
65657429Smarkm}
65757429Smarkm
658262566Sdes/* Multistate option parsing */
659262566Sdesstruct multistate {
660262566Sdes	char *key;
661262566Sdes	int value;
662262566Sdes};
663262566Sdesstatic const struct multistate multistate_flag[] = {
664262566Sdes	{ "true",			1 },
665262566Sdes	{ "false",			0 },
666262566Sdes	{ "yes",			1 },
667262566Sdes	{ "no",				0 },
668262566Sdes	{ NULL, -1 }
669262566Sdes};
670262566Sdesstatic const struct multistate multistate_yesnoask[] = {
671262566Sdes	{ "true",			1 },
672262566Sdes	{ "false",			0 },
673262566Sdes	{ "yes",			1 },
674262566Sdes	{ "no",				0 },
675262566Sdes	{ "ask",			2 },
676262566Sdes	{ NULL, -1 }
677262566Sdes};
678262566Sdesstatic const struct multistate multistate_addressfamily[] = {
679262566Sdes	{ "inet",			AF_INET },
680262566Sdes	{ "inet6",			AF_INET6 },
681262566Sdes	{ "any",			AF_UNSPEC },
682262566Sdes	{ NULL, -1 }
683262566Sdes};
684262566Sdesstatic const struct multistate multistate_controlmaster[] = {
685262566Sdes	{ "true",			SSHCTL_MASTER_YES },
686262566Sdes	{ "yes",			SSHCTL_MASTER_YES },
687262566Sdes	{ "false",			SSHCTL_MASTER_NO },
688262566Sdes	{ "no",				SSHCTL_MASTER_NO },
689262566Sdes	{ "auto",			SSHCTL_MASTER_AUTO },
690262566Sdes	{ "ask",			SSHCTL_MASTER_ASK },
691262566Sdes	{ "autoask",			SSHCTL_MASTER_AUTO_ASK },
692262566Sdes	{ NULL, -1 }
693262566Sdes};
694262566Sdesstatic const struct multistate multistate_tunnel[] = {
695262566Sdes	{ "ethernet",			SSH_TUNMODE_ETHERNET },
696262566Sdes	{ "point-to-point",		SSH_TUNMODE_POINTOPOINT },
697262566Sdes	{ "true",			SSH_TUNMODE_DEFAULT },
698262566Sdes	{ "yes",			SSH_TUNMODE_DEFAULT },
699262566Sdes	{ "false",			SSH_TUNMODE_NO },
700262566Sdes	{ "no",				SSH_TUNMODE_NO },
701262566Sdes	{ NULL, -1 }
702262566Sdes};
703262566Sdesstatic const struct multistate multistate_requesttty[] = {
704262566Sdes	{ "true",			REQUEST_TTY_YES },
705262566Sdes	{ "yes",			REQUEST_TTY_YES },
706262566Sdes	{ "false",			REQUEST_TTY_NO },
707262566Sdes	{ "no",				REQUEST_TTY_NO },
708262566Sdes	{ "force",			REQUEST_TTY_FORCE },
709262566Sdes	{ "auto",			REQUEST_TTY_AUTO },
710262566Sdes	{ NULL, -1 }
711262566Sdes};
712262566Sdesstatic const struct multistate multistate_canonicalizehostname[] = {
713262566Sdes	{ "true",			SSH_CANONICALISE_YES },
714262566Sdes	{ "false",			SSH_CANONICALISE_NO },
715262566Sdes	{ "yes",			SSH_CANONICALISE_YES },
716262566Sdes	{ "no",				SSH_CANONICALISE_NO },
717262566Sdes	{ "always",			SSH_CANONICALISE_ALWAYS },
718262566Sdes	{ NULL, -1 }
719262566Sdes};
720262566Sdes
72157429Smarkm/*
72257429Smarkm * Processes a single option line as used in the configuration files. This
72357429Smarkm * only sets those values that have not already been set.
72457429Smarkm */
725113911Sdes#define WHITESPACE " \t\r\n"
72657429Smarkmint
727262566Sdesprocess_config_line(Options *options, struct passwd *pw, const char *host,
728262566Sdes    char *line, const char *filename, int linenum, int *activep, int userconfig)
72957429Smarkm{
730226046Sdes	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
731226046Sdes	char **cpptr, fwdarg[256];
732255767Sdes	u_int i, *uintptr, max_entries = 0;
733262566Sdes	int negated, opcode, *intptr, value, value2, cmdline = 0;
734181111Sdes	LogLevel *log_level_ptr;
735255767Sdes	long long val64;
736113911Sdes	size_t len;
737147005Sdes	Forward fwd;
738262566Sdes	const struct multistate *multistate_ptr;
739262566Sdes	struct allowed_cname *cname;
74057429Smarkm
741262566Sdes	if (activep == NULL) { /* We are processing a command line directive */
742262566Sdes		cmdline = 1;
743262566Sdes		activep = &cmdline;
744262566Sdes	}
745262566Sdes
746124211Sdes	/* Strip trailing whitespace */
747147005Sdes	for (len = strlen(line) - 1; len > 0; len--) {
748124211Sdes		if (strchr(WHITESPACE, line[len]) == NULL)
749124211Sdes			break;
750124211Sdes		line[len] = '\0';
751124211Sdes	}
752124211Sdes
75365674Skris	s = line;
75465674Skris	/* Get the keyword. (Each line is supposed to begin with a keyword). */
755162856Sdes	if ((keyword = strdelim(&s)) == NULL)
756162856Sdes		return 0;
75765674Skris	/* Ignore leading whitespace. */
75865674Skris	if (*keyword == '\0')
75965674Skris		keyword = strdelim(&s);
76076262Sgreen	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
76157429Smarkm		return 0;
762255767Sdes	/* Match lowercase keyword */
763262566Sdes	lowercase(keyword);
76457429Smarkm
765255767Sdes	opcode = parse_token(keyword, filename, linenum,
766255767Sdes	    options->ignored_unknown);
76757429Smarkm
76857429Smarkm	switch (opcode) {
76957429Smarkm	case oBadOption:
77057429Smarkm		/* don't panic, but count bad options */
77157429Smarkm		return -1;
77257429Smarkm		/* NOTREACHED */
773255767Sdes	case oIgnoredUnknownOption:
774255767Sdes		debug("%s line %d: Ignored unknown option \"%s\"",
775255767Sdes		    filename, linenum, keyword);
776255767Sdes		return 0;
777124211Sdes	case oConnectTimeout:
778124211Sdes		intptr = &options->connection_timeout;
779126277Sdesparse_time:
780124211Sdes		arg = strdelim(&s);
781124211Sdes		if (!arg || *arg == '\0')
782124211Sdes			fatal("%s line %d: missing time value.",
783124211Sdes			    filename, linenum);
784124211Sdes		if ((value = convtime(arg)) == -1)
785124211Sdes			fatal("%s line %d: invalid time value.",
786124211Sdes			    filename, linenum);
787181111Sdes		if (*activep && *intptr == -1)
788124211Sdes			*intptr = value;
789124211Sdes		break;
790124211Sdes
79157429Smarkm	case oForwardAgent:
79257429Smarkm		intptr = &options->forward_agent;
793262566Sdes parse_flag:
794262566Sdes		multistate_ptr = multistate_flag;
795262566Sdes parse_multistate:
79665674Skris		arg = strdelim(&s);
79765674Skris		if (!arg || *arg == '\0')
798262566Sdes			fatal("%s line %d: missing argument.",
799262566Sdes			    filename, linenum);
800262566Sdes		value = -1;
801262566Sdes		for (i = 0; multistate_ptr[i].key != NULL; i++) {
802262566Sdes			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
803262566Sdes				value = multistate_ptr[i].value;
804262566Sdes				break;
805262566Sdes			}
806262566Sdes		}
807262566Sdes		if (value == -1)
808262566Sdes			fatal("%s line %d: unsupported option \"%s\".",
809262566Sdes			    filename, linenum, arg);
81057429Smarkm		if (*activep && *intptr == -1)
81157429Smarkm			*intptr = value;
81257429Smarkm		break;
81357429Smarkm
81457429Smarkm	case oForwardX11:
81557429Smarkm		intptr = &options->forward_x11;
81657429Smarkm		goto parse_flag;
81757429Smarkm
818126277Sdes	case oForwardX11Trusted:
819126277Sdes		intptr = &options->forward_x11_trusted;
820126277Sdes		goto parse_flag;
821215116Sdes
822215116Sdes	case oForwardX11Timeout:
823215116Sdes		intptr = &options->forward_x11_timeout;
824215116Sdes		goto parse_time;
825126277Sdes
82657429Smarkm	case oGatewayPorts:
82757429Smarkm		intptr = &options->gateway_ports;
82857429Smarkm		goto parse_flag;
82957429Smarkm
830162856Sdes	case oExitOnForwardFailure:
831162856Sdes		intptr = &options->exit_on_forward_failure;
832162856Sdes		goto parse_flag;
833162856Sdes
83457429Smarkm	case oUsePrivilegedPort:
83557429Smarkm		intptr = &options->use_privileged_port;
83657429Smarkm		goto parse_flag;
83757429Smarkm
83857429Smarkm	case oPasswordAuthentication:
83957429Smarkm		intptr = &options->password_authentication;
84057429Smarkm		goto parse_flag;
84157429Smarkm
84269591Sgreen	case oKbdInteractiveAuthentication:
84369591Sgreen		intptr = &options->kbd_interactive_authentication;
84469591Sgreen		goto parse_flag;
84569591Sgreen
84669591Sgreen	case oKbdInteractiveDevices:
84769591Sgreen		charptr = &options->kbd_interactive_devices;
84869591Sgreen		goto parse_string;
84969591Sgreen
85076262Sgreen	case oPubkeyAuthentication:
85176262Sgreen		intptr = &options->pubkey_authentication;
85260576Skris		goto parse_flag;
85360576Skris
85457429Smarkm	case oRSAAuthentication:
85557429Smarkm		intptr = &options->rsa_authentication;
85657429Smarkm		goto parse_flag;
85757429Smarkm
85857429Smarkm	case oRhostsRSAAuthentication:
85957429Smarkm		intptr = &options->rhosts_rsa_authentication;
86057429Smarkm		goto parse_flag;
86157429Smarkm
86276262Sgreen	case oHostbasedAuthentication:
86376262Sgreen		intptr = &options->hostbased_authentication;
86457429Smarkm		goto parse_flag;
86557429Smarkm
86692559Sdes	case oChallengeResponseAuthentication:
86792559Sdes		intptr = &options->challenge_response_authentication;
86892559Sdes		goto parse_flag;
869124211Sdes
870124211Sdes	case oGssAuthentication:
871124211Sdes		intptr = &options->gss_authentication;
87257429Smarkm		goto parse_flag;
873124211Sdes
874124211Sdes	case oGssDelegateCreds:
875124211Sdes		intptr = &options->gss_deleg_creds;
87676262Sgreen		goto parse_flag;
877124211Sdes
87857429Smarkm	case oBatchMode:
87957429Smarkm		intptr = &options->batch_mode;
88057429Smarkm		goto parse_flag;
88157429Smarkm
88257429Smarkm	case oCheckHostIP:
88357429Smarkm		intptr = &options->check_host_ip;
88457429Smarkm		goto parse_flag;
88557429Smarkm
886124211Sdes	case oVerifyHostKeyDNS:
887124211Sdes		intptr = &options->verify_host_key_dns;
888262566Sdes		multistate_ptr = multistate_yesnoask;
889262566Sdes		goto parse_multistate;
890124211Sdes
89157429Smarkm	case oStrictHostKeyChecking:
89257429Smarkm		intptr = &options->strict_host_key_checking;
893262566Sdes		multistate_ptr = multistate_yesnoask;
894262566Sdes		goto parse_multistate;
89557429Smarkm
89657429Smarkm	case oCompression:
89757429Smarkm		intptr = &options->compression;
89857429Smarkm		goto parse_flag;
89957429Smarkm
900126277Sdes	case oTCPKeepAlive:
901126277Sdes		intptr = &options->tcp_keep_alive;
90257429Smarkm		goto parse_flag;
90357429Smarkm
90492559Sdes	case oNoHostAuthenticationForLocalhost:
90592559Sdes		intptr = &options->no_host_authentication_for_localhost;
90692559Sdes		goto parse_flag;
90792559Sdes
90857429Smarkm	case oNumberOfPasswordPrompts:
90957429Smarkm		intptr = &options->number_of_password_prompts;
91057429Smarkm		goto parse_int;
91157429Smarkm
91257429Smarkm	case oCompressionLevel:
91357429Smarkm		intptr = &options->compression_level;
91457429Smarkm		goto parse_int;
91557429Smarkm
916124211Sdes	case oRekeyLimit:
917124211Sdes		arg = strdelim(&s);
918124211Sdes		if (!arg || *arg == '\0')
919255767Sdes			fatal("%.200s line %d: Missing argument.", filename,
920255767Sdes			    linenum);
921255767Sdes		if (strcmp(arg, "default") == 0) {
922255767Sdes			val64 = 0;
923255767Sdes		} else {
924255767Sdes			if (scan_scaled(arg, &val64) == -1)
925255767Sdes				fatal("%.200s line %d: Bad number '%s': %s",
926255767Sdes				    filename, linenum, arg, strerror(errno));
927255767Sdes			/* check for too-large or too-small limits */
928255767Sdes			if (val64 > UINT_MAX)
929255767Sdes				fatal("%.200s line %d: RekeyLimit too large",
930255767Sdes				    filename, linenum);
931255767Sdes			if (val64 != 0 && val64 < 16)
932255767Sdes				fatal("%.200s line %d: RekeyLimit too small",
933255767Sdes				    filename, linenum);
934124211Sdes		}
935181111Sdes		if (*activep && options->rekey_limit == -1)
936181111Sdes			options->rekey_limit = (u_int32_t)val64;
937255767Sdes		if (s != NULL) { /* optional rekey interval present */
938255767Sdes			if (strcmp(s, "none") == 0) {
939255767Sdes				(void)strdelim(&s);	/* discard */
940255767Sdes				break;
941255767Sdes			}
942255767Sdes			intptr = &options->rekey_interval;
943255767Sdes			goto parse_time;
944255767Sdes		}
945124211Sdes		break;
946124211Sdes
94757429Smarkm	case oIdentityFile:
94865674Skris		arg = strdelim(&s);
94965674Skris		if (!arg || *arg == '\0')
95057429Smarkm			fatal("%.200s line %d: Missing argument.", filename, linenum);
95157429Smarkm		if (*activep) {
95276262Sgreen			intptr = &options->num_identity_files;
95360576Skris			if (*intptr >= SSH_MAX_IDENTITY_FILES)
95457429Smarkm				fatal("%.200s line %d: Too many identity files specified (max %d).",
95592559Sdes				    filename, linenum, SSH_MAX_IDENTITY_FILES);
956249839Sdes			add_identity_file(options, NULL, arg, userconfig);
95757429Smarkm		}
95857429Smarkm		break;
95957429Smarkm
96065674Skris	case oXAuthLocation:
96165674Skris		charptr=&options->xauth_location;
96265674Skris		goto parse_string;
96365674Skris
96457429Smarkm	case oUser:
96557429Smarkm		charptr = &options->user;
96657429Smarkmparse_string:
96765674Skris		arg = strdelim(&s);
96865674Skris		if (!arg || *arg == '\0')
969226046Sdes			fatal("%.200s line %d: Missing argument.",
970226046Sdes			    filename, linenum);
97157429Smarkm		if (*activep && *charptr == NULL)
97265674Skris			*charptr = xstrdup(arg);
97357429Smarkm		break;
97457429Smarkm
97557429Smarkm	case oGlobalKnownHostsFile:
976226046Sdes		cpptr = (char **)&options->system_hostfiles;
977226046Sdes		uintptr = &options->num_system_hostfiles;
978226046Sdes		max_entries = SSH_MAX_HOSTS_FILES;
979226046Sdesparse_char_array:
980226046Sdes		if (*activep && *uintptr == 0) {
981226046Sdes			while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
982226046Sdes				if ((*uintptr) >= max_entries)
983226046Sdes					fatal("%s line %d: "
984226046Sdes					    "too many authorized keys files.",
985226046Sdes					    filename, linenum);
986226046Sdes				cpptr[(*uintptr)++] = xstrdup(arg);
987226046Sdes			}
988226046Sdes		}
989226046Sdes		return 0;
99057429Smarkm
99157429Smarkm	case oUserKnownHostsFile:
992226046Sdes		cpptr = (char **)&options->user_hostfiles;
993226046Sdes		uintptr = &options->num_user_hostfiles;
994226046Sdes		max_entries = SSH_MAX_HOSTS_FILES;
995226046Sdes		goto parse_char_array;
99657429Smarkm
99757429Smarkm	case oHostName:
99857429Smarkm		charptr = &options->hostname;
99957429Smarkm		goto parse_string;
100057429Smarkm
100176262Sgreen	case oHostKeyAlias:
100276262Sgreen		charptr = &options->host_key_alias;
100376262Sgreen		goto parse_string;
100476262Sgreen
100576262Sgreen	case oPreferredAuthentications:
100676262Sgreen		charptr = &options->preferred_authentications;
100776262Sgreen		goto parse_string;
100876262Sgreen
100992559Sdes	case oBindAddress:
101092559Sdes		charptr = &options->bind_address;
101192559Sdes		goto parse_string;
101292559Sdes
1013204917Sdes	case oPKCS11Provider:
1014204917Sdes		charptr = &options->pkcs11_provider;
101592559Sdes		goto parse_string;
101692559Sdes
101757429Smarkm	case oProxyCommand:
1018157019Sdes		charptr = &options->proxy_command;
1019157019Sdesparse_command:
1020124211Sdes		if (s == NULL)
1021124211Sdes			fatal("%.200s line %d: Missing argument.", filename, linenum);
1022113911Sdes		len = strspn(s, WHITESPACE "=");
102357429Smarkm		if (*activep && *charptr == NULL)
1024113911Sdes			*charptr = xstrdup(s + len);
102557429Smarkm		return 0;
102657429Smarkm
102757429Smarkm	case oPort:
102857429Smarkm		intptr = &options->port;
102957429Smarkmparse_int:
103065674Skris		arg = strdelim(&s);
103165674Skris		if (!arg || *arg == '\0')
103257429Smarkm			fatal("%.200s line %d: Missing argument.", filename, linenum);
103365674Skris		if (arg[0] < '0' || arg[0] > '9')
103457429Smarkm			fatal("%.200s line %d: Bad number.", filename, linenum);
103557429Smarkm
103657429Smarkm		/* Octal, decimal, or hex format? */
103765674Skris		value = strtol(arg, &endofnumber, 0);
103865674Skris		if (arg == endofnumber)
103957429Smarkm			fatal("%.200s line %d: Bad number.", filename, linenum);
104057429Smarkm		if (*activep && *intptr == -1)
104157429Smarkm			*intptr = value;
104257429Smarkm		break;
104357429Smarkm
104457429Smarkm	case oConnectionAttempts:
104557429Smarkm		intptr = &options->connection_attempts;
104657429Smarkm		goto parse_int;
104757429Smarkm
104857429Smarkm	case oCipher:
104957429Smarkm		intptr = &options->cipher;
105065674Skris		arg = strdelim(&s);
105165674Skris		if (!arg || *arg == '\0')
105261203Skris			fatal("%.200s line %d: Missing argument.", filename, linenum);
105365674Skris		value = cipher_number(arg);
105457429Smarkm		if (value == -1)
105557429Smarkm			fatal("%.200s line %d: Bad cipher '%s'.",
105692559Sdes			    filename, linenum, arg ? arg : "<NONE>");
105757429Smarkm		if (*activep && *intptr == -1)
105857429Smarkm			*intptr = value;
105957429Smarkm		break;
106057429Smarkm
106160576Skris	case oCiphers:
106265674Skris		arg = strdelim(&s);
106365674Skris		if (!arg || *arg == '\0')
106461203Skris			fatal("%.200s line %d: Missing argument.", filename, linenum);
106565674Skris		if (!ciphers_valid(arg))
106660576Skris			fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.",
106792559Sdes			    filename, linenum, arg ? arg : "<NONE>");
106860576Skris		if (*activep && options->ciphers == NULL)
106965674Skris			options->ciphers = xstrdup(arg);
107060576Skris		break;
107160576Skris
107276262Sgreen	case oMacs:
107376262Sgreen		arg = strdelim(&s);
107476262Sgreen		if (!arg || *arg == '\0')
107576262Sgreen			fatal("%.200s line %d: Missing argument.", filename, linenum);
107676262Sgreen		if (!mac_valid(arg))
107776262Sgreen			fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.",
107892559Sdes			    filename, linenum, arg ? arg : "<NONE>");
107976262Sgreen		if (*activep && options->macs == NULL)
108076262Sgreen			options->macs = xstrdup(arg);
108176262Sgreen		break;
108276262Sgreen
1083221420Sdes	case oKexAlgorithms:
1084221420Sdes		arg = strdelim(&s);
1085221420Sdes		if (!arg || *arg == '\0')
1086221420Sdes			fatal("%.200s line %d: Missing argument.",
1087221420Sdes			    filename, linenum);
1088221420Sdes		if (!kex_names_valid(arg))
1089221420Sdes			fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
1090221420Sdes			    filename, linenum, arg ? arg : "<NONE>");
1091221420Sdes		if (*activep && options->kex_algorithms == NULL)
1092221420Sdes			options->kex_algorithms = xstrdup(arg);
1093221420Sdes		break;
1094221420Sdes
109576262Sgreen	case oHostKeyAlgorithms:
109676262Sgreen		arg = strdelim(&s);
109776262Sgreen		if (!arg || *arg == '\0')
109876262Sgreen			fatal("%.200s line %d: Missing argument.", filename, linenum);
109976262Sgreen		if (!key_names_valid2(arg))
110076262Sgreen			fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.",
110192559Sdes			    filename, linenum, arg ? arg : "<NONE>");
110276262Sgreen		if (*activep && options->hostkeyalgorithms == NULL)
110376262Sgreen			options->hostkeyalgorithms = xstrdup(arg);
110476262Sgreen		break;
110576262Sgreen
110660576Skris	case oProtocol:
110760576Skris		intptr = &options->protocol;
110865674Skris		arg = strdelim(&s);
110965674Skris		if (!arg || *arg == '\0')
111061203Skris			fatal("%.200s line %d: Missing argument.", filename, linenum);
111165674Skris		value = proto_spec(arg);
111260576Skris		if (value == SSH_PROTO_UNKNOWN)
111360576Skris			fatal("%.200s line %d: Bad protocol spec '%s'.",
111492559Sdes			    filename, linenum, arg ? arg : "<NONE>");
111560576Skris		if (*activep && *intptr == SSH_PROTO_UNKNOWN)
111660576Skris			*intptr = value;
111760576Skris		break;
111860576Skris
111957429Smarkm	case oLogLevel:
1120181111Sdes		log_level_ptr = &options->log_level;
112165674Skris		arg = strdelim(&s);
112265674Skris		value = log_level_number(arg);
112392559Sdes		if (value == SYSLOG_LEVEL_NOT_SET)
112476262Sgreen			fatal("%.200s line %d: unsupported log level '%s'",
112592559Sdes			    filename, linenum, arg ? arg : "<NONE>");
1126181111Sdes		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
1127181111Sdes			*log_level_ptr = (LogLevel) value;
112857429Smarkm		break;
112957429Smarkm
113092559Sdes	case oLocalForward:
113157429Smarkm	case oRemoteForward:
1132192595Sdes	case oDynamicForward:
113365674Skris		arg = strdelim(&s);
1134147005Sdes		if (arg == NULL || *arg == '\0')
113592559Sdes			fatal("%.200s line %d: Missing port argument.",
113692559Sdes			    filename, linenum);
1137147005Sdes
1138192595Sdes		if (opcode == oLocalForward ||
1139192595Sdes		    opcode == oRemoteForward) {
1140192595Sdes			arg2 = strdelim(&s);
1141192595Sdes			if (arg2 == NULL || *arg2 == '\0')
1142192595Sdes				fatal("%.200s line %d: Missing target argument.",
1143192595Sdes				    filename, linenum);
1144147005Sdes
1145192595Sdes			/* construct a string for parse_forward */
1146192595Sdes			snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2);
1147192595Sdes		} else if (opcode == oDynamicForward) {
1148192595Sdes			strlcpy(fwdarg, arg, sizeof(fwdarg));
1149192595Sdes		}
1150192595Sdes
1151192595Sdes		if (parse_forward(&fwd, fwdarg,
1152192595Sdes		    opcode == oDynamicForward ? 1 : 0,
1153192595Sdes		    opcode == oRemoteForward ? 1 : 0) == 0)
115492559Sdes			fatal("%.200s line %d: Bad forwarding specification.",
115592559Sdes			    filename, linenum);
1156147005Sdes
115792559Sdes		if (*activep) {
1158192595Sdes			if (opcode == oLocalForward ||
1159192595Sdes			    opcode == oDynamicForward)
1160147005Sdes				add_local_forward(options, &fwd);
116192559Sdes			else if (opcode == oRemoteForward)
1162147005Sdes				add_remote_forward(options, &fwd);
116392559Sdes		}
116457429Smarkm		break;
116557429Smarkm
116692559Sdes	case oClearAllForwardings:
116792559Sdes		intptr = &options->clear_forwardings;
116892559Sdes		goto parse_flag;
116992559Sdes
117057429Smarkm	case oHost:
1171262566Sdes		if (cmdline)
1172262566Sdes			fatal("Host directive not supported as a command-line "
1173262566Sdes			    "option");
117457429Smarkm		*activep = 0;
1175226046Sdes		arg2 = NULL;
1176226046Sdes		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1177226046Sdes			negated = *arg == '!';
1178226046Sdes			if (negated)
1179226046Sdes				arg++;
118065674Skris			if (match_pattern(host, arg)) {
1181226046Sdes				if (negated) {
1182226046Sdes					debug("%.200s line %d: Skipping Host "
1183226046Sdes					    "block because of negated match "
1184226046Sdes					    "for %.100s", filename, linenum,
1185226046Sdes					    arg);
1186226046Sdes					*activep = 0;
1187226046Sdes					break;
1188226046Sdes				}
1189226046Sdes				if (!*activep)
1190226046Sdes					arg2 = arg; /* logged below */
119157429Smarkm				*activep = 1;
119257429Smarkm			}
1193226046Sdes		}
1194226046Sdes		if (*activep)
1195226046Sdes			debug("%.200s line %d: Applying options for %.100s",
1196226046Sdes			    filename, linenum, arg2);
119765674Skris		/* Avoid garbage check below, as strdelim is done. */
119857429Smarkm		return 0;
119957429Smarkm
1200262566Sdes	case oMatch:
1201262566Sdes		if (cmdline)
1202262566Sdes			fatal("Host directive not supported as a command-line "
1203262566Sdes			    "option");
1204262566Sdes		value = match_cfg_line(options, &s, pw, host,
1205262566Sdes		    filename, linenum);
1206262566Sdes		if (value < 0)
1207262566Sdes			fatal("%.200s line %d: Bad Match condition", filename,
1208262566Sdes			    linenum);
1209262566Sdes		*activep = value;
1210262566Sdes		break;
1211262566Sdes
121257429Smarkm	case oEscapeChar:
121357429Smarkm		intptr = &options->escape_char;
121465674Skris		arg = strdelim(&s);
121565674Skris		if (!arg || *arg == '\0')
121657429Smarkm			fatal("%.200s line %d: Missing argument.", filename, linenum);
121765674Skris		if (arg[0] == '^' && arg[2] == 0 &&
121876262Sgreen		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
121976262Sgreen			value = (u_char) arg[1] & 31;
122065674Skris		else if (strlen(arg) == 1)
122176262Sgreen			value = (u_char) arg[0];
122265674Skris		else if (strcmp(arg, "none") == 0)
122392559Sdes			value = SSH_ESCAPECHAR_NONE;
122457429Smarkm		else {
122557429Smarkm			fatal("%.200s line %d: Bad escape character.",
122692559Sdes			    filename, linenum);
122757429Smarkm			/* NOTREACHED */
122857429Smarkm			value = 0;	/* Avoid compiler warning. */
122957429Smarkm		}
123057429Smarkm		if (*activep && *intptr == -1)
123157429Smarkm			*intptr = value;
123257429Smarkm		break;
123357429Smarkm
1234124211Sdes	case oAddressFamily:
1235124211Sdes		intptr = &options->address_family;
1236262566Sdes		multistate_ptr = multistate_addressfamily;
1237262566Sdes		goto parse_multistate;
1238124211Sdes
1239113911Sdes	case oEnableSSHKeysign:
1240113911Sdes		intptr = &options->enable_ssh_keysign;
1241113911Sdes		goto parse_flag;
1242113911Sdes
1243128460Sdes	case oIdentitiesOnly:
1244128460Sdes		intptr = &options->identities_only;
1245128460Sdes		goto parse_flag;
1246128460Sdes
1247126277Sdes	case oServerAliveInterval:
1248126277Sdes		intptr = &options->server_alive_interval;
1249126277Sdes		goto parse_time;
1250126277Sdes
1251126277Sdes	case oServerAliveCountMax:
1252126277Sdes		intptr = &options->server_alive_count_max;
1253126277Sdes		goto parse_int;
1254126277Sdes
1255137019Sdes	case oSendEnv:
1256137019Sdes		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1257137019Sdes			if (strchr(arg, '=') != NULL)
1258137019Sdes				fatal("%s line %d: Invalid environment name.",
1259137019Sdes				    filename, linenum);
1260147005Sdes			if (!*activep)
1261147005Sdes				continue;
1262137019Sdes			if (options->num_send_env >= MAX_SEND_ENV)
1263137019Sdes				fatal("%s line %d: too many send env.",
1264137019Sdes				    filename, linenum);
1265137019Sdes			options->send_env[options->num_send_env++] =
1266137019Sdes			    xstrdup(arg);
1267137019Sdes		}
1268137019Sdes		break;
1269137019Sdes
1270137019Sdes	case oControlPath:
1271137019Sdes		charptr = &options->control_path;
1272137019Sdes		goto parse_string;
1273137019Sdes
1274137019Sdes	case oControlMaster:
1275137019Sdes		intptr = &options->control_master;
1276262566Sdes		multistate_ptr = multistate_controlmaster;
1277262566Sdes		goto parse_multistate;
1278137019Sdes
1279215116Sdes	case oControlPersist:
1280215116Sdes		/* no/false/yes/true, or a time spec */
1281215116Sdes		intptr = &options->control_persist;
1282215116Sdes		arg = strdelim(&s);
1283215116Sdes		if (!arg || *arg == '\0')
1284215116Sdes			fatal("%.200s line %d: Missing ControlPersist"
1285215116Sdes			    " argument.", filename, linenum);
1286215116Sdes		value = 0;
1287215116Sdes		value2 = 0;	/* timeout */
1288215116Sdes		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
1289215116Sdes			value = 0;
1290215116Sdes		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
1291215116Sdes			value = 1;
1292215116Sdes		else if ((value2 = convtime(arg)) >= 0)
1293215116Sdes			value = 1;
1294215116Sdes		else
1295215116Sdes			fatal("%.200s line %d: Bad ControlPersist argument.",
1296215116Sdes			    filename, linenum);
1297215116Sdes		if (*activep && *intptr == -1) {
1298215116Sdes			*intptr = value;
1299215116Sdes			options->control_persist_timeout = value2;
1300215116Sdes		}
1301215116Sdes		break;
1302215116Sdes
1303147005Sdes	case oHashKnownHosts:
1304147005Sdes		intptr = &options->hash_known_hosts;
1305147005Sdes		goto parse_flag;
1306147005Sdes
1307157019Sdes	case oTunnel:
1308157019Sdes		intptr = &options->tun_open;
1309262566Sdes		multistate_ptr = multistate_tunnel;
1310262566Sdes		goto parse_multistate;
1311157019Sdes
1312157019Sdes	case oTunnelDevice:
1313157019Sdes		arg = strdelim(&s);
1314157019Sdes		if (!arg || *arg == '\0')
1315157019Sdes			fatal("%.200s line %d: Missing argument.", filename, linenum);
1316157019Sdes		value = a2tun(arg, &value2);
1317157019Sdes		if (value == SSH_TUNID_ERR)
1318157019Sdes			fatal("%.200s line %d: Bad tun device.", filename, linenum);
1319157019Sdes		if (*activep) {
1320157019Sdes			options->tun_local = value;
1321157019Sdes			options->tun_remote = value2;
1322157019Sdes		}
1323157019Sdes		break;
1324157019Sdes
1325157019Sdes	case oLocalCommand:
1326157019Sdes		charptr = &options->local_command;
1327157019Sdes		goto parse_command;
1328157019Sdes
1329157019Sdes	case oPermitLocalCommand:
1330157019Sdes		intptr = &options->permit_local_command;
1331157019Sdes		goto parse_flag;
1332157019Sdes
1333181111Sdes	case oVisualHostKey:
1334181111Sdes		intptr = &options->visual_host_key;
1335181111Sdes		goto parse_flag;
1336181111Sdes
1337221420Sdes	case oIPQoS:
1338221420Sdes		arg = strdelim(&s);
1339221420Sdes		if ((value = parse_ipqos(arg)) == -1)
1340221420Sdes			fatal("%s line %d: Bad IPQoS value: %s",
1341221420Sdes			    filename, linenum, arg);
1342221420Sdes		arg = strdelim(&s);
1343221420Sdes		if (arg == NULL)
1344221420Sdes			value2 = value;
1345221420Sdes		else if ((value2 = parse_ipqos(arg)) == -1)
1346221420Sdes			fatal("%s line %d: Bad IPQoS value: %s",
1347221420Sdes			    filename, linenum, arg);
1348221420Sdes		if (*activep) {
1349221420Sdes			options->ip_qos_interactive = value;
1350221420Sdes			options->ip_qos_bulk = value2;
1351221420Sdes		}
1352221420Sdes		break;
1353221420Sdes
1354197679Sdes	case oUseRoaming:
1355197679Sdes		intptr = &options->use_roaming;
1356197679Sdes		goto parse_flag;
1357197679Sdes
1358226046Sdes	case oRequestTTY:
1359226046Sdes		intptr = &options->request_tty;
1360262566Sdes		multistate_ptr = multistate_requesttty;
1361262566Sdes		goto parse_multistate;
136299048Sdes
1363224638Sbrooks	case oHPNDisabled:
1364224638Sbrooks		intptr = &options->hpn_disabled;
1365224638Sbrooks		goto parse_flag;
1366224638Sbrooks
1367224638Sbrooks	case oHPNBufferSize:
1368224638Sbrooks		intptr = &options->hpn_buffer_size;
1369224638Sbrooks		goto parse_int;
1370224638Sbrooks
1371224638Sbrooks	case oTcpRcvBufPoll:
1372224638Sbrooks		intptr = &options->tcp_rcv_buf_poll;
1373224638Sbrooks		goto parse_flag;
1374224638Sbrooks
1375224638Sbrooks	case oTcpRcvBuf:
1376224638Sbrooks		intptr = &options->tcp_rcv_buf;
1377224638Sbrooks		goto parse_int;
1378224638Sbrooks
1379224638Sbrooks#ifdef	NONE_CIPHER_ENABLED
1380224638Sbrooks	case oNoneEnabled:
1381224638Sbrooks		intptr = &options->none_enabled;
1382224638Sbrooks		goto parse_flag;
1383231584Sed
1384224638Sbrooks	/*
1385231584Sed	 * We check to see if the command comes from the command line or not.
1386224638Sbrooks	 * If it does then enable it otherwise fail.  NONE must never be a
1387224638Sbrooks	 * default configuration.
1388224638Sbrooks	 */
1389224638Sbrooks	case oNoneSwitch:
1390224638Sbrooks		if (strcmp(filename,"command-line") == 0) {
1391224638Sbrooks			intptr = &options->none_switch;
1392224638Sbrooks			goto parse_flag;
1393224638Sbrooks		} else {
1394224638Sbrooks			debug("NoneSwitch directive found in %.200s.",
1395224638Sbrooks			    filename);
1396224638Sbrooks			error("NoneSwitch is found in %.200s.\n"
1397224638Sbrooks			    "You may only use this configuration option "
1398224638Sbrooks			    "from the command line", filename);
1399224638Sbrooks			error("Continuing...");
1400224638Sbrooks			return 0;
1401231584Sed		}
1402224638Sbrooks#endif
1403224638Sbrooks
1404226046Sdes	case oVersionAddendum:
1405240075Sdes		if (s == NULL)
1406240075Sdes			fatal("%.200s line %d: Missing argument.", filename,
1407240075Sdes			    linenum);
1408240075Sdes		len = strspn(s, WHITESPACE);
1409240075Sdes		if (*activep && options->version_addendum == NULL) {
1410240075Sdes			if (strcasecmp(s + len, "none") == 0)
1411240075Sdes				options->version_addendum = xstrdup("");
1412240075Sdes			else if (strchr(s + len, '\r') != NULL)
1413240075Sdes				fatal("%.200s line %d: Invalid argument",
1414240075Sdes				    filename, linenum);
1415240075Sdes			else
1416240075Sdes				options->version_addendum = xstrdup(s + len);
1417240075Sdes		}
1418240075Sdes		return 0;
1419226046Sdes
1420255767Sdes	case oIgnoreUnknown:
1421255767Sdes		charptr = &options->ignored_unknown;
1422255767Sdes		goto parse_string;
1423255767Sdes
1424262566Sdes	case oProxyUseFdpass:
1425262566Sdes		intptr = &options->proxy_use_fdpass;
1426262566Sdes		goto parse_flag;
1427262566Sdes
1428262566Sdes	case oCanonicalDomains:
1429262566Sdes		value = options->num_canonical_domains != 0;
1430262566Sdes		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1431262566Sdes			valid_domain(arg, filename, linenum);
1432262566Sdes			if (!*activep || value)
1433262566Sdes				continue;
1434262566Sdes			if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
1435262566Sdes				fatal("%s line %d: too many hostname suffixes.",
1436262566Sdes				    filename, linenum);
1437262566Sdes			options->canonical_domains[
1438262566Sdes			    options->num_canonical_domains++] = xstrdup(arg);
1439262566Sdes		}
1440262566Sdes		break;
1441262566Sdes
1442262566Sdes	case oCanonicalizePermittedCNAMEs:
1443262566Sdes		value = options->num_permitted_cnames != 0;
1444262566Sdes		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
1445262566Sdes			/* Either '*' for everything or 'list:list' */
1446262566Sdes			if (strcmp(arg, "*") == 0)
1447262566Sdes				arg2 = arg;
1448262566Sdes			else {
1449262566Sdes				lowercase(arg);
1450262566Sdes				if ((arg2 = strchr(arg, ':')) == NULL ||
1451262566Sdes				    arg2[1] == '\0') {
1452262566Sdes					fatal("%s line %d: "
1453262566Sdes					    "Invalid permitted CNAME \"%s\"",
1454262566Sdes					    filename, linenum, arg);
1455262566Sdes				}
1456262566Sdes				*arg2 = '\0';
1457262566Sdes				arg2++;
1458262566Sdes			}
1459262566Sdes			if (!*activep || value)
1460262566Sdes				continue;
1461262566Sdes			if (options->num_permitted_cnames >= MAX_CANON_DOMAINS)
1462262566Sdes				fatal("%s line %d: too many permitted CNAMEs.",
1463262566Sdes				    filename, linenum);
1464262566Sdes			cname = options->permitted_cnames +
1465262566Sdes			    options->num_permitted_cnames++;
1466262566Sdes			cname->source_list = xstrdup(arg);
1467262566Sdes			cname->target_list = xstrdup(arg2);
1468262566Sdes		}
1469262566Sdes		break;
1470262566Sdes
1471262566Sdes	case oCanonicalizeHostname:
1472262566Sdes		intptr = &options->canonicalize_hostname;
1473262566Sdes		multistate_ptr = multistate_canonicalizehostname;
1474262566Sdes		goto parse_multistate;
1475262566Sdes
1476262566Sdes	case oCanonicalizeMaxDots:
1477262566Sdes		intptr = &options->canonicalize_max_dots;
1478262566Sdes		goto parse_int;
1479262566Sdes
1480262566Sdes	case oCanonicalizeFallbackLocal:
1481262566Sdes		intptr = &options->canonicalize_fallback_local;
1482262566Sdes		goto parse_flag;
1483262566Sdes
148498684Sdes	case oDeprecated:
148598684Sdes		debug("%s line %d: Deprecated option \"%s\"",
148698684Sdes		    filename, linenum, keyword);
148798684Sdes		return 0;
148898684Sdes
1489124211Sdes	case oUnsupported:
1490124211Sdes		error("%s line %d: Unsupported option \"%s\"",
1491124211Sdes		    filename, linenum, keyword);
1492124211Sdes		return 0;
1493124211Sdes
149457429Smarkm	default:
149557429Smarkm		fatal("process_config_line: Unimplemented opcode %d", opcode);
149657429Smarkm	}
149757429Smarkm
149857429Smarkm	/* Check that there is no garbage at end of line. */
149976262Sgreen	if ((arg = strdelim(&s)) != NULL && *arg != '\0') {
150065674Skris		fatal("%.200s line %d: garbage at end of line; \"%.200s\".",
1501149753Sdes		    filename, linenum, arg);
150265674Skris	}
150357429Smarkm	return 0;
150457429Smarkm}
150557429Smarkm
150657429Smarkm
150757429Smarkm/*
150857429Smarkm * Reads the config file and modifies the options accordingly.  Options
150957429Smarkm * should already be initialized before this call.  This never returns if
151092559Sdes * there is an error.  If the file does not exist, this returns 0.
151157429Smarkm */
151257429Smarkm
151392559Sdesint
1514262566Sdesread_config_file(const char *filename, struct passwd *pw, const char *host,
1515262566Sdes    Options *options, int flags)
151657429Smarkm{
151757429Smarkm	FILE *f;
151857429Smarkm	char line[1024];
151957429Smarkm	int active, linenum;
152057429Smarkm	int bad_options = 0;
152157429Smarkm
1522137019Sdes	if ((f = fopen(filename, "r")) == NULL)
152392559Sdes		return 0;
152457429Smarkm
1525249839Sdes	if (flags & SSHCONF_CHECKPERM) {
1526137019Sdes		struct stat sb;
1527137019Sdes
1528137019Sdes		if (fstat(fileno(f), &sb) == -1)
1529137019Sdes			fatal("fstat %s: %s", filename, strerror(errno));
1530137019Sdes		if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
1531137019Sdes		    (sb.st_mode & 022) != 0))
1532137019Sdes			fatal("Bad owner or permissions on %s", filename);
1533137019Sdes	}
1534137019Sdes
153557429Smarkm	debug("Reading configuration data %.200s", filename);
153657429Smarkm
153757429Smarkm	/*
153857429Smarkm	 * Mark that we are now processing the options.  This flag is turned
153957429Smarkm	 * on/off by Host specifications.
154057429Smarkm	 */
154157429Smarkm	active = 1;
154257429Smarkm	linenum = 0;
154357429Smarkm	while (fgets(line, sizeof(line), f)) {
154457429Smarkm		/* Update line number counter. */
154557429Smarkm		linenum++;
1546262566Sdes		if (process_config_line(options, pw, host, line, filename,
1547262566Sdes		    linenum, &active, flags & SSHCONF_USERCONF) != 0)
154857429Smarkm			bad_options++;
154957429Smarkm	}
155057429Smarkm	fclose(f);
155157429Smarkm	if (bad_options > 0)
155276262Sgreen		fatal("%s: terminating, %d bad configuration options",
155392559Sdes		    filename, bad_options);
155492559Sdes	return 1;
155557429Smarkm}
155657429Smarkm
1557264377Sdes/* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
1558264377Sdesint
1559264377Sdesoption_clear_or_none(const char *o)
1560264377Sdes{
1561264377Sdes	return o == NULL || strcasecmp(o, "none") == 0;
1562264377Sdes}
1563264377Sdes
156457429Smarkm/*
156557429Smarkm * Initializes options to special values that indicate that they have not yet
156657429Smarkm * been set.  Read_config_file will only set options with this value. Options
156757429Smarkm * are processed in the following order: command line, user config file,
156857429Smarkm * system config file.  Last, fill_default_options is called.
156957429Smarkm */
157057429Smarkm
157160576Skrisvoid
157257429Smarkminitialize_options(Options * options)
157357429Smarkm{
157457429Smarkm	memset(options, 'X', sizeof(*options));
157557429Smarkm	options->forward_agent = -1;
157657429Smarkm	options->forward_x11 = -1;
1577126277Sdes	options->forward_x11_trusted = -1;
1578215116Sdes	options->forward_x11_timeout = -1;
1579162856Sdes	options->exit_on_forward_failure = -1;
158065674Skris	options->xauth_location = NULL;
158157429Smarkm	options->gateway_ports = -1;
158257429Smarkm	options->use_privileged_port = -1;
158357429Smarkm	options->rsa_authentication = -1;
158476262Sgreen	options->pubkey_authentication = -1;
158592559Sdes	options->challenge_response_authentication = -1;
1586124211Sdes	options->gss_authentication = -1;
1587124211Sdes	options->gss_deleg_creds = -1;
158857429Smarkm	options->password_authentication = -1;
158969591Sgreen	options->kbd_interactive_authentication = -1;
159069591Sgreen	options->kbd_interactive_devices = NULL;
159157429Smarkm	options->rhosts_rsa_authentication = -1;
159276262Sgreen	options->hostbased_authentication = -1;
159357429Smarkm	options->batch_mode = -1;
159457429Smarkm	options->check_host_ip = -1;
159557429Smarkm	options->strict_host_key_checking = -1;
159657429Smarkm	options->compression = -1;
1597126277Sdes	options->tcp_keep_alive = -1;
159857429Smarkm	options->compression_level = -1;
159957429Smarkm	options->port = -1;
1600124211Sdes	options->address_family = -1;
160157429Smarkm	options->connection_attempts = -1;
1602124211Sdes	options->connection_timeout = -1;
160357429Smarkm	options->number_of_password_prompts = -1;
160457429Smarkm	options->cipher = -1;
160560576Skris	options->ciphers = NULL;
160676262Sgreen	options->macs = NULL;
1607221420Sdes	options->kex_algorithms = NULL;
160876262Sgreen	options->hostkeyalgorithms = NULL;
160960576Skris	options->protocol = SSH_PROTO_UNKNOWN;
161057429Smarkm	options->num_identity_files = 0;
161157429Smarkm	options->hostname = NULL;
161276262Sgreen	options->host_key_alias = NULL;
161357429Smarkm	options->proxy_command = NULL;
161457429Smarkm	options->user = NULL;
161557429Smarkm	options->escape_char = -1;
1616226046Sdes	options->num_system_hostfiles = 0;
1617226046Sdes	options->num_user_hostfiles = 0;
1618215116Sdes	options->local_forwards = NULL;
161957429Smarkm	options->num_local_forwards = 0;
1620215116Sdes	options->remote_forwards = NULL;
162157429Smarkm	options->num_remote_forwards = 0;
162292559Sdes	options->clear_forwardings = -1;
162392559Sdes	options->log_level = SYSLOG_LEVEL_NOT_SET;
162476262Sgreen	options->preferred_authentications = NULL;
162592559Sdes	options->bind_address = NULL;
1626204917Sdes	options->pkcs11_provider = NULL;
1627113911Sdes	options->enable_ssh_keysign = - 1;
162892559Sdes	options->no_host_authentication_for_localhost = - 1;
1629128460Sdes	options->identities_only = - 1;
1630124211Sdes	options->rekey_limit = - 1;
1631255767Sdes	options->rekey_interval = -1;
1632124211Sdes	options->verify_host_key_dns = -1;
1633126277Sdes	options->server_alive_interval = -1;
1634126277Sdes	options->server_alive_count_max = -1;
1635137019Sdes	options->num_send_env = 0;
1636137019Sdes	options->control_path = NULL;
1637137019Sdes	options->control_master = -1;
1638215116Sdes	options->control_persist = -1;
1639215116Sdes	options->control_persist_timeout = 0;
1640147005Sdes	options->hash_known_hosts = -1;
1641157019Sdes	options->tun_open = -1;
1642157019Sdes	options->tun_local = -1;
1643157019Sdes	options->tun_remote = -1;
1644157019Sdes	options->local_command = NULL;
1645157019Sdes	options->permit_local_command = -1;
1646294051Sglebius	options->use_roaming = 0;
1647181111Sdes	options->visual_host_key = -1;
1648221420Sdes	options->ip_qos_interactive = -1;
1649221420Sdes	options->ip_qos_bulk = -1;
1650226046Sdes	options->request_tty = -1;
1651262566Sdes	options->proxy_use_fdpass = -1;
1652262566Sdes	options->ignored_unknown = NULL;
1653262566Sdes	options->num_canonical_domains = 0;
1654262566Sdes	options->num_permitted_cnames = 0;
1655262566Sdes	options->canonicalize_max_dots = -1;
1656262566Sdes	options->canonicalize_fallback_local = -1;
1657262566Sdes	options->canonicalize_hostname = -1;
1658240075Sdes	options->version_addendum = NULL;
1659224638Sbrooks	options->hpn_disabled = -1;
1660224638Sbrooks	options->hpn_buffer_size = -1;
1661224638Sbrooks	options->tcp_rcv_buf_poll = -1;
1662224638Sbrooks	options->tcp_rcv_buf = -1;
1663224638Sbrooks#ifdef NONE_CIPHER_ENABLED
1664224638Sbrooks	options->none_enabled = -1;
1665224638Sbrooks	options->none_switch = -1;
1666224638Sbrooks#endif
166757429Smarkm}
166857429Smarkm
166957429Smarkm/*
1670264377Sdes * A petite version of fill_default_options() that just fills the options
1671264377Sdes * needed for hostname canonicalization to proceed.
1672264377Sdes */
1673264377Sdesvoid
1674264377Sdesfill_default_options_for_canonicalization(Options *options)
1675264377Sdes{
1676264377Sdes	if (options->canonicalize_max_dots == -1)
1677264377Sdes		options->canonicalize_max_dots = 1;
1678264377Sdes	if (options->canonicalize_fallback_local == -1)
1679264377Sdes		options->canonicalize_fallback_local = 1;
1680264377Sdes	if (options->canonicalize_hostname == -1)
1681264377Sdes		options->canonicalize_hostname = SSH_CANONICALISE_NO;
1682264377Sdes}
1683264377Sdes
1684264377Sdes/*
168557429Smarkm * Called after processing other sources of option data, this fills those
168657429Smarkm * options for which no value has been specified with their default values.
168757429Smarkm */
168860576Skrisvoid
168957429Smarkmfill_default_options(Options * options)
169057429Smarkm{
169157429Smarkm	if (options->forward_agent == -1)
169261203Skris		options->forward_agent = 0;
169357429Smarkm	if (options->forward_x11 == -1)
169457708Sgreen		options->forward_x11 = 0;
1695126277Sdes	if (options->forward_x11_trusted == -1)
1696126277Sdes		options->forward_x11_trusted = 0;
1697215116Sdes	if (options->forward_x11_timeout == -1)
1698215116Sdes		options->forward_x11_timeout = 1200;
1699162856Sdes	if (options->exit_on_forward_failure == -1)
1700162856Sdes		options->exit_on_forward_failure = 0;
170165674Skris	if (options->xauth_location == NULL)
170292559Sdes		options->xauth_location = _PATH_XAUTH;
170357429Smarkm	if (options->gateway_ports == -1)
170457429Smarkm		options->gateway_ports = 0;
170557429Smarkm	if (options->use_privileged_port == -1)
170676262Sgreen		options->use_privileged_port = 0;
170757429Smarkm	if (options->rsa_authentication == -1)
170857429Smarkm		options->rsa_authentication = 1;
170976262Sgreen	if (options->pubkey_authentication == -1)
171076262Sgreen		options->pubkey_authentication = 1;
171192559Sdes	if (options->challenge_response_authentication == -1)
171292559Sdes		options->challenge_response_authentication = 1;
1713124211Sdes	if (options->gss_authentication == -1)
1714126277Sdes		options->gss_authentication = 0;
1715124211Sdes	if (options->gss_deleg_creds == -1)
1716124211Sdes		options->gss_deleg_creds = 0;
171757429Smarkm	if (options->password_authentication == -1)
171857429Smarkm		options->password_authentication = 1;
171969591Sgreen	if (options->kbd_interactive_authentication == -1)
172076262Sgreen		options->kbd_interactive_authentication = 1;
172157429Smarkm	if (options->rhosts_rsa_authentication == -1)
172298684Sdes		options->rhosts_rsa_authentication = 0;
172376262Sgreen	if (options->hostbased_authentication == -1)
172476262Sgreen		options->hostbased_authentication = 0;
172557429Smarkm	if (options->batch_mode == -1)
172657429Smarkm		options->batch_mode = 0;
172757429Smarkm	if (options->check_host_ip == -1)
172899048Sdes		options->check_host_ip = 0;
172957429Smarkm	if (options->strict_host_key_checking == -1)
173057429Smarkm		options->strict_host_key_checking = 2;	/* 2 is default */
173157429Smarkm	if (options->compression == -1)
173257429Smarkm		options->compression = 0;
1733126277Sdes	if (options->tcp_keep_alive == -1)
1734126277Sdes		options->tcp_keep_alive = 1;
173557429Smarkm	if (options->compression_level == -1)
173657429Smarkm		options->compression_level = 6;
173757429Smarkm	if (options->port == -1)
173857429Smarkm		options->port = 0;	/* Filled in ssh_connect. */
1739124211Sdes	if (options->address_family == -1)
1740124211Sdes		options->address_family = AF_UNSPEC;
174157429Smarkm	if (options->connection_attempts == -1)
174292559Sdes		options->connection_attempts = 1;
174357429Smarkm	if (options->number_of_password_prompts == -1)
174457429Smarkm		options->number_of_password_prompts = 3;
174557429Smarkm	/* Selected in ssh_login(). */
174657429Smarkm	if (options->cipher == -1)
174757429Smarkm		options->cipher = SSH_CIPHER_NOT_SET;
174860576Skris	/* options->ciphers, default set in myproposals.h */
174976262Sgreen	/* options->macs, default set in myproposals.h */
1750221420Sdes	/* options->kex_algorithms, default set in myproposals.h */
175176262Sgreen	/* options->hostkeyalgorithms, default set in myproposals.h */
175260576Skris	if (options->protocol == SSH_PROTO_UNKNOWN)
1753204917Sdes		options->protocol = SSH_PROTO_2;
175457429Smarkm	if (options->num_identity_files == 0) {
175576262Sgreen		if (options->protocol & SSH_PROTO_1) {
1756249839Sdes			add_identity_file(options, "~/",
1757249839Sdes			    _PATH_SSH_CLIENT_IDENTITY, 0);
175876262Sgreen		}
175976262Sgreen		if (options->protocol & SSH_PROTO_2) {
1760249839Sdes			add_identity_file(options, "~/",
1761249839Sdes			    _PATH_SSH_CLIENT_ID_RSA, 0);
1762249839Sdes			add_identity_file(options, "~/",
1763249839Sdes			    _PATH_SSH_CLIENT_ID_DSA, 0);
1764221420Sdes#ifdef OPENSSL_HAS_ECC
1765249839Sdes			add_identity_file(options, "~/",
1766249839Sdes			    _PATH_SSH_CLIENT_ID_ECDSA, 0);
1767221420Sdes#endif
1768262566Sdes			add_identity_file(options, "~/",
1769262566Sdes			    _PATH_SSH_CLIENT_ID_ED25519, 0);
177076262Sgreen		}
177157429Smarkm	}
177257429Smarkm	if (options->escape_char == -1)
177357429Smarkm		options->escape_char = '~';
1774226046Sdes	if (options->num_system_hostfiles == 0) {
1775226046Sdes		options->system_hostfiles[options->num_system_hostfiles++] =
1776226046Sdes		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE);
1777226046Sdes		options->system_hostfiles[options->num_system_hostfiles++] =
1778226046Sdes		    xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2);
1779226046Sdes	}
1780226046Sdes	if (options->num_user_hostfiles == 0) {
1781226046Sdes		options->user_hostfiles[options->num_user_hostfiles++] =
1782226046Sdes		    xstrdup(_PATH_SSH_USER_HOSTFILE);
1783226046Sdes		options->user_hostfiles[options->num_user_hostfiles++] =
1784226046Sdes		    xstrdup(_PATH_SSH_USER_HOSTFILE2);
1785226046Sdes	}
178692559Sdes	if (options->log_level == SYSLOG_LEVEL_NOT_SET)
178757429Smarkm		options->log_level = SYSLOG_LEVEL_INFO;
178892559Sdes	if (options->clear_forwardings == 1)
178992559Sdes		clear_forwardings(options);
179092559Sdes	if (options->no_host_authentication_for_localhost == - 1)
179192559Sdes		options->no_host_authentication_for_localhost = 0;
1792128460Sdes	if (options->identities_only == -1)
1793128460Sdes		options->identities_only = 0;
1794113911Sdes	if (options->enable_ssh_keysign == -1)
1795113911Sdes		options->enable_ssh_keysign = 0;
1796124211Sdes	if (options->rekey_limit == -1)
1797124211Sdes		options->rekey_limit = 0;
1798255767Sdes	if (options->rekey_interval == -1)
1799255767Sdes		options->rekey_interval = 0;
1800255461Sdes#if HAVE_LDNS
1801124211Sdes	if (options->verify_host_key_dns == -1)
1802255461Sdes		/* automatically trust a verified SSHFP record */
1803255461Sdes		options->verify_host_key_dns = 1;
1804255461Sdes#else
1805255461Sdes	if (options->verify_host_key_dns == -1)
1806124211Sdes		options->verify_host_key_dns = 0;
1807255461Sdes#endif
1808126277Sdes	if (options->server_alive_interval == -1)
1809126277Sdes		options->server_alive_interval = 0;
1810126277Sdes	if (options->server_alive_count_max == -1)
1811126277Sdes		options->server_alive_count_max = 3;
1812137019Sdes	if (options->control_master == -1)
1813137019Sdes		options->control_master = 0;
1814215116Sdes	if (options->control_persist == -1) {
1815215116Sdes		options->control_persist = 0;
1816215116Sdes		options->control_persist_timeout = 0;
1817215116Sdes	}
1818147005Sdes	if (options->hash_known_hosts == -1)
1819147005Sdes		options->hash_known_hosts = 0;
1820157019Sdes	if (options->tun_open == -1)
1821157019Sdes		options->tun_open = SSH_TUNMODE_NO;
1822157019Sdes	if (options->tun_local == -1)
1823157019Sdes		options->tun_local = SSH_TUNID_ANY;
1824157019Sdes	if (options->tun_remote == -1)
1825157019Sdes		options->tun_remote = SSH_TUNID_ANY;
1826157019Sdes	if (options->permit_local_command == -1)
1827157019Sdes		options->permit_local_command = 0;
1828294051Sglebius	options->use_roaming = 0;
1829181111Sdes	if (options->visual_host_key == -1)
1830181111Sdes		options->visual_host_key = 0;
1831221420Sdes	if (options->ip_qos_interactive == -1)
1832221420Sdes		options->ip_qos_interactive = IPTOS_LOWDELAY;
1833221420Sdes	if (options->ip_qos_bulk == -1)
1834221420Sdes		options->ip_qos_bulk = IPTOS_THROUGHPUT;
1835226046Sdes	if (options->request_tty == -1)
1836226046Sdes		options->request_tty = REQUEST_TTY_AUTO;
1837262566Sdes	if (options->proxy_use_fdpass == -1)
1838262566Sdes		options->proxy_use_fdpass = 0;
1839262566Sdes	if (options->canonicalize_max_dots == -1)
1840262566Sdes		options->canonicalize_max_dots = 1;
1841262566Sdes	if (options->canonicalize_fallback_local == -1)
1842262566Sdes		options->canonicalize_fallback_local = 1;
1843262566Sdes	if (options->canonicalize_hostname == -1)
1844262566Sdes		options->canonicalize_hostname = SSH_CANONICALISE_NO;
1845262566Sdes#define CLEAR_ON_NONE(v) \
1846262566Sdes	do { \
1847264377Sdes		if (option_clear_or_none(v)) { \
1848262566Sdes			free(v); \
1849262566Sdes			v = NULL; \
1850262566Sdes		} \
1851262566Sdes	} while(0)
1852262566Sdes	CLEAR_ON_NONE(options->local_command);
1853262566Sdes	CLEAR_ON_NONE(options->proxy_command);
1854262566Sdes	CLEAR_ON_NONE(options->control_path);
185557429Smarkm	/* options->user will be set in the main program if appropriate */
185657429Smarkm	/* options->hostname will be set in the main program if appropriate */
185776262Sgreen	/* options->host_key_alias should not be set by default */
185876262Sgreen	/* options->preferred_authentications will be set in ssh */
1859240075Sdes	if (options->version_addendum == NULL)
1860240075Sdes		options->version_addendum = xstrdup(SSH_VERSION_FREEBSD);
1861224638Sbrooks	if (options->hpn_disabled == -1)
1862231584Sed		options->hpn_disabled = 0;
1863224638Sbrooks	if (options->hpn_buffer_size > -1)
1864224638Sbrooks	{
1865224638Sbrooks		u_int maxlen;
1866224638Sbrooks
1867224638Sbrooks		/* If a user tries to set the size to 0 set it to 1KB. */
1868224638Sbrooks		if (options->hpn_buffer_size == 0)
1869224638Sbrooks			options->hpn_buffer_size = 1024;
1870224638Sbrooks		/* Limit the buffer to BUFFER_MAX_LEN. */
1871224638Sbrooks		maxlen = buffer_get_max_len();
1872224638Sbrooks		if (options->hpn_buffer_size > (maxlen / 1024)) {
1873224638Sbrooks			debug("User requested buffer larger than %ub: %ub. "
1874224638Sbrooks			    "Request reverted to %ub", maxlen,
1875224638Sbrooks			    options->hpn_buffer_size * 1024, maxlen);
1876224638Sbrooks			options->hpn_buffer_size = maxlen;
1877224638Sbrooks		}
1878224638Sbrooks		debug("hpn_buffer_size set to %d", options->hpn_buffer_size);
1879224638Sbrooks	}
1880224638Sbrooks	if (options->tcp_rcv_buf == 0)
1881224638Sbrooks		options->tcp_rcv_buf = 1;
1882231584Sed	if (options->tcp_rcv_buf > -1)
1883224638Sbrooks		options->tcp_rcv_buf *= 1024;
1884224638Sbrooks	if (options->tcp_rcv_buf_poll == -1)
1885224638Sbrooks		options->tcp_rcv_buf_poll = 1;
1886224638Sbrooks#ifdef	NONE_CIPHER_ENABLED
1887224638Sbrooks	/* options->none_enabled must not be set by default */
1888224638Sbrooks	if (options->none_switch == -1)
1889224638Sbrooks		options->none_switch = 0;
1890224638Sbrooks#endif
189157429Smarkm}
1892147005Sdes
1893147005Sdes/*
1894147005Sdes * parse_forward
1895147005Sdes * parses a string containing a port forwarding specification of the form:
1896192595Sdes *   dynamicfwd == 0
1897147005Sdes *	[listenhost:]listenport:connecthost:connectport
1898192595Sdes *   dynamicfwd == 1
1899192595Sdes *	[listenhost:]listenport
1900147005Sdes * returns number of arguments parsed or zero on error
1901147005Sdes */
1902147005Sdesint
1903192595Sdesparse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
1904147005Sdes{
1905147005Sdes	int i;
1906147005Sdes	char *p, *cp, *fwdarg[4];
1907147005Sdes
1908147005Sdes	memset(fwd, '\0', sizeof(*fwd));
1909147005Sdes
1910147005Sdes	cp = p = xstrdup(fwdspec);
1911147005Sdes
1912147005Sdes	/* skip leading spaces */
1913262566Sdes	while (isspace((u_char)*cp))
1914147005Sdes		cp++;
1915147005Sdes
1916147005Sdes	for (i = 0; i < 4; ++i)
1917147005Sdes		if ((fwdarg[i] = hpdelim(&cp)) == NULL)
1918147005Sdes			break;
1919147005Sdes
1920192595Sdes	/* Check for trailing garbage */
1921147005Sdes	if (cp != NULL)
1922147005Sdes		i = 0;	/* failure */
1923147005Sdes
1924147005Sdes	switch (i) {
1925192595Sdes	case 1:
1926192595Sdes		fwd->listen_host = NULL;
1927192595Sdes		fwd->listen_port = a2port(fwdarg[0]);
1928192595Sdes		fwd->connect_host = xstrdup("socks");
1929192595Sdes		break;
1930192595Sdes
1931192595Sdes	case 2:
1932192595Sdes		fwd->listen_host = xstrdup(cleanhostname(fwdarg[0]));
1933192595Sdes		fwd->listen_port = a2port(fwdarg[1]);
1934192595Sdes		fwd->connect_host = xstrdup("socks");
1935192595Sdes		break;
1936192595Sdes
1937147005Sdes	case 3:
1938147005Sdes		fwd->listen_host = NULL;
1939147005Sdes		fwd->listen_port = a2port(fwdarg[0]);
1940147005Sdes		fwd->connect_host = xstrdup(cleanhostname(fwdarg[1]));
1941147005Sdes		fwd->connect_port = a2port(fwdarg[2]);
1942147005Sdes		break;
1943147005Sdes
1944147005Sdes	case 4:
1945147005Sdes		fwd->listen_host = xstrdup(cleanhostname(fwdarg[0]));
1946147005Sdes		fwd->listen_port = a2port(fwdarg[1]);
1947147005Sdes		fwd->connect_host = xstrdup(cleanhostname(fwdarg[2]));
1948147005Sdes		fwd->connect_port = a2port(fwdarg[3]);
1949147005Sdes		break;
1950147005Sdes	default:
1951147005Sdes		i = 0; /* failure */
1952147005Sdes	}
1953147005Sdes
1954255767Sdes	free(p);
1955147005Sdes
1956192595Sdes	if (dynamicfwd) {
1957192595Sdes		if (!(i == 1 || i == 2))
1958192595Sdes			goto fail_free;
1959192595Sdes	} else {
1960192595Sdes		if (!(i == 3 || i == 4))
1961192595Sdes			goto fail_free;
1962192595Sdes		if (fwd->connect_port <= 0)
1963192595Sdes			goto fail_free;
1964192595Sdes	}
1965192595Sdes
1966192595Sdes	if (fwd->listen_port < 0 || (!remotefwd && fwd->listen_port == 0))
1967147005Sdes		goto fail_free;
1968147005Sdes
1969147005Sdes	if (fwd->connect_host != NULL &&
1970147005Sdes	    strlen(fwd->connect_host) >= NI_MAXHOST)
1971147005Sdes		goto fail_free;
1972192595Sdes	if (fwd->listen_host != NULL &&
1973192595Sdes	    strlen(fwd->listen_host) >= NI_MAXHOST)
1974192595Sdes		goto fail_free;
1975147005Sdes
1976192595Sdes
1977147005Sdes	return (i);
1978147005Sdes
1979147005Sdes fail_free:
1980255767Sdes	free(fwd->connect_host);
1981255767Sdes	fwd->connect_host = NULL;
1982255767Sdes	free(fwd->listen_host);
1983255767Sdes	fwd->listen_host = NULL;
1984147005Sdes	return (0);
1985147005Sdes}
1986