readconf.c 51.8 KB
Newer Older
1
/* $OpenBSD: readconf.c,v 1.215 2013/12/06 13:39:49 markus Exp $ */
Damien Miller's avatar
Damien Miller committed
2
/*
3 4 5 6
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 *                    All rights reserved
 * Functions for reading the configuration files.
7
 *
8 9 10 11 12
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose.  Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
13
 */
Damien Miller's avatar
Damien Miller committed
14 15

#include "includes.h"
16 17 18

#include <sys/types.h>
#include <sys/stat.h>
19
#include <sys/socket.h>
20
#include <sys/wait.h>
21 22

#include <netinet/in.h>
23 24
#include <netinet/in_systm.h>
#include <netinet/ip.h>
Damien Miller's avatar
Damien Miller committed
25

26
#include <ctype.h>
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <netdb.h>
30 31 32
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif
33
#include <pwd.h>
34
#include <signal.h>
35
#include <stdarg.h>
36
#include <stdio.h>
37
#include <string.h>
38
#include <unistd.h>
39
#ifdef HAVE_UTIL_H
40
#include <util.h>
41
#endif
42

Damien Miller's avatar
Damien Miller committed
43
#include "xmalloc.h"
44
#include "ssh.h"
45
#include "compat.h"
46 47 48
#include "cipher.h"
#include "pathnames.h"
#include "log.h"
49
#include "key.h"
50 51 52
#include "readconf.h"
#include "match.h"
#include "misc.h"
53
#include "buffer.h"
54 55
#include "kex.h"
#include "mac.h"
56
#include "uidswap.h"
Damien Miller's avatar
Damien Miller committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

/* Format of the configuration file:

   # Configuration data is parsed as follows:
   #  1. command line options
   #  2. user-specific file
   #  3. system-wide file
   # Any configuration value is only changed the first time it is set.
   # Thus, host-specific definitions should be at the beginning of the
   # configuration file, and defaults at the end.

   # Host-specific declarations.  These may override anything above.  A single
   # host may match multiple declarations; these are processed in the order
   # that they are given in.

   Host *.ngs.fi ngs.fi
73
     User foo
Damien Miller's avatar
Damien Miller committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

   Host fake.com
     HostName another.host.name.real.org
     User blaah
     Port 34289
     ForwardX11 no
     ForwardAgent no

   Host books.com
     RemoteForward 9999 shadows.cs.hut.fi:9999
     Cipher 3des

   Host fascist.blob.com
     Port 23123
     User tylonen
     PasswordAuthentication no

   Host puukko.hut.fi
     User t35124p
     ProxyCommand ssh-proxy %h %p

   Host *.fr
96
     PublicKeyAuthentication no
Damien Miller's avatar
Damien Miller committed
97 98 99 100 101

   Host *.su
     Cipher none
     PasswordAuthentication no

102 103 104 105
   Host vpn.fake.com
     Tunnel yes
     TunnelDevice 3

Damien Miller's avatar
Damien Miller committed
106 107 108
   # Defaults for various options
   Host *
     ForwardAgent no
109
     ForwardX11 no
Damien Miller's avatar
Damien Miller committed
110 111 112 113
     PasswordAuthentication yes
     RSAAuthentication yes
     RhostsRSAAuthentication yes
     StrictHostKeyChecking yes
114
     TcpKeepAlive no
Damien Miller's avatar
Damien Miller committed
115 116 117 118 119 120 121 122
     IdentityFile ~/.ssh/identity
     Port 22
     EscapeChar ~

*/

/* Keyword tokens. */

123 124
typedef enum {
	oBadOption,
125
	oHost, oMatch,
126 127
	oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout,
	oGatewayPorts, oExitOnForwardFailure,
128
	oPasswordAuthentication, oRSAAuthentication,
129
	oChallengeResponseAuthentication, oXAuthLocation,
130
	oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
131
	oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
132 133
	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
134
	oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
135
	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
136
	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
137
	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
138
	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
139
	oHostKeyAlgorithms, oBindAddress, oPKCS11Provider,
140
	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
141
	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
142
	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
143
	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
144 145
	oSendEnv, oControlPath, oControlMaster, oControlPersist,
	oHashKnownHosts,
146
	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
147
	oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
148
	oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
149 150
	oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
	oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
151
	oIgnoredUnknownOption, oDeprecated, oUnsupported
Damien Miller's avatar
Damien Miller committed
152 153 154 155
} OpCodes;

/* Textual representations of the tokens. */

156 157 158 159 160 161
static struct {
	const char *name;
	OpCodes opcode;
} keywords[] = {
	{ "forwardagent", oForwardAgent },
	{ "forwardx11", oForwardX11 },
162
	{ "forwardx11trusted", oForwardX11Trusted },
163
	{ "forwardx11timeout", oForwardX11Timeout },
164
	{ "exitonforwardfailure", oExitOnForwardFailure },
165
	{ "xauthlocation", oXAuthLocation },
166 167
	{ "gatewayports", oGatewayPorts },
	{ "useprivilegedport", oUsePrivilegedPort },
168
	{ "rhostsauthentication", oDeprecated },
169
	{ "passwordauthentication", oPasswordAuthentication },
170 171
	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
	{ "kbdinteractivedevices", oKbdInteractiveDevices },
172
	{ "rsaauthentication", oRSAAuthentication },
173
	{ "pubkeyauthentication", oPubkeyAuthentication },
174
	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
175
	{ "rhostsrsaauthentication", oRhostsRSAAuthentication },
176
	{ "hostbasedauthentication", oHostbasedAuthentication },
177 178 179
	{ "challengeresponseauthentication", oChallengeResponseAuthentication },
	{ "skeyauthentication", oChallengeResponseAuthentication }, /* alias */
	{ "tisauthentication", oChallengeResponseAuthentication },  /* alias */
180 181 182
	{ "kerberosauthentication", oUnsupported },
	{ "kerberostgtpassing", oUnsupported },
	{ "afstokenpassing", oUnsupported },
183 184 185 186 187 188 189
#if defined(GSSAPI)
	{ "gssapiauthentication", oGssAuthentication },
	{ "gssapidelegatecredentials", oGssDelegateCreds },
#else
	{ "gssapiauthentication", oUnsupported },
	{ "gssapidelegatecredentials", oUnsupported },
#endif
190 191
	{ "fallbacktorsh", oDeprecated },
	{ "usersh", oDeprecated },
192
	{ "identityfile", oIdentityFile },
193
	{ "identityfile2", oIdentityFile },			/* obsolete */
194
	{ "identitiesonly", oIdentitiesOnly },
195
	{ "hostname", oHostName },
196
	{ "hostkeyalias", oHostKeyAlias },
197 198 199
	{ "proxycommand", oProxyCommand },
	{ "port", oPort },
	{ "cipher", oCipher },
200
	{ "ciphers", oCiphers },
201
	{ "macs", oMacs },
202
	{ "protocol", oProtocol },
203 204 205 206
	{ "remoteforward", oRemoteForward },
	{ "localforward", oLocalForward },
	{ "user", oUser },
	{ "host", oHost },
207
	{ "match", oMatch },
208 209
	{ "escapechar", oEscapeChar },
	{ "globalknownhostsfile", oGlobalKnownHostsFile },
210
	{ "globalknownhostsfile2", oDeprecated },
211
	{ "userknownhostsfile", oUserKnownHostsFile },
212
	{ "userknownhostsfile2", oDeprecated }, 
213 214 215 216 217 218
	{ "connectionattempts", oConnectionAttempts },
	{ "batchmode", oBatchMode },
	{ "checkhostip", oCheckHostIP },
	{ "stricthostkeychecking", oStrictHostKeyChecking },
	{ "compression", oCompression },
	{ "compressionlevel", oCompressionLevel },
219 220
	{ "tcpkeepalive", oTCPKeepAlive },
	{ "keepalive", oTCPKeepAlive },				/* obsolete */
221 222
	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
	{ "loglevel", oLogLevel },
223
	{ "dynamicforward", oDynamicForward },
224
	{ "preferredauthentications", oPreferredAuthentications },
225
	{ "hostkeyalgorithms", oHostKeyAlgorithms },
226
	{ "bindaddress", oBindAddress },
227 228 229
#ifdef ENABLE_PKCS11
	{ "smartcarddevice", oPKCS11Provider },
	{ "pkcs11provider", oPKCS11Provider },
230 231
#else
	{ "smartcarddevice", oUnsupported },
232
	{ "pkcs11provider", oUnsupported },
233
#endif
234
	{ "clearallforwardings", oClearAllForwardings },
235
	{ "enablesshkeysign", oEnableSSHKeysign },
236
	{ "verifyhostkeydns", oVerifyHostKeyDNS },
237
	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
238
	{ "rekeylimit", oRekeyLimit },
239
	{ "connecttimeout", oConnectTimeout },
240
	{ "addressfamily", oAddressFamily },
241 242
	{ "serveraliveinterval", oServerAliveInterval },
	{ "serveralivecountmax", oServerAliveCountMax },
243
	{ "sendenv", oSendEnv },
244 245
	{ "controlpath", oControlPath },
	{ "controlmaster", oControlMaster },
246
	{ "controlpersist", oControlPersist },
247
	{ "hashknownhosts", oHashKnownHosts },
248 249 250 251
	{ "tunnel", oTunnel },
	{ "tunneldevice", oTunnelDevice },
	{ "localcommand", oLocalCommand },
	{ "permitlocalcommand", oPermitLocalCommand },
252
	{ "visualhostkey", oVisualHostKey },
253
	{ "useroaming", oUseRoaming },
254 255 256 257 258 259
#ifdef JPAKE
	{ "zeroknowledgepasswordauthentication",
	    oZeroKnowledgePasswordAuthentication },
#else
	{ "zeroknowledgepasswordauthentication", oUnsupported },
#endif
260
	{ "kexalgorithms", oKexAlgorithms },
261
	{ "ipqos", oIPQoS },
262
	{ "requesttty", oRequestTTY },
263
	{ "proxyusefdpass", oProxyUseFdpass },
264
	{ "canonicaldomains", oCanonicalDomains },
265 266 267 268
	{ "canonicalizefallbacklocal", oCanonicalizeFallbackLocal },
	{ "canonicalizehostname", oCanonicalizeHostname },
	{ "canonicalizemaxdots", oCanonicalizeMaxDots },
	{ "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs },
269
	{ "ignoreunknown", oIgnoreUnknown },
270

271
	{ NULL, oBadOption }
272 273
};

274 275 276 277
/*
 * Adds a local TCP/IP port forward to options.  Never returns if there is an
 * error.
 */
Damien Miller's avatar
Damien Miller committed
278

279
void
280
add_local_forward(Options *options, const Forward *newfwd)
Damien Miller's avatar
Damien Miller committed
281
{
282
	Forward *fwd;
283
#ifndef NO_IPPORT_RESERVED_CONCEPT
284
	extern uid_t original_real_uid;
285
	if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0)
286
		fatal("Privileged ports can only be forwarded by root.");
287
#endif
288 289 290
	options->local_forwards = xrealloc(options->local_forwards,
	    options->num_local_forwards + 1,
	    sizeof(*options->local_forwards));
291
	fwd = &options->local_forwards[options->num_local_forwards++];
292

293
	fwd->listen_host = newfwd->listen_host;
294
	fwd->listen_port = newfwd->listen_port;
295
	fwd->connect_host = newfwd->connect_host;
296
	fwd->connect_port = newfwd->connect_port;
Damien Miller's avatar
Damien Miller committed
297 298
}

299 300 301 302
/*
 * Adds a remote TCP/IP port forward to options.  Never returns if there is
 * an error.
 */
Damien Miller's avatar
Damien Miller committed
303

304
void
305
add_remote_forward(Options *options, const Forward *newfwd)
Damien Miller's avatar
Damien Miller committed
306
{
307
	Forward *fwd;
308 309 310 311

	options->remote_forwards = xrealloc(options->remote_forwards,
	    options->num_remote_forwards + 1,
	    sizeof(*options->remote_forwards));
312
	fwd = &options->remote_forwards[options->num_remote_forwards++];
313

314
	fwd->listen_host = newfwd->listen_host;
315
	fwd->listen_port = newfwd->listen_port;
316
	fwd->connect_host = newfwd->connect_host;
317
	fwd->connect_port = newfwd->connect_port;
318
	fwd->handle = newfwd->handle;
319
	fwd->allocated_port = 0;
Damien Miller's avatar
Damien Miller committed
320 321
}

322 323 324 325 326
static void
clear_forwardings(Options *options)
{
	int i;

327
	for (i = 0; i < options->num_local_forwards; i++) {
328 329
		free(options->local_forwards[i].listen_host);
		free(options->local_forwards[i].connect_host);
330
	}
331
	if (options->num_local_forwards > 0) {
332
		free(options->local_forwards);
333 334
		options->local_forwards = NULL;
	}
335
	options->num_local_forwards = 0;
336
	for (i = 0; i < options->num_remote_forwards; i++) {
337 338
		free(options->remote_forwards[i].listen_host);
		free(options->remote_forwards[i].connect_host);
339
	}
340
	if (options->num_remote_forwards > 0) {
341
		free(options->remote_forwards);
342 343
		options->remote_forwards = NULL;
	}
344
	options->num_remote_forwards = 0;
345
	options->tun_open = SSH_TUNMODE_NO;
346 347
}

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
void
add_identity_file(Options *options, const char *dir, const char *filename,
    int userprovided)
{
	char *path;

	if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
		fatal("Too many identity files specified (max %d)",
		    SSH_MAX_IDENTITY_FILES);

	if (dir == NULL) /* no dir, filename is absolute */
		path = xstrdup(filename);
	else
		(void)xasprintf(&path, "%.100s%.100s", dir, filename);

	options->identity_file_userprovided[options->num_identity_files] =
	    userprovided;
	options->identity_files[options->num_identity_files++] = path;
}

368 369 370 371 372 373 374 375 376 377 378 379 380
int
default_ssh_port(void)
{
	static int port;
	struct servent *sp;

	if (port == 0) {
		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
		port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT;
	}
	return port;
}

381
/*
382 383
 * Execute a command in a shell.
 * Return its exit status or -1 on abnormal exit.
384
 */
385 386 387 388 389 390 391
static int
execute_in_shell(const char *cmd)
{
	char *shell, *command_string;
	pid_t pid;
	int devnull, status;
	extern uid_t original_real_uid;
Damien Miller's avatar
Damien Miller committed
392

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
	if ((shell = getenv("SHELL")) == NULL)
		shell = _PATH_BSHELL;

	/*
	 * Use "exec" to avoid "sh -c" processes on some platforms
	 * (e.g. Solaris)
	 */
	xasprintf(&command_string, "exec %s", cmd);

	/* Need this to redirect subprocess stdin/out */
	if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
		fatal("open(/dev/null): %s", strerror(errno));

	debug("Executing command: '%.500s'", cmd);

	/* Fork and execute the command. */
	if ((pid = fork()) == 0) {
		char *argv[4];

		/* Child.  Permanently give up superuser privileges. */
		permanently_drop_suid(original_real_uid);

		/* Redirect child stdin and stdout. Leave stderr */
		if (dup2(devnull, STDIN_FILENO) == -1)
			fatal("dup2: %s", strerror(errno));
		if (dup2(devnull, STDOUT_FILENO) == -1)
			fatal("dup2: %s", strerror(errno));
		if (devnull > STDERR_FILENO)
			close(devnull);
		closefrom(STDERR_FILENO + 1);

		argv[0] = shell;
		argv[1] = "-c";
		argv[2] = command_string;
		argv[3] = NULL;

		execv(argv[0], argv);
		error("Unable to execute '%.100s': %s", cmd, strerror(errno));
		/* Die with signal to make this error apparent to parent. */
		signal(SIGTERM, SIG_DFL);
		kill(getpid(), SIGTERM);
		_exit(1);
	}
	/* Parent. */
	if (pid < 0)
		fatal("%s: fork: %.100s", __func__, strerror(errno));

	close(devnull);
	free(command_string);

	while (waitpid(pid, &status, 0) == -1) {
		if (errno != EINTR && errno != EAGAIN)
			fatal("%s: waitpid: %s", __func__, strerror(errno));
	}
	if (!WIFEXITED(status)) {
		error("command '%.100s' exited abnormally", cmd);
		return -1;
	} 
	debug3("command returned status %d", WEXITSTATUS(status));
	return WEXITSTATUS(status);
}

/*
 * Parse and execute a Match directive.
 */
static int
match_cfg_line(Options *options, char **condition, struct passwd *pw,
    const char *host_arg, const char *filename, int linenum)
{
462 463
	char *arg, *attrib, *cmd, *cp = *condition, *host;
	const char *ruser;
464
	int r, port, result = 1, attributes = 0;
465 466 467 468 469 470 471 472 473
	size_t len;
	char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV];

	/*
	 * Configuration is likely to be incomplete at this point so we
	 * must be prepared to use default values.
	 */
	port = options->port <= 0 ? default_ssh_port() : options->port;
	ruser = options->user == NULL ? pw->pw_name : options->user;
474
	if (options->hostname != NULL) {
475
		/* NB. Please keep in sync with ssh.c:main() */
476 477 478 479
		host = percent_expand(options->hostname,
		    "h", host_arg, (char *)NULL);
	} else
		host = xstrdup(host_arg);
480 481 482

	debug3("checking match for '%s' host %s", cp, host);
	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
483 484 485 486 487 488 489 490 491 492 493 494 495
		attributes++;
		if (strcasecmp(attrib, "all") == 0) {
			if (attributes != 1 ||
			    ((arg = strdelim(&cp)) != NULL && *arg != '\0')) {
				error("'all' cannot be combined with other "
				    "Match attributes");
				result = -1;
				goto out;
			}
			*condition = cp;
			result = 1;
			goto out;
		}
496 497
		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
			error("Missing Match criteria for %s", attrib);
498 499
			result = -1;
			goto out;
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
		}
		len = strlen(arg);
		if (strcasecmp(attrib, "host") == 0) {
			if (match_hostname(host, arg, len) != 1)
				result = 0;
			else
				debug("%.200s line %d: matched 'Host %.100s' ",
				    filename, linenum, host);
		} else if (strcasecmp(attrib, "originalhost") == 0) {
			if (match_hostname(host_arg, arg, len) != 1)
				result = 0;
			else
				debug("%.200s line %d: matched "
				    "'OriginalHost %.100s' ",
				    filename, linenum, host_arg);
		} else if (strcasecmp(attrib, "user") == 0) {
			if (match_pattern_list(ruser, arg, len, 0) != 1)
				result = 0;
			else
				debug("%.200s line %d: matched 'User %.100s' ",
				    filename, linenum, ruser);
		} else if (strcasecmp(attrib, "localuser") == 0) {
			if (match_pattern_list(pw->pw_name, arg, len, 0) != 1)
				result = 0;
			else
				debug("%.200s line %d: matched "
				    "'LocalUser %.100s' ",
				    filename, linenum, pw->pw_name);
528
		} else if (strcasecmp(attrib, "exec") == 0) {
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
			if (gethostname(thishost, sizeof(thishost)) == -1)
				fatal("gethostname: %s", strerror(errno));
			strlcpy(shorthost, thishost, sizeof(shorthost));
			shorthost[strcspn(thishost, ".")] = '\0';
			snprintf(portstr, sizeof(portstr), "%d", port);

			cmd = percent_expand(arg,
			    "L", shorthost,
			    "d", pw->pw_dir,
			    "h", host,
			    "l", thishost,
			    "n", host_arg,
			    "p", portstr,
			    "r", ruser,
			    "u", pw->pw_name,
			    (char *)NULL);
			r = execute_in_shell(cmd);
			if (r == -1) {
547
				fatal("%.200s line %d: match exec '%.100s' "
548 549 550
				    "error", filename, linenum, cmd);
			} else if (r == 0) {
				debug("%.200s line %d: matched "
551
				    "'exec \"%.100s\"' ",
552 553 554 555 556 557
				    filename, linenum, cmd);
			} else
				result = 0;
			free(cmd);
		} else {
			error("Unsupported Match attribute %s", attrib);
558 559
			result = -1;
			goto out;
560 561
		}
	}
562 563 564 565 566
	if (attributes == 0) {
		error("One or more attributes required for Match");
		result = -1;
		goto out;
	}
567 568
	debug3("match %sfound", result ? "" : "not ");
	*condition = cp;
569 570
 out:
	free(host);
571 572 573
	return result;
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
/* Check and prepare a domain name: removes trailing '.' and lowercases */
static void
valid_domain(char *name, const char *filename, int linenum)
{
	size_t i, l = strlen(name);
	u_char c, last = '\0';

	if (l == 0)
		fatal("%s line %d: empty hostname suffix", filename, linenum);
	if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0]))
		fatal("%s line %d: hostname suffix \"%.100s\" "
		    "starts with invalid character", filename, linenum, name);
	for (i = 0; i < l; i++) {
		c = tolower((u_char)name[i]);
		name[i] = (char)c;
		if (last == '.' && c == '.')
			fatal("%s line %d: hostname suffix \"%.100s\" contains "
			    "consecutive separators", filename, linenum, name);
		if (c != '.' && c != '-' && !isalnum(c) &&
		    c != '_') /* technically invalid, but common */
			fatal("%s line %d: hostname suffix \"%.100s\" contains "
			    "invalid characters", filename, linenum, name);
		last = c;
	}
	if (name[l - 1] == '.')
		name[l - 1] = '\0';
}

602 603 604
/*
 * Returns the number of the token pointed to by cp or oBadOption.
 */
605
static OpCodes
606 607
parse_token(const char *cp, const char *filename, int linenum,
    const char *ignored_unknown)
Damien Miller's avatar
Damien Miller committed
608
{
609
	int i;
Damien Miller's avatar
Damien Miller committed
610

611
	for (i = 0; keywords[i].name; i++)
612
		if (strcmp(cp, keywords[i].name) == 0)
613
			return keywords[i].opcode;
614 615 616
	if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown,
	    strlen(ignored_unknown), 1) == 1)
		return oIgnoredUnknownOption;
617 618
	error("%s: line %d: Bad configuration option: %s",
	    filename, linenum, cp);
619
	return oBadOption;
Damien Miller's avatar
Damien Miller committed
620 621
}

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
/* Multistate option parsing */
struct multistate {
	char *key;
	int value;
};
static const struct multistate multistate_flag[] = {
	{ "true",			1 },
	{ "false",			0 },
	{ "yes",			1 },
	{ "no",				0 },
	{ NULL, -1 }
};
static const struct multistate multistate_yesnoask[] = {
	{ "true",			1 },
	{ "false",			0 },
	{ "yes",			1 },
	{ "no",				0 },
	{ "ask",			2 },
	{ NULL, -1 }
};
static const struct multistate multistate_addressfamily[] = {
	{ "inet",			AF_INET },
	{ "inet6",			AF_INET6 },
	{ "any",			AF_UNSPEC },
	{ NULL, -1 }
};
static const struct multistate multistate_controlmaster[] = {
	{ "true",			SSHCTL_MASTER_YES },
	{ "yes",			SSHCTL_MASTER_YES },
	{ "false",			SSHCTL_MASTER_NO },
	{ "no",				SSHCTL_MASTER_NO },
	{ "auto",			SSHCTL_MASTER_AUTO },
	{ "ask",			SSHCTL_MASTER_ASK },
	{ "autoask",			SSHCTL_MASTER_AUTO_ASK },
	{ NULL, -1 }
};
static const struct multistate multistate_tunnel[] = {
	{ "ethernet",			SSH_TUNMODE_ETHERNET },
	{ "point-to-point",		SSH_TUNMODE_POINTOPOINT },
	{ "true",			SSH_TUNMODE_DEFAULT },
	{ "yes",			SSH_TUNMODE_DEFAULT },
	{ "false",			SSH_TUNMODE_NO },
	{ "no",				SSH_TUNMODE_NO },
	{ NULL, -1 }
};
static const struct multistate multistate_requesttty[] = {
	{ "true",			REQUEST_TTY_YES },
	{ "yes",			REQUEST_TTY_YES },
	{ "false",			REQUEST_TTY_NO },
	{ "no",				REQUEST_TTY_NO },
	{ "force",			REQUEST_TTY_FORCE },
	{ "auto",			REQUEST_TTY_AUTO },
	{ NULL, -1 }
};
676
static const struct multistate multistate_canonicalizehostname[] = {
677 678 679 680 681 682 683
	{ "true",			SSH_CANONICALISE_YES },
	{ "false",			SSH_CANONICALISE_NO },
	{ "yes",			SSH_CANONICALISE_YES },
	{ "no",				SSH_CANONICALISE_NO },
	{ "always",			SSH_CANONICALISE_ALWAYS },
	{ NULL, -1 }
};
684

685 686 687 688
/*
 * Processes a single option line as used in the configuration files. This
 * only sets those values that have not already been set.
 */
689
#define WHITESPACE " \t\r\n"
690
int
691 692
process_config_line(Options *options, struct passwd *pw, const char *host,
    char *line, const char *filename, int linenum, int *activep, int userconfig)
Damien Miller's avatar
Damien Miller committed
693
{
694 695
	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2;
	char **cpptr, fwdarg[256];
696
	u_int i, *uintptr, max_entries = 0;
697
	int negated, opcode, *intptr, value, value2, cmdline = 0;
698
	LogLevel *log_level_ptr;
699
	long long val64;
700
	size_t len;
701
	Forward fwd;
702
	const struct multistate *multistate_ptr;
703
	struct allowed_cname *cname;
704

705 706 707 708 709
	if (activep == NULL) { /* We are processing a command line directive */
		cmdline = 1;
		activep = &cmdline;
	}

710
	/* Strip trailing whitespace */
711
	for (len = strlen(line) - 1; len > 0; len--) {
712 713 714 715 716
		if (strchr(WHITESPACE, line[len]) == NULL)
			break;
		line[len] = '\0';
	}

717 718
	s = line;
	/* Get the keyword. (Each line is supposed to begin with a keyword). */
719 720
	if ((keyword = strdelim(&s)) == NULL)
		return 0;
721 722 723
	/* Ignore leading whitespace. */
	if (*keyword == '\0')
		keyword = strdelim(&s);
724
	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
725
		return 0;
726
	/* Match lowercase keyword */
727
	lowercase(keyword);
728

729 730
	opcode = parse_token(keyword, filename, linenum,
	    options->ignored_unknown);
731 732 733

	switch (opcode) {
	case oBadOption:
734 735
		/* don't panic, but count bad options */
		return -1;
736
		/* NOTREACHED */
737 738 739 740
	case oIgnoredUnknownOption:
		debug("%s line %d: Ignored unknown option \"%s\"",
		    filename, linenum, keyword);
		return 0;
741 742
	case oConnectTimeout:
		intptr = &options->connection_timeout;
743
parse_time:
744 745 746 747 748 749 750
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%s line %d: missing time value.",
			    filename, linenum);
		if ((value = convtime(arg)) == -1)
			fatal("%s line %d: invalid time value.",
			    filename, linenum);
751
		if (*activep && *intptr == -1)
752 753 754
			*intptr = value;
		break;

755 756
	case oForwardAgent:
		intptr = &options->forward_agent;
757 758 759
 parse_flag:
		multistate_ptr = multistate_flag;
 parse_multistate:
760
		arg = strdelim(&s);
761
		if (!arg || *arg == '\0')
762 763 764 765 766 767 768 769 770 771 772 773
			fatal("%s line %d: missing argument.",
			    filename, linenum);
		value = -1;
		for (i = 0; multistate_ptr[i].key != NULL; i++) {
			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
				value = multistate_ptr[i].value;
				break;
			}
		}
		if (value == -1)
			fatal("%s line %d: unsupported option \"%s\".",
			    filename, linenum, arg);
774 775 776 777 778 779 780 781
		if (*activep && *intptr == -1)
			*intptr = value;
		break;

	case oForwardX11:
		intptr = &options->forward_x11;
		goto parse_flag;

782 783 784
	case oForwardX11Trusted:
		intptr = &options->forward_x11_trusted;
		goto parse_flag;
785 786 787 788
	
	case oForwardX11Timeout:
		intptr = &options->forward_x11_timeout;
		goto parse_time;
789

790 791 792 793
	case oGatewayPorts:
		intptr = &options->gateway_ports;
		goto parse_flag;

794 795 796 797
	case oExitOnForwardFailure:
		intptr = &options->exit_on_forward_failure;
		goto parse_flag;

798 799 800 801 802 803 804 805
	case oUsePrivilegedPort:
		intptr = &options->use_privileged_port;
		goto parse_flag;

	case oPasswordAuthentication:
		intptr = &options->password_authentication;
		goto parse_flag;

806 807 808 809
	case oZeroKnowledgePasswordAuthentication:
		intptr = &options->zero_knowledge_password_authentication;
		goto parse_flag;

810 811 812 813 814 815 816 817
	case oKbdInteractiveAuthentication:
		intptr = &options->kbd_interactive_authentication;
		goto parse_flag;

	case oKbdInteractiveDevices:
		charptr = &options->kbd_interactive_devices;
		goto parse_string;

818 819
	case oPubkeyAuthentication:
		intptr = &options->pubkey_authentication;
820 821
		goto parse_flag;

822 823 824 825 826 827 828 829
	case oRSAAuthentication:
		intptr = &options->rsa_authentication;
		goto parse_flag;

	case oRhostsRSAAuthentication:
		intptr = &options->rhosts_rsa_authentication;
		goto parse_flag;

830 831 832 833
	case oHostbasedAuthentication:
		intptr = &options->hostbased_authentication;
		goto parse_flag;

834
	case oChallengeResponseAuthentication:
835
		intptr = &options->challenge_response_authentication;
836
		goto parse_flag;
837

838 839 840 841 842 843 844 845
	case oGssAuthentication:
		intptr = &options->gss_authentication;
		goto parse_flag;

	case oGssDelegateCreds:
		intptr = &options->gss_deleg_creds;
		goto parse_flag;

846 847 848 849 850 851
	case oBatchMode:
		intptr = &options->batch_mode;
		goto parse_flag;

	case oCheckHostIP:
		intptr = &options->check_host_ip;
852
		goto parse_flag;
853

854 855
	case oVerifyHostKeyDNS:
		intptr = &options->verify_host_key_dns;
856 857
		multistate_ptr = multistate_yesnoask;
		goto parse_multistate;
858

859 860
	case oStrictHostKeyChecking:
		intptr = &options->strict_host_key_checking;
861 862
		multistate_ptr = multistate_yesnoask;
		goto parse_multistate;
863 864 865 866 867

	case oCompression:
		intptr = &options->compression;
		goto parse_flag;

868 869
	case oTCPKeepAlive:
		intptr = &options->tcp_keep_alive;
870 871
		goto parse_flag;

872 873 874 875
	case oNoHostAuthenticationForLocalhost:
		intptr = &options->no_host_authentication_for_localhost;
		goto parse_flag;

876 877 878 879 880 881 882 883
	case oNumberOfPasswordPrompts:
		intptr = &options->number_of_password_prompts;
		goto parse_int;

	case oCompressionLevel:
		intptr = &options->compression_level;
		goto parse_int;

884 885 886
	case oRekeyLimit:
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
887 888 889 890 891
			fatal("%.200s line %d: Missing argument.", filename,
			    linenum);
		if (strcmp(arg, "default") == 0) {
			val64 = 0;
		} else {
892 893 894 895 896
			if (scan_scaled(arg, &val64) == -1)
				fatal("%.200s line %d: Bad number '%s': %s",
				    filename, linenum, arg, strerror(errno));
			/* check for too-large or too-small limits */
			if (val64 > UINT_MAX)
897 898 899 900 901
				fatal("%.200s line %d: RekeyLimit too large",
				    filename, linenum);
			if (val64 != 0 && val64 < 16)
				fatal("%.200s line %d: RekeyLimit too small",
				    filename, linenum);
902
		}
903 904
		if (*activep && options->rekey_limit == -1)
			options->rekey_limit = (u_int32_t)val64;
905 906 907 908 909 910 911 912
		if (s != NULL) { /* optional rekey interval present */
			if (strcmp(s, "none") == 0) {
				(void)strdelim(&s);	/* discard */
				break;
			}
			intptr = &options->rekey_interval;
			goto parse_time;
		}
913 914
		break;

915
	case oIdentityFile:
916
		arg = strdelim(&s);
917
		if (!arg || *arg == '\0')
918 919
			fatal("%.200s line %d: Missing argument.", filename, linenum);
		if (*activep) {
920
			intptr = &options->num_identity_files;
921
			if (*intptr >= SSH_MAX_IDENTITY_FILES)
922
				fatal("%.200s line %d: Too many identity files specified (max %d).",
923
				    filename, linenum, SSH_MAX_IDENTITY_FILES);
924
			add_identity_file(options, NULL, arg, userconfig);
925 926 927
		}
		break;

928 929 930 931
	case oXAuthLocation:
		charptr=&options->xauth_location;
		goto parse_string;

932 933 934
	case oUser:
		charptr = &options->user;
parse_string:
935
		arg = strdelim(&s);
936
		if (!arg || *arg == '\0')
937 938
			fatal("%.200s line %d: Missing argument.",
			    filename, linenum);
939
		if (*activep && *charptr == NULL)
940
			*charptr = xstrdup(arg);
941 942 943
		break;

	case oGlobalKnownHostsFile:
944 945 946 947 948 949 950 951 952 953 954 955 956 957
		cpptr = (char **)&options->system_hostfiles;
		uintptr = &options->num_system_hostfiles;
		max_entries = SSH_MAX_HOSTS_FILES;
parse_char_array:
		if (*activep && *uintptr == 0) {
			while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
				if ((*uintptr) >= max_entries)
					fatal("%s line %d: "
					    "too many authorized keys files.",
					    filename, linenum);
				cpptr[(*uintptr)++] = xstrdup(arg);
			}
		}
		return 0;
958 959

	case oUserKnownHostsFile:
960 961 962 963
		cpptr = (char **)&options->user_hostfiles;
		uintptr = &options->num_user_hostfiles;
		max_entries = SSH_MAX_HOSTS_FILES;
		goto parse_char_array;
964

965 966 967 968
	case oHostName:
		charptr = &options->hostname;
		goto parse_string;

969 970 971 972
	case oHostKeyAlias:
		charptr = &options->host_key_alias;
		goto parse_string;

973 974 975 976
	case oPreferredAuthentications:
		charptr = &options->preferred_authentications;
		goto parse_string;

977 978 979 980
	case oBindAddress:
		charptr = &options->bind_address;
		goto parse_string;

981 982
	case oPKCS11Provider:
		charptr = &options->pkcs11_provider;
983
		goto parse_string;
984

985
	case oProxyCommand:
986 987
		charptr = &options->proxy_command;
parse_command:
988 989
		if (s == NULL)
			fatal("%.200s line %d: Missing argument.", filename, linenum);
990
		len = strspn(s, WHITESPACE "=");
991
		if (*activep && *charptr == NULL)
992
			*charptr = xstrdup(s + len);
993 994 995 996 997
		return 0;

	case oPort:
		intptr = &options->port;
parse_int:
998
		arg = strdelim(&s);
999
		if (!arg || *arg == '\0')
1000
			fatal("%.200s line %d: Missing argument.", filename, linenum);
1001
		if (arg[0] < '0' || arg[0] > '9')
1002
			fatal("%.200s line %d: Bad number.", filename, linenum);
1003 1004

		/* Octal, decimal, or hex format? */
1005 1006
		value = strtol(arg, &endofnumber, 0);
		if (arg == endofnumber)
1007
			fatal("%.200s line %d: Bad number.", filename, linenum);
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
		if (*activep && *intptr == -1)
			*intptr = value;
		break;

	case oConnectionAttempts:
		intptr = &options->connection_attempts;
		goto parse_int;

	case oCipher:
		intptr = &options->cipher;
1018
		arg = strdelim(&s);
1019
		if (!arg || *arg == '\0')
1020
			fatal("%.200s line %d: Missing argument.", filename, linenum);
1021
		value = cipher_number(arg);
1022 1023
		if (value == -1)
			fatal("%.200s line %d: Bad cipher '%s'.",
1024
			    filename, linenum, arg ? arg : "<NONE>");
1025 1026 1027 1028
		if (*activep && *intptr == -1)
			*intptr = value;
		break;

1029
	case oCiphers:
1030
		arg = strdelim(&s);
1031
		if (!arg || *arg == '\0')
1032
			fatal("%.200s line %d: Missing argument.", filename, linenum);
1033
		if (!ciphers_valid(arg))
Damien Miller's avatar
Damien Miller committed
1034
			fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.",
1035
			    filename, linenum, arg ? arg : "<NONE>");
1036
		if (*activep && options->ciphers == NULL)
1037
			options->ciphers = xstrdup(arg);
1038 1039
		break;

1040 1041 1042 1043 1044 1045
	case oMacs:
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%.200s line %d: Missing argument.", filename, linenum);
		if (!mac_valid(arg))
			fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.",
1046
			    filename, linenum, arg ? arg : "<NONE>");
1047 1048 1049 1050
		if (*activep && options->macs == NULL)
			options->macs = xstrdup(arg);
		break;

1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
	case oKexAlgorithms:
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%.200s line %d: Missing argument.",
			    filename, linenum);
		if (!kex_names_valid(arg))
			fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.",
			    filename, linenum, arg ? arg : "<NONE>");
		if (*activep && options->kex_algorithms == NULL)
			options->kex_algorithms = xstrdup(arg);
		break;

1063 1064 1065 1066 1067 1068
	case oHostKeyAlgorithms:
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%.200s line %d: Missing argument.", filename, linenum);
		if (!key_names_valid2(arg))
			fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.",
1069
			    filename, linenum, arg ? arg : "<NONE>");
1070 1071 1072 1073
		if (*activep && options->hostkeyalgorithms == NULL)
			options->hostkeyalgorithms = xstrdup(arg);
		break;

1074 1075
	case oProtocol:
		intptr = &options->protocol;
1076
		arg = strdelim(&s);
1077
		if (!arg || *arg == '\0')
1078
			fatal("%.200s line %d: Missing argument.", filename, linenum);
1079
		value = proto_spec(arg);
1080 1081
		if (value == SSH_PROTO_UNKNOWN)
			fatal("%.200s line %d: Bad protocol spec '%s'.",
1082
			    filename, linenum, arg ? arg : "<NONE>");
1083 1084 1085 1086
		if (*activep && *intptr == SSH_PROTO_UNKNOWN)
			*intptr = value;
		break;

1087
	case oLogLevel:
1088
		log_level_ptr = &options->log_level;
1089
		arg = strdelim(&s);
1090
		value = log_level_number(arg);
1091
		if (value == SYSLOG_LEVEL_NOT_SET)
1092
			fatal("%.200s line %d: unsupported log level '%s'",
1093
			    filename, linenum, arg ? arg : "<NONE>");
1094 1095
		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
			*log_level_ptr = (LogLevel) value;
1096 1097 1098
		break;

	case oLocalForward:
1099
	case oRemoteForward:
1100
	case oDynamicForward:
1101
		arg = strdelim(&s);
1102
		if (arg == NULL || *arg == '\0')
1103 1104
			fatal("%.200s line %d: Missing port argument.",
			    filename, linenum);
1105

1106 1107 1108 1109 1110 1111
		if (opcode == oLocalForward ||
		    opcode == oRemoteForward) {
			arg2 = strdelim(&s);
			if (arg2 == NULL || *arg2 == '\0')
				fatal("%.200s line %d: Missing target argument.",
				    filename, linenum);
1112

1113 1114 1115 1116 1117 1118 1119
			/* construct a string for parse_forward */
			snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2);
		} else if (opcode == oDynamicForward) {
			strlcpy(fwdarg, arg, sizeof(fwdarg));
		}

		if (parse_forward(&fwd, fwdarg,
1120 1121
		    opcode == oDynamicForward ? 1 : 0,
		    opcode == oRemoteForward ? 1 : 0) == 0)
1122 1123
			fatal("%.200s line %d: Bad forwarding specification.",
			    filename, linenum);
1124

1125
		if (*activep) {
1126 1127
			if (opcode == oLocalForward ||
			    opcode == oDynamicForward)
1128
				add_local_forward(options, &fwd);
1129
			else if (opcode == oRemoteForward)
1130
				add_remote_forward(options, &fwd);
1131
		}
1132 1133
		break;

1134 1135 1136 1137
	case oClearAllForwardings:
		intptr = &options->clear_forwardings;
		goto parse_flag;

1138
	case oHost:
1139 1140 1141
		if (cmdline)
			fatal("Host directive not supported as a command-line "
			    "option");
1142
		*activep = 0;
1143 1144 1145 1146 1147
		arg2 = NULL;
		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
			negated = *arg == '!';
			if (negated)
				arg++;
1148
			if (match_pattern(host, arg)) {
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
				if (negated) {
					debug("%.200s line %d: Skipping Host "
					    "block because of negated match "
					    "for %.100s", filename, linenum,
					    arg);
					*activep = 0;
					break;
				}
				if (!*activep)
					arg2 = arg; /* logged below */
1159 1160
				*activep = 1;
			}
1161 1162 1163 1164
		}
		if (*activep)
			debug("%.200s line %d: Applying options for %.100s",
			    filename, linenum, arg2);
1165
		/* Avoid garbage check below, as strdelim is done. */
1166 1167
		return 0;

1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179
	case oMatch:
		if (cmdline)
			fatal("Host directive not supported as a command-line "
			    "option");
		value = match_cfg_line(options, &s, pw, host,
		    filename, linenum);
		if (value < 0)
			fatal("%.200s line %d: Bad Match condition", filename,
			    linenum);
		*activep = value;
		break;

1180 1181
	case oEscapeChar:
		intptr = &options->escape_char;
1182
		arg = strdelim(&s);
1183
		if (!arg || *arg == '\0')
1184
			fatal("%.200s line %d: Missing argument.", filename, linenum);
1185
		if (arg[0] == '^' && arg[2] == 0 &&
1186 1187
		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
			value = (u_char) arg[1] & 31;
1188
		else if (strlen(arg) == 1)
1189
			value = (u_char) arg[0];
1190
		else if (strcmp(arg, "none") == 0)
1191
			value = SSH_ESCAPECHAR_NONE;
1192 1193
		else {
			fatal("%.200s line %d: Bad escape character.",
1194
			    filename, linenum);
1195 1196 1197 1198 1199 1200 1201
			/* NOTREACHED */
			value = 0;	/* Avoid compiler warning. */
		}
		if (*activep && *intptr == -1)
			*intptr = value;
		break;

1202
	case oAddressFamily:
1203
		intptr = &options->address_family;
1204 1205
		multistate_ptr = multistate_addressfamily;
		goto parse_multistate;
1206

1207 1208 1209 1210
	case oEnableSSHKeysign:
		intptr = &options->enable_ssh_keysign;
		goto parse_flag;

1211 1212 1213 1214
	case oIdentitiesOnly:
		intptr = &options->identities_only;
		goto parse_flag;

1215 1216 1217 1218 1219 1220 1221 1222
	case oServerAliveInterval:
		intptr = &options->server_alive_interval;
		goto parse_time;

	case oServerAliveCountMax:
		intptr = &options->server_alive_count_max;
		goto parse_int;

1223 1224 1225 1226 1227
	case oSendEnv:
		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
			if (strchr(arg, '=') != NULL)
				fatal("%s line %d: Invalid environment name.",
				    filename, linenum);
1228 1229
			if (!*activep)
				continue;
1230 1231 1232 1233 1234 1235 1236 1237
			if (options->num_send_env >= MAX_SEND_ENV)
				fatal("%s line %d: too many send env.",
				    filename, linenum);
			options->send_env[options->num_send_env++] =
			    xstrdup(arg);
		}
		break;

1238 1239 1240 1241 1242 1243
	case oControlPath:
		charptr = &options->control_path;
		goto parse_string;

	case oControlMaster:
		intptr = &options->control_master;
1244 1245
		multistate_ptr = multistate_controlmaster;
		goto parse_multistate;
1246

1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
	case oControlPersist:
		/* no/false/yes/true, or a time spec */
		intptr = &options->control_persist;
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%.200s line %d: Missing ControlPersist"
			    " argument.", filename, linenum);
		value = 0;
		value2 = 0;	/* timeout */
		if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
			value = 0;
		else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
			value = 1;
		else if ((value2 = convtime(arg)) >= 0)
			value = 1;
		else
			fatal("%.200s line %d: Bad ControlPersist argument.",
			    filename, linenum);
		if (*activep && *intptr == -1) {
			*intptr = value;
			options->control_persist_timeout = value2;
		}
		break;

1271 1272 1273 1274
	case oHashKnownHosts:
		intptr = &options->hash_known_hosts;
		goto parse_flag;

1275 1276
	case oTunnel:
		intptr = &options->tun_open;
1277 1278
		multistate_ptr = multistate_tunnel;
		goto parse_multistate;
1279 1280 1281 1282 1283 1284

	case oTunnelDevice:
		arg = strdelim(&s);
		if (!arg || *arg == '\0')
			fatal("%.200s line %d: Missing argument.", filename, linenum);
		value = a2tun(arg, &value2);
1285
		if (value == SSH_TUNID_ERR)
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
			fatal("%.200s line %d: Bad tun device.", filename, linenum);
		if (*activep) {
			options->tun_local = value;
			options->tun_remote = value2;
		}
		break;

	case oLocalCommand:
		charptr = &options->local_command;
		goto parse_command;

	case oPermitLocalCommand:
		intptr = &options->permit_local_command;
		goto parse_flag;

1301 1302 1303 1304
	case oVisualHostKey:
		intptr = &options->visual_host_key;
		goto parse_flag;

1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
	case oIPQoS:
		arg = strdelim(&s);
		if ((value = parse_ipqos(arg)) == -1)
			fatal("%s line %d: Bad IPQoS value: %s",
			    filename, linenum, arg);
		arg = strdelim(&s);
		if (arg == NULL)
			value2 = value;
		else if ((value2 = parse_ipqos(arg)) == -1)
			fatal("%s line %d: Bad IPQoS value: %s",
			    filename, linenum, arg);
		if (*activep) {
			options->ip_qos_interactive = value;
			options->ip_qos_bulk = value2;
		}
		break;

1322 1323 1324 1325
	case oUseRoaming:
		intptr = &options->use_roaming;
		goto parse_flag;

1326 1327
	case oRequestTTY:
		intptr = &options->request_tty;
1328 1329
		multistate_ptr = multistate_requesttty;
		goto parse_multistate;
1330

1331 1332 1333 1334
	case oIgnoreUnknown:
		charptr = &options->ignored_unknown;
		goto parse_string;

1335 1336 1337 1338
	case oProxyUseFdpass:
		intptr = &options->proxy_use_fdpass;
		goto parse_flag;

1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
	case oCanonicalDomains:
		value = options->num_canonical_domains != 0;
		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
			valid_domain(arg, filename, linenum);
			if (!*activep || value)
				continue;
			if (options->num_canonical_domains >= MAX_CANON_DOMAINS)
				fatal("%s line %d: too many hostname suffixes.",
				    filename, linenum);
			options->canonical_domains[
			    options->num_canonical_domains++] = xstrdup(arg);
		}
		break;

1353
	case oCanonicalizePermittedCNAMEs:
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
		value = options->num_permitted_cnames != 0;
		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
			/* Either '*' for everything or 'list:list' */
			if (strcmp(arg, "*") == 0)
				arg2 = arg;
			else {
				lowercase(arg);
				if ((arg2 = strchr(arg, ':')) == NULL ||
				    arg2[1] == '\0') {
					fatal("%s line %d: "
					    "Invalid permitted CNAME \"%s\"",
					    filename, linenum, arg);
				}
				*arg2 = '\0';
				arg2++;
			}
			if (!*activep || value)
				continue;
			if (options->num_permitted_cnames >= MAX_CANON_DOMAINS)
				fatal("%s line %d: too many permitted CNAMEs.",
				    filename, linenum);
			cname = options->permitted_cnames +
			    options->num_permitted_cnames++;
			cname->source_list = xstrdup(arg);
			cname->target_list = xstrdup(arg2);
		}
		break;

1382 1383 1384
	case oCanonicalizeHostname:
		intptr = &options->canonicalize_hostname;
		multistate_ptr = multistate_canonicalizehostname;
1385 1386
		goto parse_multistate;

1387 1388
	case oCanonicalizeMaxDots:
		intptr = &options->canonicalize_max_dots;
1389 1390
		goto parse_int;

1391 1392
	case oCanonicalizeFallbackLocal:
		intptr = &options->canonicalize_fallback_local;