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