xref: /openbsd-src/usr.bin/ssh/readconf.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /* $OpenBSD: readconf.c,v 1.179 2009/10/28 16:38:18 reyk Exp $ */
2 /*
3  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5  *                    All rights reserved
6  * Functions for reading the configuration files.
7  *
8  * As far as I am concerned, the code I have written for this software
9  * can be used freely for any purpose.  Any derived versions of this
10  * software must be clearly marked as such, and if the derived work is
11  * incompatible with the protocol description in the RFC file, it must be
12  * called by a name other than "ssh" or "Secure Shell".
13  */
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/socket.h>
18 
19 #include <netinet/in.h>
20 
21 #include <ctype.h>
22 #include <errno.h>
23 #include <netdb.h>
24 #include <signal.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "xmalloc.h"
30 #include "ssh.h"
31 #include "compat.h"
32 #include "cipher.h"
33 #include "pathnames.h"
34 #include "log.h"
35 #include "key.h"
36 #include "readconf.h"
37 #include "match.h"
38 #include "misc.h"
39 #include "buffer.h"
40 #include "kex.h"
41 #include "mac.h"
42 
43 /* Format of the configuration file:
44 
45    # Configuration data is parsed as follows:
46    #  1. command line options
47    #  2. user-specific file
48    #  3. system-wide file
49    # Any configuration value is only changed the first time it is set.
50    # Thus, host-specific definitions should be at the beginning of the
51    # configuration file, and defaults at the end.
52 
53    # Host-specific declarations.  These may override anything above.  A single
54    # host may match multiple declarations; these are processed in the order
55    # that they are given in.
56 
57    Host *.ngs.fi ngs.fi
58      User foo
59 
60    Host fake.com
61      HostName another.host.name.real.org
62      User blaah
63      Port 34289
64      ForwardX11 no
65      ForwardAgent no
66 
67    Host books.com
68      RemoteForward 9999 shadows.cs.hut.fi:9999
69      Cipher 3des
70 
71    Host fascist.blob.com
72      Port 23123
73      User tylonen
74      PasswordAuthentication no
75 
76    Host puukko.hut.fi
77      User t35124p
78      ProxyCommand ssh-proxy %h %p
79 
80    Host *.fr
81      PublicKeyAuthentication no
82 
83    Host *.su
84      Cipher none
85      PasswordAuthentication no
86 
87    Host vpn.fake.com
88      Tunnel yes
89      TunnelDevice 3
90 
91    # Defaults for various options
92    Host *
93      ForwardAgent no
94      ForwardX11 no
95      PasswordAuthentication yes
96      RSAAuthentication yes
97      RhostsRSAAuthentication yes
98      StrictHostKeyChecking yes
99      TcpKeepAlive no
100      IdentityFile ~/.ssh/identity
101      Port 22
102      EscapeChar ~
103 
104 */
105 
106 /* Keyword tokens. */
107 
108 typedef enum {
109 	oBadOption,
110 	oForwardAgent, oForwardX11, oForwardX11Trusted, oGatewayPorts,
111 	oExitOnForwardFailure,
112 	oPasswordAuthentication, oRSAAuthentication,
113 	oChallengeResponseAuthentication, oXAuthLocation,
114 	oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
115 	oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
116 	oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
117 	oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
118 	oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts,
119 	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
120 	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
121 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
122 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
123 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
124 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
125 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
126 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
127 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
128 	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
129 	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
130 	oVisualHostKey, oUseRoaming, oRDomain,
131 	oZeroKnowledgePasswordAuthentication, oDeprecated, oUnsupported
132 } OpCodes;
133 
134 /* Textual representations of the tokens. */
135 
136 static struct {
137 	const char *name;
138 	OpCodes opcode;
139 } keywords[] = {
140 	{ "forwardagent", oForwardAgent },
141 	{ "forwardx11", oForwardX11 },
142 	{ "forwardx11trusted", oForwardX11Trusted },
143 	{ "exitonforwardfailure", oExitOnForwardFailure },
144 	{ "xauthlocation", oXAuthLocation },
145 	{ "gatewayports", oGatewayPorts },
146 	{ "useprivilegedport", oUsePrivilegedPort },
147 	{ "rhostsauthentication", oDeprecated },
148 	{ "passwordauthentication", oPasswordAuthentication },
149 	{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
150 	{ "kbdinteractivedevices", oKbdInteractiveDevices },
151 	{ "rsaauthentication", oRSAAuthentication },
152 	{ "pubkeyauthentication", oPubkeyAuthentication },
153 	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
154 	{ "rhostsrsaauthentication", oRhostsRSAAuthentication },
155 	{ "hostbasedauthentication", oHostbasedAuthentication },
156 	{ "challengeresponseauthentication", oChallengeResponseAuthentication },
157 	{ "skeyauthentication", oChallengeResponseAuthentication }, /* alias */
158 	{ "tisauthentication", oChallengeResponseAuthentication },  /* alias */
159 	{ "kerberosauthentication", oUnsupported },
160 	{ "kerberostgtpassing", oUnsupported },
161 	{ "afstokenpassing", oUnsupported },
162 #if defined(GSSAPI)
163 	{ "gssapiauthentication", oGssAuthentication },
164 	{ "gssapidelegatecredentials", oGssDelegateCreds },
165 #else
166 	{ "gssapiauthentication", oUnsupported },
167 	{ "gssapidelegatecredentials", oUnsupported },
168 #endif
169 	{ "fallbacktorsh", oDeprecated },
170 	{ "usersh", oDeprecated },
171 	{ "identityfile", oIdentityFile },
172 	{ "identityfile2", oIdentityFile },			/* obsolete */
173 	{ "identitiesonly", oIdentitiesOnly },
174 	{ "hostname", oHostName },
175 	{ "hostkeyalias", oHostKeyAlias },
176 	{ "proxycommand", oProxyCommand },
177 	{ "port", oPort },
178 	{ "cipher", oCipher },
179 	{ "ciphers", oCiphers },
180 	{ "macs", oMacs },
181 	{ "protocol", oProtocol },
182 	{ "remoteforward", oRemoteForward },
183 	{ "localforward", oLocalForward },
184 	{ "user", oUser },
185 	{ "host", oHost },
186 	{ "escapechar", oEscapeChar },
187 	{ "globalknownhostsfile", oGlobalKnownHostsFile },
188 	{ "globalknownhostsfile2", oGlobalKnownHostsFile2 },	/* obsolete */
189 	{ "userknownhostsfile", oUserKnownHostsFile },
190 	{ "userknownhostsfile2", oUserKnownHostsFile2 },	/* obsolete */
191 	{ "connectionattempts", oConnectionAttempts },
192 	{ "batchmode", oBatchMode },
193 	{ "checkhostip", oCheckHostIP },
194 	{ "stricthostkeychecking", oStrictHostKeyChecking },
195 	{ "compression", oCompression },
196 	{ "compressionlevel", oCompressionLevel },
197 	{ "tcpkeepalive", oTCPKeepAlive },
198 	{ "keepalive", oTCPKeepAlive },				/* obsolete */
199 	{ "numberofpasswordprompts", oNumberOfPasswordPrompts },
200 	{ "loglevel", oLogLevel },
201 	{ "dynamicforward", oDynamicForward },
202 	{ "preferredauthentications", oPreferredAuthentications },
203 	{ "hostkeyalgorithms", oHostKeyAlgorithms },
204 	{ "bindaddress", oBindAddress },
205 #ifdef SMARTCARD
206 	{ "smartcarddevice", oSmartcardDevice },
207 #else
208 	{ "smartcarddevice", oUnsupported },
209 #endif
210 	{ "clearallforwardings", oClearAllForwardings },
211 	{ "enablesshkeysign", oEnableSSHKeysign },
212 	{ "verifyhostkeydns", oVerifyHostKeyDNS },
213 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
214 	{ "rekeylimit", oRekeyLimit },
215 	{ "connecttimeout", oConnectTimeout },
216 	{ "addressfamily", oAddressFamily },
217 	{ "serveraliveinterval", oServerAliveInterval },
218 	{ "serveralivecountmax", oServerAliveCountMax },
219 	{ "sendenv", oSendEnv },
220 	{ "controlpath", oControlPath },
221 	{ "controlmaster", oControlMaster },
222 	{ "hashknownhosts", oHashKnownHosts },
223 	{ "tunnel", oTunnel },
224 	{ "tunneldevice", oTunnelDevice },
225 	{ "localcommand", oLocalCommand },
226 	{ "permitlocalcommand", oPermitLocalCommand },
227 	{ "visualhostkey", oVisualHostKey },
228 	{ "useroaming", oUseRoaming },
229 	{ "rdomain", oRDomain },
230 #ifdef JPAKE
231 	{ "zeroknowledgepasswordauthentication",
232 	    oZeroKnowledgePasswordAuthentication },
233 #else
234 	{ "zeroknowledgepasswordauthentication", oUnsupported },
235 #endif
236 
237 	{ NULL, oBadOption }
238 };
239 
240 /*
241  * Adds a local TCP/IP port forward to options.  Never returns if there is an
242  * error.
243  */
244 
245 void
246 add_local_forward(Options *options, const Forward *newfwd)
247 {
248 	Forward *fwd;
249 	extern uid_t original_real_uid;
250 	if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0)
251 		fatal("Privileged ports can only be forwarded by root.");
252 	if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
253 		fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION);
254 	fwd = &options->local_forwards[options->num_local_forwards++];
255 
256 	fwd->listen_host = newfwd->listen_host;
257 	fwd->listen_port = newfwd->listen_port;
258 	fwd->connect_host = newfwd->connect_host;
259 	fwd->connect_port = newfwd->connect_port;
260 }
261 
262 /*
263  * Adds a remote TCP/IP port forward to options.  Never returns if there is
264  * an error.
265  */
266 
267 void
268 add_remote_forward(Options *options, const Forward *newfwd)
269 {
270 	Forward *fwd;
271 	if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
272 		fatal("Too many remote forwards (max %d).",
273 		    SSH_MAX_FORWARDS_PER_DIRECTION);
274 	fwd = &options->remote_forwards[options->num_remote_forwards++];
275 
276 	fwd->listen_host = newfwd->listen_host;
277 	fwd->listen_port = newfwd->listen_port;
278 	fwd->connect_host = newfwd->connect_host;
279 	fwd->connect_port = newfwd->connect_port;
280 }
281 
282 static void
283 clear_forwardings(Options *options)
284 {
285 	int i;
286 
287 	for (i = 0; i < options->num_local_forwards; i++) {
288 		if (options->local_forwards[i].listen_host != NULL)
289 			xfree(options->local_forwards[i].listen_host);
290 		xfree(options->local_forwards[i].connect_host);
291 	}
292 	options->num_local_forwards = 0;
293 	for (i = 0; i < options->num_remote_forwards; i++) {
294 		if (options->remote_forwards[i].listen_host != NULL)
295 			xfree(options->remote_forwards[i].listen_host);
296 		xfree(options->remote_forwards[i].connect_host);
297 	}
298 	options->num_remote_forwards = 0;
299 	options->tun_open = SSH_TUNMODE_NO;
300 }
301 
302 /*
303  * Returns the number of the token pointed to by cp or oBadOption.
304  */
305 
306 static OpCodes
307 parse_token(const char *cp, const char *filename, int linenum)
308 {
309 	u_int i;
310 
311 	for (i = 0; keywords[i].name; i++)
312 		if (strcasecmp(cp, keywords[i].name) == 0)
313 			return keywords[i].opcode;
314 
315 	error("%s: line %d: Bad configuration option: %s",
316 	    filename, linenum, cp);
317 	return oBadOption;
318 }
319 
320 /*
321  * Processes a single option line as used in the configuration files. This
322  * only sets those values that have not already been set.
323  */
324 #define WHITESPACE " \t\r\n"
325 
326 int
327 process_config_line(Options *options, const char *host,
328 		    char *line, const char *filename, int linenum,
329 		    int *activep)
330 {
331 	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256];
332 	int opcode, *intptr, value, value2, scale;
333 	LogLevel *log_level_ptr;
334 	long long orig, val64;
335 	size_t len;
336 	Forward fwd;
337 
338 	/* Strip trailing whitespace */
339 	for (len = strlen(line) - 1; len > 0; len--) {
340 		if (strchr(WHITESPACE, line[len]) == NULL)
341 			break;
342 		line[len] = '\0';
343 	}
344 
345 	s = line;
346 	/* Get the keyword. (Each line is supposed to begin with a keyword). */
347 	if ((keyword = strdelim(&s)) == NULL)
348 		return 0;
349 	/* Ignore leading whitespace. */
350 	if (*keyword == '\0')
351 		keyword = strdelim(&s);
352 	if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#')
353 		return 0;
354 
355 	opcode = parse_token(keyword, filename, linenum);
356 
357 	switch (opcode) {
358 	case oBadOption:
359 		/* don't panic, but count bad options */
360 		return -1;
361 		/* NOTREACHED */
362 	case oConnectTimeout:
363 		intptr = &options->connection_timeout;
364 parse_time:
365 		arg = strdelim(&s);
366 		if (!arg || *arg == '\0')
367 			fatal("%s line %d: missing time value.",
368 			    filename, linenum);
369 		if ((value = convtime(arg)) == -1)
370 			fatal("%s line %d: invalid time value.",
371 			    filename, linenum);
372 		if (*activep && *intptr == -1)
373 			*intptr = value;
374 		break;
375 
376 	case oForwardAgent:
377 		intptr = &options->forward_agent;
378 parse_flag:
379 		arg = strdelim(&s);
380 		if (!arg || *arg == '\0')
381 			fatal("%.200s line %d: Missing yes/no argument.", filename, linenum);
382 		value = 0;	/* To avoid compiler warning... */
383 		if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
384 			value = 1;
385 		else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
386 			value = 0;
387 		else
388 			fatal("%.200s line %d: Bad yes/no argument.", filename, linenum);
389 		if (*activep && *intptr == -1)
390 			*intptr = value;
391 		break;
392 
393 	case oForwardX11:
394 		intptr = &options->forward_x11;
395 		goto parse_flag;
396 
397 	case oForwardX11Trusted:
398 		intptr = &options->forward_x11_trusted;
399 		goto parse_flag;
400 
401 	case oGatewayPorts:
402 		intptr = &options->gateway_ports;
403 		goto parse_flag;
404 
405 	case oExitOnForwardFailure:
406 		intptr = &options->exit_on_forward_failure;
407 		goto parse_flag;
408 
409 	case oUsePrivilegedPort:
410 		intptr = &options->use_privileged_port;
411 		goto parse_flag;
412 
413 	case oPasswordAuthentication:
414 		intptr = &options->password_authentication;
415 		goto parse_flag;
416 
417 	case oZeroKnowledgePasswordAuthentication:
418 		intptr = &options->zero_knowledge_password_authentication;
419 		goto parse_flag;
420 
421 	case oKbdInteractiveAuthentication:
422 		intptr = &options->kbd_interactive_authentication;
423 		goto parse_flag;
424 
425 	case oKbdInteractiveDevices:
426 		charptr = &options->kbd_interactive_devices;
427 		goto parse_string;
428 
429 	case oPubkeyAuthentication:
430 		intptr = &options->pubkey_authentication;
431 		goto parse_flag;
432 
433 	case oRSAAuthentication:
434 		intptr = &options->rsa_authentication;
435 		goto parse_flag;
436 
437 	case oRhostsRSAAuthentication:
438 		intptr = &options->rhosts_rsa_authentication;
439 		goto parse_flag;
440 
441 	case oHostbasedAuthentication:
442 		intptr = &options->hostbased_authentication;
443 		goto parse_flag;
444 
445 	case oChallengeResponseAuthentication:
446 		intptr = &options->challenge_response_authentication;
447 		goto parse_flag;
448 
449 	case oGssAuthentication:
450 		intptr = &options->gss_authentication;
451 		goto parse_flag;
452 
453 	case oGssDelegateCreds:
454 		intptr = &options->gss_deleg_creds;
455 		goto parse_flag;
456 
457 	case oBatchMode:
458 		intptr = &options->batch_mode;
459 		goto parse_flag;
460 
461 	case oCheckHostIP:
462 		intptr = &options->check_host_ip;
463 		goto parse_flag;
464 
465 	case oVerifyHostKeyDNS:
466 		intptr = &options->verify_host_key_dns;
467 		goto parse_yesnoask;
468 
469 	case oStrictHostKeyChecking:
470 		intptr = &options->strict_host_key_checking;
471 parse_yesnoask:
472 		arg = strdelim(&s);
473 		if (!arg || *arg == '\0')
474 			fatal("%.200s line %d: Missing yes/no/ask argument.",
475 			    filename, linenum);
476 		value = 0;	/* To avoid compiler warning... */
477 		if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
478 			value = 1;
479 		else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
480 			value = 0;
481 		else if (strcmp(arg, "ask") == 0)
482 			value = 2;
483 		else
484 			fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum);
485 		if (*activep && *intptr == -1)
486 			*intptr = value;
487 		break;
488 
489 	case oCompression:
490 		intptr = &options->compression;
491 		goto parse_flag;
492 
493 	case oTCPKeepAlive:
494 		intptr = &options->tcp_keep_alive;
495 		goto parse_flag;
496 
497 	case oNoHostAuthenticationForLocalhost:
498 		intptr = &options->no_host_authentication_for_localhost;
499 		goto parse_flag;
500 
501 	case oNumberOfPasswordPrompts:
502 		intptr = &options->number_of_password_prompts;
503 		goto parse_int;
504 
505 	case oCompressionLevel:
506 		intptr = &options->compression_level;
507 		goto parse_int;
508 
509 	case oRekeyLimit:
510 		arg = strdelim(&s);
511 		if (!arg || *arg == '\0')
512 			fatal("%.200s line %d: Missing argument.", filename, linenum);
513 		if (arg[0] < '0' || arg[0] > '9')
514 			fatal("%.200s line %d: Bad number.", filename, linenum);
515 		orig = val64 = strtoll(arg, &endofnumber, 10);
516 		if (arg == endofnumber)
517 			fatal("%.200s line %d: Bad number.", filename, linenum);
518 		switch (toupper(*endofnumber)) {
519 		case '\0':
520 			scale = 1;
521 			break;
522 		case 'K':
523 			scale = 1<<10;
524 			break;
525 		case 'M':
526 			scale = 1<<20;
527 			break;
528 		case 'G':
529 			scale = 1<<30;
530 			break;
531 		default:
532 			fatal("%.200s line %d: Invalid RekeyLimit suffix",
533 			    filename, linenum);
534 		}
535 		val64 *= scale;
536 		/* detect integer wrap and too-large limits */
537 		if ((val64 / scale) != orig || val64 > UINT_MAX)
538 			fatal("%.200s line %d: RekeyLimit too large",
539 			    filename, linenum);
540 		if (val64 < 16)
541 			fatal("%.200s line %d: RekeyLimit too small",
542 			    filename, linenum);
543 		if (*activep && options->rekey_limit == -1)
544 			options->rekey_limit = (u_int32_t)val64;
545 		break;
546 
547 	case oIdentityFile:
548 		arg = strdelim(&s);
549 		if (!arg || *arg == '\0')
550 			fatal("%.200s line %d: Missing argument.", filename, linenum);
551 		if (*activep) {
552 			intptr = &options->num_identity_files;
553 			if (*intptr >= SSH_MAX_IDENTITY_FILES)
554 				fatal("%.200s line %d: Too many identity files specified (max %d).",
555 				    filename, linenum, SSH_MAX_IDENTITY_FILES);
556 			charptr = &options->identity_files[*intptr];
557 			*charptr = xstrdup(arg);
558 			*intptr = *intptr + 1;
559 		}
560 		break;
561 
562 	case oXAuthLocation:
563 		charptr=&options->xauth_location;
564 		goto parse_string;
565 
566 	case oUser:
567 		charptr = &options->user;
568 parse_string:
569 		arg = strdelim(&s);
570 		if (!arg || *arg == '\0')
571 			fatal("%.200s line %d: Missing argument.", filename, linenum);
572 		if (*activep && *charptr == NULL)
573 			*charptr = xstrdup(arg);
574 		break;
575 
576 	case oGlobalKnownHostsFile:
577 		charptr = &options->system_hostfile;
578 		goto parse_string;
579 
580 	case oUserKnownHostsFile:
581 		charptr = &options->user_hostfile;
582 		goto parse_string;
583 
584 	case oGlobalKnownHostsFile2:
585 		charptr = &options->system_hostfile2;
586 		goto parse_string;
587 
588 	case oUserKnownHostsFile2:
589 		charptr = &options->user_hostfile2;
590 		goto parse_string;
591 
592 	case oHostName:
593 		charptr = &options->hostname;
594 		goto parse_string;
595 
596 	case oHostKeyAlias:
597 		charptr = &options->host_key_alias;
598 		goto parse_string;
599 
600 	case oPreferredAuthentications:
601 		charptr = &options->preferred_authentications;
602 		goto parse_string;
603 
604 	case oBindAddress:
605 		charptr = &options->bind_address;
606 		goto parse_string;
607 
608 	case oSmartcardDevice:
609 		charptr = &options->smartcard_device;
610 		goto parse_string;
611 
612 	case oProxyCommand:
613 		charptr = &options->proxy_command;
614 parse_command:
615 		if (s == NULL)
616 			fatal("%.200s line %d: Missing argument.", filename, linenum);
617 		len = strspn(s, WHITESPACE "=");
618 		if (*activep && *charptr == NULL)
619 			*charptr = xstrdup(s + len);
620 		return 0;
621 
622 	case oPort:
623 		intptr = &options->port;
624 parse_int:
625 		arg = strdelim(&s);
626 		if (!arg || *arg == '\0')
627 			fatal("%.200s line %d: Missing argument.", filename, linenum);
628 		if (arg[0] < '0' || arg[0] > '9')
629 			fatal("%.200s line %d: Bad number.", filename, linenum);
630 
631 		/* Octal, decimal, or hex format? */
632 		value = strtol(arg, &endofnumber, 0);
633 		if (arg == endofnumber)
634 			fatal("%.200s line %d: Bad number.", filename, linenum);
635 		if (*activep && *intptr == -1)
636 			*intptr = value;
637 		break;
638 
639 	case oConnectionAttempts:
640 		intptr = &options->connection_attempts;
641 		goto parse_int;
642 
643 	case oCipher:
644 		intptr = &options->cipher;
645 		arg = strdelim(&s);
646 		if (!arg || *arg == '\0')
647 			fatal("%.200s line %d: Missing argument.", filename, linenum);
648 		value = cipher_number(arg);
649 		if (value == -1)
650 			fatal("%.200s line %d: Bad cipher '%s'.",
651 			    filename, linenum, arg ? arg : "<NONE>");
652 		if (*activep && *intptr == -1)
653 			*intptr = value;
654 		break;
655 
656 	case oCiphers:
657 		arg = strdelim(&s);
658 		if (!arg || *arg == '\0')
659 			fatal("%.200s line %d: Missing argument.", filename, linenum);
660 		if (!ciphers_valid(arg))
661 			fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.",
662 			    filename, linenum, arg ? arg : "<NONE>");
663 		if (*activep && options->ciphers == NULL)
664 			options->ciphers = xstrdup(arg);
665 		break;
666 
667 	case oMacs:
668 		arg = strdelim(&s);
669 		if (!arg || *arg == '\0')
670 			fatal("%.200s line %d: Missing argument.", filename, linenum);
671 		if (!mac_valid(arg))
672 			fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.",
673 			    filename, linenum, arg ? arg : "<NONE>");
674 		if (*activep && options->macs == NULL)
675 			options->macs = xstrdup(arg);
676 		break;
677 
678 	case oHostKeyAlgorithms:
679 		arg = strdelim(&s);
680 		if (!arg || *arg == '\0')
681 			fatal("%.200s line %d: Missing argument.", filename, linenum);
682 		if (!key_names_valid2(arg))
683 			fatal("%.200s line %d: Bad protocol 2 host key algorithms '%s'.",
684 			    filename, linenum, arg ? arg : "<NONE>");
685 		if (*activep && options->hostkeyalgorithms == NULL)
686 			options->hostkeyalgorithms = xstrdup(arg);
687 		break;
688 
689 	case oProtocol:
690 		intptr = &options->protocol;
691 		arg = strdelim(&s);
692 		if (!arg || *arg == '\0')
693 			fatal("%.200s line %d: Missing argument.", filename, linenum);
694 		value = proto_spec(arg);
695 		if (value == SSH_PROTO_UNKNOWN)
696 			fatal("%.200s line %d: Bad protocol spec '%s'.",
697 			    filename, linenum, arg ? arg : "<NONE>");
698 		if (*activep && *intptr == SSH_PROTO_UNKNOWN)
699 			*intptr = value;
700 		break;
701 
702 	case oLogLevel:
703 		log_level_ptr = &options->log_level;
704 		arg = strdelim(&s);
705 		value = log_level_number(arg);
706 		if (value == SYSLOG_LEVEL_NOT_SET)
707 			fatal("%.200s line %d: unsupported log level '%s'",
708 			    filename, linenum, arg ? arg : "<NONE>");
709 		if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET)
710 			*log_level_ptr = (LogLevel) value;
711 		break;
712 
713 	case oLocalForward:
714 	case oRemoteForward:
715 	case oDynamicForward:
716 		arg = strdelim(&s);
717 		if (arg == NULL || *arg == '\0')
718 			fatal("%.200s line %d: Missing port argument.",
719 			    filename, linenum);
720 
721 		if (opcode == oLocalForward ||
722 		    opcode == oRemoteForward) {
723 			arg2 = strdelim(&s);
724 			if (arg2 == NULL || *arg2 == '\0')
725 				fatal("%.200s line %d: Missing target argument.",
726 				    filename, linenum);
727 
728 			/* construct a string for parse_forward */
729 			snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2);
730 		} else if (opcode == oDynamicForward) {
731 			strlcpy(fwdarg, arg, sizeof(fwdarg));
732 		}
733 
734 		if (parse_forward(&fwd, fwdarg,
735 		    opcode == oDynamicForward ? 1 : 0,
736 		    opcode == oRemoteForward ? 1 : 0) == 0)
737 			fatal("%.200s line %d: Bad forwarding specification.",
738 			    filename, linenum);
739 
740 		if (*activep) {
741 			if (opcode == oLocalForward ||
742 			    opcode == oDynamicForward)
743 				add_local_forward(options, &fwd);
744 			else if (opcode == oRemoteForward)
745 				add_remote_forward(options, &fwd);
746 		}
747 		break;
748 
749 	case oClearAllForwardings:
750 		intptr = &options->clear_forwardings;
751 		goto parse_flag;
752 
753 	case oHost:
754 		*activep = 0;
755 		while ((arg = strdelim(&s)) != NULL && *arg != '\0')
756 			if (match_pattern(host, arg)) {
757 				debug("Applying options for %.100s", arg);
758 				*activep = 1;
759 				break;
760 			}
761 		/* Avoid garbage check below, as strdelim is done. */
762 		return 0;
763 
764 	case oEscapeChar:
765 		intptr = &options->escape_char;
766 		arg = strdelim(&s);
767 		if (!arg || *arg == '\0')
768 			fatal("%.200s line %d: Missing argument.", filename, linenum);
769 		if (arg[0] == '^' && arg[2] == 0 &&
770 		    (u_char) arg[1] >= 64 && (u_char) arg[1] < 128)
771 			value = (u_char) arg[1] & 31;
772 		else if (strlen(arg) == 1)
773 			value = (u_char) arg[0];
774 		else if (strcmp(arg, "none") == 0)
775 			value = SSH_ESCAPECHAR_NONE;
776 		else {
777 			fatal("%.200s line %d: Bad escape character.",
778 			    filename, linenum);
779 			/* NOTREACHED */
780 			value = 0;	/* Avoid compiler warning. */
781 		}
782 		if (*activep && *intptr == -1)
783 			*intptr = value;
784 		break;
785 
786 	case oAddressFamily:
787 		arg = strdelim(&s);
788 		if (!arg || *arg == '\0')
789 			fatal("%s line %d: missing address family.",
790 			    filename, linenum);
791 		intptr = &options->address_family;
792 		if (strcasecmp(arg, "inet") == 0)
793 			value = AF_INET;
794 		else if (strcasecmp(arg, "inet6") == 0)
795 			value = AF_INET6;
796 		else if (strcasecmp(arg, "any") == 0)
797 			value = AF_UNSPEC;
798 		else
799 			fatal("Unsupported AddressFamily \"%s\"", arg);
800 		if (*activep && *intptr == -1)
801 			*intptr = value;
802 		break;
803 
804 	case oEnableSSHKeysign:
805 		intptr = &options->enable_ssh_keysign;
806 		goto parse_flag;
807 
808 	case oIdentitiesOnly:
809 		intptr = &options->identities_only;
810 		goto parse_flag;
811 
812 	case oServerAliveInterval:
813 		intptr = &options->server_alive_interval;
814 		goto parse_time;
815 
816 	case oServerAliveCountMax:
817 		intptr = &options->server_alive_count_max;
818 		goto parse_int;
819 
820 	case oSendEnv:
821 		while ((arg = strdelim(&s)) != NULL && *arg != '\0') {
822 			if (strchr(arg, '=') != NULL)
823 				fatal("%s line %d: Invalid environment name.",
824 				    filename, linenum);
825 			if (!*activep)
826 				continue;
827 			if (options->num_send_env >= MAX_SEND_ENV)
828 				fatal("%s line %d: too many send env.",
829 				    filename, linenum);
830 			options->send_env[options->num_send_env++] =
831 			    xstrdup(arg);
832 		}
833 		break;
834 
835 	case oControlPath:
836 		charptr = &options->control_path;
837 		goto parse_string;
838 
839 	case oControlMaster:
840 		intptr = &options->control_master;
841 		arg = strdelim(&s);
842 		if (!arg || *arg == '\0')
843 			fatal("%.200s line %d: Missing ControlMaster argument.",
844 			    filename, linenum);
845 		value = 0;	/* To avoid compiler warning... */
846 		if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
847 			value = SSHCTL_MASTER_YES;
848 		else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
849 			value = SSHCTL_MASTER_NO;
850 		else if (strcmp(arg, "auto") == 0)
851 			value = SSHCTL_MASTER_AUTO;
852 		else if (strcmp(arg, "ask") == 0)
853 			value = SSHCTL_MASTER_ASK;
854 		else if (strcmp(arg, "autoask") == 0)
855 			value = SSHCTL_MASTER_AUTO_ASK;
856 		else
857 			fatal("%.200s line %d: Bad ControlMaster argument.",
858 			    filename, linenum);
859 		if (*activep && *intptr == -1)
860 			*intptr = value;
861 		break;
862 
863 	case oHashKnownHosts:
864 		intptr = &options->hash_known_hosts;
865 		goto parse_flag;
866 
867 	case oTunnel:
868 		intptr = &options->tun_open;
869 		arg = strdelim(&s);
870 		if (!arg || *arg == '\0')
871 			fatal("%s line %d: Missing yes/point-to-point/"
872 			    "ethernet/no argument.", filename, linenum);
873 		value = 0;	/* silence compiler */
874 		if (strcasecmp(arg, "ethernet") == 0)
875 			value = SSH_TUNMODE_ETHERNET;
876 		else if (strcasecmp(arg, "point-to-point") == 0)
877 			value = SSH_TUNMODE_POINTOPOINT;
878 		else if (strcasecmp(arg, "yes") == 0)
879 			value = SSH_TUNMODE_DEFAULT;
880 		else if (strcasecmp(arg, "no") == 0)
881 			value = SSH_TUNMODE_NO;
882 		else
883 			fatal("%s line %d: Bad yes/point-to-point/ethernet/"
884 			    "no argument: %s", filename, linenum, arg);
885 		if (*activep)
886 			*intptr = value;
887 		break;
888 
889 	case oTunnelDevice:
890 		arg = strdelim(&s);
891 		if (!arg || *arg == '\0')
892 			fatal("%.200s line %d: Missing argument.", filename, linenum);
893 		value = a2tun(arg, &value2);
894 		if (value == SSH_TUNID_ERR)
895 			fatal("%.200s line %d: Bad tun device.", filename, linenum);
896 		if (*activep) {
897 			options->tun_local = value;
898 			options->tun_remote = value2;
899 		}
900 		break;
901 
902 	case oLocalCommand:
903 		charptr = &options->local_command;
904 		goto parse_command;
905 
906 	case oPermitLocalCommand:
907 		intptr = &options->permit_local_command;
908 		goto parse_flag;
909 
910 	case oVisualHostKey:
911 		intptr = &options->visual_host_key;
912 		goto parse_flag;
913 
914 	case oUseRoaming:
915 		intptr = &options->use_roaming;
916 		goto parse_flag;
917 
918 	case oRDomain:
919 		arg = strdelim(&s);
920 		if (!arg || *arg == '\0')
921 			fatal("%.200s line %d: Missing argument.",
922 			    filename, linenum);
923 		value = a2port(arg);
924 		if (value == -1)
925 			fatal("%.200s line %d: Bad rdomain.",
926 			    filename, linenum);
927 		if (*activep)
928 			options->rdomain = value;
929 		break;
930 
931 	case oDeprecated:
932 		debug("%s line %d: Deprecated option \"%s\"",
933 		    filename, linenum, keyword);
934 		return 0;
935 
936 	case oUnsupported:
937 		error("%s line %d: Unsupported option \"%s\"",
938 		    filename, linenum, keyword);
939 		return 0;
940 
941 	default:
942 		fatal("process_config_line: Unimplemented opcode %d", opcode);
943 	}
944 
945 	/* Check that there is no garbage at end of line. */
946 	if ((arg = strdelim(&s)) != NULL && *arg != '\0') {
947 		fatal("%.200s line %d: garbage at end of line; \"%.200s\".",
948 		    filename, linenum, arg);
949 	}
950 	return 0;
951 }
952 
953 
954 /*
955  * Reads the config file and modifies the options accordingly.  Options
956  * should already be initialized before this call.  This never returns if
957  * there is an error.  If the file does not exist, this returns 0.
958  */
959 
960 int
961 read_config_file(const char *filename, const char *host, Options *options,
962     int checkperm)
963 {
964 	FILE *f;
965 	char line[1024];
966 	int active, linenum;
967 	int bad_options = 0;
968 
969 	if ((f = fopen(filename, "r")) == NULL)
970 		return 0;
971 
972 	if (checkperm) {
973 		struct stat sb;
974 
975 		if (fstat(fileno(f), &sb) == -1)
976 			fatal("fstat %s: %s", filename, strerror(errno));
977 		if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
978 		    (sb.st_mode & 022) != 0))
979 			fatal("Bad owner or permissions on %s", filename);
980 	}
981 
982 	debug("Reading configuration data %.200s", filename);
983 
984 	/*
985 	 * Mark that we are now processing the options.  This flag is turned
986 	 * on/off by Host specifications.
987 	 */
988 	active = 1;
989 	linenum = 0;
990 	while (fgets(line, sizeof(line), f)) {
991 		/* Update line number counter. */
992 		linenum++;
993 		if (process_config_line(options, host, line, filename, linenum, &active) != 0)
994 			bad_options++;
995 	}
996 	fclose(f);
997 	if (bad_options > 0)
998 		fatal("%s: terminating, %d bad configuration options",
999 		    filename, bad_options);
1000 	return 1;
1001 }
1002 
1003 /*
1004  * Initializes options to special values that indicate that they have not yet
1005  * been set.  Read_config_file will only set options with this value. Options
1006  * are processed in the following order: command line, user config file,
1007  * system config file.  Last, fill_default_options is called.
1008  */
1009 
1010 void
1011 initialize_options(Options * options)
1012 {
1013 	memset(options, 'X', sizeof(*options));
1014 	options->forward_agent = -1;
1015 	options->forward_x11 = -1;
1016 	options->forward_x11_trusted = -1;
1017 	options->exit_on_forward_failure = -1;
1018 	options->xauth_location = NULL;
1019 	options->gateway_ports = -1;
1020 	options->use_privileged_port = -1;
1021 	options->rsa_authentication = -1;
1022 	options->pubkey_authentication = -1;
1023 	options->challenge_response_authentication = -1;
1024 	options->gss_authentication = -1;
1025 	options->gss_deleg_creds = -1;
1026 	options->password_authentication = -1;
1027 	options->kbd_interactive_authentication = -1;
1028 	options->kbd_interactive_devices = NULL;
1029 	options->rhosts_rsa_authentication = -1;
1030 	options->hostbased_authentication = -1;
1031 	options->batch_mode = -1;
1032 	options->check_host_ip = -1;
1033 	options->strict_host_key_checking = -1;
1034 	options->compression = -1;
1035 	options->tcp_keep_alive = -1;
1036 	options->compression_level = -1;
1037 	options->port = -1;
1038 	options->address_family = -1;
1039 	options->connection_attempts = -1;
1040 	options->connection_timeout = -1;
1041 	options->number_of_password_prompts = -1;
1042 	options->cipher = -1;
1043 	options->ciphers = NULL;
1044 	options->macs = NULL;
1045 	options->hostkeyalgorithms = NULL;
1046 	options->protocol = SSH_PROTO_UNKNOWN;
1047 	options->num_identity_files = 0;
1048 	options->hostname = NULL;
1049 	options->host_key_alias = NULL;
1050 	options->proxy_command = NULL;
1051 	options->user = NULL;
1052 	options->escape_char = -1;
1053 	options->system_hostfile = NULL;
1054 	options->user_hostfile = NULL;
1055 	options->system_hostfile2 = NULL;
1056 	options->user_hostfile2 = NULL;
1057 	options->num_local_forwards = 0;
1058 	options->num_remote_forwards = 0;
1059 	options->clear_forwardings = -1;
1060 	options->log_level = SYSLOG_LEVEL_NOT_SET;
1061 	options->preferred_authentications = NULL;
1062 	options->bind_address = NULL;
1063 	options->smartcard_device = NULL;
1064 	options->enable_ssh_keysign = - 1;
1065 	options->no_host_authentication_for_localhost = - 1;
1066 	options->identities_only = - 1;
1067 	options->rekey_limit = - 1;
1068 	options->verify_host_key_dns = -1;
1069 	options->server_alive_interval = -1;
1070 	options->server_alive_count_max = -1;
1071 	options->num_send_env = 0;
1072 	options->control_path = NULL;
1073 	options->control_master = -1;
1074 	options->hash_known_hosts = -1;
1075 	options->tun_open = -1;
1076 	options->tun_local = -1;
1077 	options->tun_remote = -1;
1078 	options->local_command = NULL;
1079 	options->permit_local_command = -1;
1080 	options->use_roaming = -1;
1081 	options->rdomain = -1;
1082 	options->visual_host_key = -1;
1083 	options->zero_knowledge_password_authentication = -1;
1084 }
1085 
1086 /*
1087  * Called after processing other sources of option data, this fills those
1088  * options for which no value has been specified with their default values.
1089  */
1090 
1091 void
1092 fill_default_options(Options * options)
1093 {
1094 	int len;
1095 
1096 	if (options->forward_agent == -1)
1097 		options->forward_agent = 0;
1098 	if (options->forward_x11 == -1)
1099 		options->forward_x11 = 0;
1100 	if (options->forward_x11_trusted == -1)
1101 		options->forward_x11_trusted = 0;
1102 	if (options->exit_on_forward_failure == -1)
1103 		options->exit_on_forward_failure = 0;
1104 	if (options->xauth_location == NULL)
1105 		options->xauth_location = _PATH_XAUTH;
1106 	if (options->gateway_ports == -1)
1107 		options->gateway_ports = 0;
1108 	if (options->use_privileged_port == -1)
1109 		options->use_privileged_port = 0;
1110 	if (options->rsa_authentication == -1)
1111 		options->rsa_authentication = 1;
1112 	if (options->pubkey_authentication == -1)
1113 		options->pubkey_authentication = 1;
1114 	if (options->challenge_response_authentication == -1)
1115 		options->challenge_response_authentication = 1;
1116 	if (options->gss_authentication == -1)
1117 		options->gss_authentication = 0;
1118 	if (options->gss_deleg_creds == -1)
1119 		options->gss_deleg_creds = 0;
1120 	if (options->password_authentication == -1)
1121 		options->password_authentication = 1;
1122 	if (options->kbd_interactive_authentication == -1)
1123 		options->kbd_interactive_authentication = 1;
1124 	if (options->rhosts_rsa_authentication == -1)
1125 		options->rhosts_rsa_authentication = 0;
1126 	if (options->hostbased_authentication == -1)
1127 		options->hostbased_authentication = 0;
1128 	if (options->batch_mode == -1)
1129 		options->batch_mode = 0;
1130 	if (options->check_host_ip == -1)
1131 		options->check_host_ip = 1;
1132 	if (options->strict_host_key_checking == -1)
1133 		options->strict_host_key_checking = 2;	/* 2 is default */
1134 	if (options->compression == -1)
1135 		options->compression = 0;
1136 	if (options->tcp_keep_alive == -1)
1137 		options->tcp_keep_alive = 1;
1138 	if (options->compression_level == -1)
1139 		options->compression_level = 6;
1140 	if (options->port == -1)
1141 		options->port = 0;	/* Filled in ssh_connect. */
1142 	if (options->address_family == -1)
1143 		options->address_family = AF_UNSPEC;
1144 	if (options->connection_attempts == -1)
1145 		options->connection_attempts = 1;
1146 	if (options->number_of_password_prompts == -1)
1147 		options->number_of_password_prompts = 3;
1148 	/* Selected in ssh_login(). */
1149 	if (options->cipher == -1)
1150 		options->cipher = SSH_CIPHER_NOT_SET;
1151 	/* options->ciphers, default set in myproposals.h */
1152 	/* options->macs, default set in myproposals.h */
1153 	/* options->hostkeyalgorithms, default set in myproposals.h */
1154 	if (options->protocol == SSH_PROTO_UNKNOWN)
1155 		options->protocol = SSH_PROTO_2;
1156 	if (options->num_identity_files == 0) {
1157 		if (options->protocol & SSH_PROTO_1) {
1158 			len = 2 + strlen(_PATH_SSH_CLIENT_IDENTITY) + 1;
1159 			options->identity_files[options->num_identity_files] =
1160 			    xmalloc(len);
1161 			snprintf(options->identity_files[options->num_identity_files++],
1162 			    len, "~/%.100s", _PATH_SSH_CLIENT_IDENTITY);
1163 		}
1164 		if (options->protocol & SSH_PROTO_2) {
1165 			len = 2 + strlen(_PATH_SSH_CLIENT_ID_RSA) + 1;
1166 			options->identity_files[options->num_identity_files] =
1167 			    xmalloc(len);
1168 			snprintf(options->identity_files[options->num_identity_files++],
1169 			    len, "~/%.100s", _PATH_SSH_CLIENT_ID_RSA);
1170 
1171 			len = 2 + strlen(_PATH_SSH_CLIENT_ID_DSA) + 1;
1172 			options->identity_files[options->num_identity_files] =
1173 			    xmalloc(len);
1174 			snprintf(options->identity_files[options->num_identity_files++],
1175 			    len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA);
1176 		}
1177 	}
1178 	if (options->escape_char == -1)
1179 		options->escape_char = '~';
1180 	if (options->system_hostfile == NULL)
1181 		options->system_hostfile = _PATH_SSH_SYSTEM_HOSTFILE;
1182 	if (options->user_hostfile == NULL)
1183 		options->user_hostfile = _PATH_SSH_USER_HOSTFILE;
1184 	if (options->system_hostfile2 == NULL)
1185 		options->system_hostfile2 = _PATH_SSH_SYSTEM_HOSTFILE2;
1186 	if (options->user_hostfile2 == NULL)
1187 		options->user_hostfile2 = _PATH_SSH_USER_HOSTFILE2;
1188 	if (options->log_level == SYSLOG_LEVEL_NOT_SET)
1189 		options->log_level = SYSLOG_LEVEL_INFO;
1190 	if (options->clear_forwardings == 1)
1191 		clear_forwardings(options);
1192 	if (options->no_host_authentication_for_localhost == - 1)
1193 		options->no_host_authentication_for_localhost = 0;
1194 	if (options->identities_only == -1)
1195 		options->identities_only = 0;
1196 	if (options->enable_ssh_keysign == -1)
1197 		options->enable_ssh_keysign = 0;
1198 	if (options->rekey_limit == -1)
1199 		options->rekey_limit = 0;
1200 	if (options->verify_host_key_dns == -1)
1201 		options->verify_host_key_dns = 0;
1202 	if (options->server_alive_interval == -1)
1203 		options->server_alive_interval = 0;
1204 	if (options->server_alive_count_max == -1)
1205 		options->server_alive_count_max = 3;
1206 	if (options->control_master == -1)
1207 		options->control_master = 0;
1208 	if (options->hash_known_hosts == -1)
1209 		options->hash_known_hosts = 0;
1210 	if (options->tun_open == -1)
1211 		options->tun_open = SSH_TUNMODE_NO;
1212 	if (options->tun_local == -1)
1213 		options->tun_local = SSH_TUNID_ANY;
1214 	if (options->tun_remote == -1)
1215 		options->tun_remote = SSH_TUNID_ANY;
1216 	if (options->permit_local_command == -1)
1217 		options->permit_local_command = 0;
1218 	if (options->use_roaming == -1)
1219 		options->use_roaming = 1;
1220 	if (options->visual_host_key == -1)
1221 		options->visual_host_key = 0;
1222 	if (options->zero_knowledge_password_authentication == -1)
1223 		options->zero_knowledge_password_authentication = 0;
1224 	/* options->local_command should not be set by default */
1225 	/* options->proxy_command should not be set by default */
1226 	/* options->user will be set in the main program if appropriate */
1227 	/* options->hostname will be set in the main program if appropriate */
1228 	/* options->host_key_alias should not be set by default */
1229 	/* options->preferred_authentications will be set in ssh */
1230 	/* options->rdomain should not be set by default */
1231 }
1232 
1233 /*
1234  * parse_forward
1235  * parses a string containing a port forwarding specification of the form:
1236  *   dynamicfwd == 0
1237  *	[listenhost:]listenport:connecthost:connectport
1238  *   dynamicfwd == 1
1239  *	[listenhost:]listenport
1240  * returns number of arguments parsed or zero on error
1241  */
1242 int
1243 parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd)
1244 {
1245 	int i;
1246 	char *p, *cp, *fwdarg[4];
1247 
1248 	memset(fwd, '\0', sizeof(*fwd));
1249 
1250 	cp = p = xstrdup(fwdspec);
1251 
1252 	/* skip leading spaces */
1253 	while (isspace(*cp))
1254 		cp++;
1255 
1256 	for (i = 0; i < 4; ++i)
1257 		if ((fwdarg[i] = hpdelim(&cp)) == NULL)
1258 			break;
1259 
1260 	/* Check for trailing garbage */
1261 	if (cp != NULL)
1262 		i = 0;	/* failure */
1263 
1264 	switch (i) {
1265 	case 1:
1266 		fwd->listen_host = NULL;
1267 		fwd->listen_port = a2port(fwdarg[0]);
1268 		fwd->connect_host = xstrdup("socks");
1269 		break;
1270 
1271 	case 2:
1272 		fwd->listen_host = xstrdup(cleanhostname(fwdarg[0]));
1273 		fwd->listen_port = a2port(fwdarg[1]);
1274 		fwd->connect_host = xstrdup("socks");
1275 		break;
1276 
1277 	case 3:
1278 		fwd->listen_host = NULL;
1279 		fwd->listen_port = a2port(fwdarg[0]);
1280 		fwd->connect_host = xstrdup(cleanhostname(fwdarg[1]));
1281 		fwd->connect_port = a2port(fwdarg[2]);
1282 		break;
1283 
1284 	case 4:
1285 		fwd->listen_host = xstrdup(cleanhostname(fwdarg[0]));
1286 		fwd->listen_port = a2port(fwdarg[1]);
1287 		fwd->connect_host = xstrdup(cleanhostname(fwdarg[2]));
1288 		fwd->connect_port = a2port(fwdarg[3]);
1289 		break;
1290 	default:
1291 		i = 0; /* failure */
1292 	}
1293 
1294 	xfree(p);
1295 
1296 	if (dynamicfwd) {
1297 		if (!(i == 1 || i == 2))
1298 			goto fail_free;
1299 	} else {
1300 		if (!(i == 3 || i == 4))
1301 			goto fail_free;
1302 		if (fwd->connect_port <= 0)
1303 			goto fail_free;
1304 	}
1305 
1306 	if (fwd->listen_port < 0 || (!remotefwd && fwd->listen_port == 0))
1307 		goto fail_free;
1308 
1309 	if (fwd->connect_host != NULL &&
1310 	    strlen(fwd->connect_host) >= NI_MAXHOST)
1311 		goto fail_free;
1312 	if (fwd->listen_host != NULL &&
1313 	    strlen(fwd->listen_host) >= NI_MAXHOST)
1314 		goto fail_free;
1315 
1316 
1317 	return (i);
1318 
1319  fail_free:
1320 	if (fwd->connect_host != NULL) {
1321 		xfree(fwd->connect_host);
1322 		fwd->connect_host = NULL;
1323 	}
1324 	if (fwd->listen_host != NULL) {
1325 		xfree(fwd->listen_host);
1326 		fwd->listen_host = NULL;
1327 	}
1328 	return (0);
1329 }
1330