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