blob: 281548d2a84554d7a911023f6d1384990f911901 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
2
3readconf.c
4
5Author: Tatu Ylonen <ylo@cs.hut.fi>
6
7Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 All rights reserved
9
10Created: Sat Apr 22 00:03:10 1995 ylo
11
12Functions for reading the configuration files.
13
14*/
15
16#include "includes.h"
17RCSID("$Id: readconf.c,v 1.1 1999/10/27 03:42:44 damien Exp $");
18
19#include "ssh.h"
20#include "cipher.h"
21#include "readconf.h"
22#include "xmalloc.h"
23
24/* Format of the configuration file:
25
26 # Configuration data is parsed as follows:
27 # 1. command line options
28 # 2. user-specific file
29 # 3. system-wide file
30 # Any configuration value is only changed the first time it is set.
31 # Thus, host-specific definitions should be at the beginning of the
32 # configuration file, and defaults at the end.
33
34 # Host-specific declarations. These may override anything above. A single
35 # host may match multiple declarations; these are processed in the order
36 # that they are given in.
37
38 Host *.ngs.fi ngs.fi
39 FallBackToRsh no
40
41 Host fake.com
42 HostName another.host.name.real.org
43 User blaah
44 Port 34289
45 ForwardX11 no
46 ForwardAgent no
47
48 Host books.com
49 RemoteForward 9999 shadows.cs.hut.fi:9999
50 Cipher 3des
51
52 Host fascist.blob.com
53 Port 23123
54 User tylonen
55 RhostsAuthentication no
56 PasswordAuthentication no
57
58 Host puukko.hut.fi
59 User t35124p
60 ProxyCommand ssh-proxy %h %p
61
62 Host *.fr
63 UseRsh yes
64
65 Host *.su
66 Cipher none
67 PasswordAuthentication no
68
69 # Defaults for various options
70 Host *
71 ForwardAgent no
72 ForwardX11 yes
73 RhostsAuthentication yes
74 PasswordAuthentication yes
75 RSAAuthentication yes
76 RhostsRSAAuthentication yes
77 FallBackToRsh no
78 UseRsh no
79 StrictHostKeyChecking yes
80 KeepAlives no
81 IdentityFile ~/.ssh/identity
82 Port 22
83 EscapeChar ~
84
85*/
86
87/* Keyword tokens. */
88
89typedef enum
90{
91 oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication,
92 oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh,
93#ifdef KRB4
94 oKerberosAuthentication,
95#endif /* KRB4 */
96#ifdef AFS
97 oKerberosTgtPassing, oAFSTokenPassing,
98#endif
99 oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward,
100 oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand,
101 oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts,
102 oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
103 oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication,
104 oUsePrivilegedPort
105} OpCodes;
106
107/* Textual representations of the tokens. */
108
109static struct
110{
111 const char *name;
112 OpCodes opcode;
113} keywords[] =
114{
115 { "forwardagent", oForwardAgent },
116 { "forwardx11", oForwardX11 },
117 { "gatewayports", oGatewayPorts },
118 { "useprivilegedport", oUsePrivilegedPort },
119 { "rhostsauthentication", oRhostsAuthentication },
120 { "passwordauthentication", oPasswordAuthentication },
121 { "rsaauthentication", oRSAAuthentication },
122#ifdef KRB4
123 { "kerberosauthentication", oKerberosAuthentication },
124#endif /* KRB4 */
125#ifdef AFS
126 { "kerberostgtpassing", oKerberosTgtPassing },
127 { "afstokenpassing", oAFSTokenPassing },
128#endif
129 { "fallbacktorsh", oFallBackToRsh },
130 { "usersh", oUseRsh },
131 { "identityfile", oIdentityFile },
132 { "hostname", oHostName },
133 { "proxycommand", oProxyCommand },
134 { "port", oPort },
135 { "cipher", oCipher },
136 { "remoteforward", oRemoteForward },
137 { "localforward", oLocalForward },
138 { "user", oUser },
139 { "host", oHost },
140 { "escapechar", oEscapeChar },
141 { "rhostsrsaauthentication", oRhostsRSAAuthentication },
142 { "globalknownhostsfile", oGlobalKnownHostsFile },
143 { "userknownhostsfile", oUserKnownHostsFile },
144 { "connectionattempts", oConnectionAttempts },
145 { "batchmode", oBatchMode },
146 { "checkhostip", oCheckHostIP },
147 { "stricthostkeychecking", oStrictHostKeyChecking },
148 { "compression", oCompression },
149 { "compressionlevel", oCompressionLevel },
150 { "keepalive", oKeepAlives },
151 { "numberofpasswordprompts", oNumberOfPasswordPrompts },
152 { "tisauthentication", oTISAuthentication },
153 { NULL, 0 }
154};
155
156/* Characters considered whitespace in strtok calls. */
157#define WHITESPACE " \t\r\n"
158
159
160/* Adds a local TCP/IP port forward to options. Never returns if there
161 is an error. */
162
163void add_local_forward(Options *options, int port, const char *host,
164 int host_port)
165{
166 Forward *fwd;
167 extern uid_t original_real_uid;
168 if ((port & 0xffff) != port)
169 fatal("Requested forwarding of nonexistent port %d.", port);
170 if (port < IPPORT_RESERVED && original_real_uid != 0)
171 fatal("Privileged ports can only be forwarded by root.\n");
172 if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
173 fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION);
174 fwd = &options->local_forwards[options->num_local_forwards++];
175 fwd->port = port;
176 fwd->host = xstrdup(host);
177 fwd->host_port = host_port;
178}
179
180/* Adds a remote TCP/IP port forward to options. Never returns if there
181 is an error. */
182
183void add_remote_forward(Options *options, int port, const char *host,
184 int host_port)
185{
186 Forward *fwd;
187 if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
188 fatal("Too many remote forwards (max %d).",
189 SSH_MAX_FORWARDS_PER_DIRECTION);
190 fwd = &options->remote_forwards[options->num_remote_forwards++];
191 fwd->port = port;
192 fwd->host = xstrdup(host);
193 fwd->host_port = host_port;
194}
195
196/* Returns the number of the token pointed to by cp of length len.
197 Never returns if the token is not known. */
198
199static OpCodes parse_token(const char *cp, const char *filename, int linenum)
200{
201 unsigned int i;
202
203 for (i = 0; keywords[i].name; i++)
204 if (strcmp(cp, keywords[i].name) == 0)
205 return keywords[i].opcode;
206
207 fatal("%.200s line %d: Bad configuration option.",
208 filename, linenum);
209 /*NOTREACHED*/
210 return 0;
211}
212
213/* Processes a single option line as used in the configuration files.
214 This only sets those values that have not already been set. */
215
216void process_config_line(Options *options, const char *host,
217 char *line, const char *filename, int linenum,
218 int *activep)
219{
220 char buf[256], *cp, *string, **charptr;
221 int opcode, *intptr, value, fwd_port, fwd_host_port;
222
223 /* Skip leading whitespace. */
224 cp = line + strspn(line, WHITESPACE);
225 if (!*cp || *cp == '\n' || *cp == '#')
226 return;
227
228 /* Get the keyword. (Each line is supposed to begin with a keyword). */
229 cp = strtok(cp, WHITESPACE);
230 {
231 char *t = cp;
232 for (; *t != 0; t++)
233 if ('A' <= *t && *t <= 'Z')
234 *t = *t - 'A' + 'a'; /* tolower */
235
236 }
237 opcode = parse_token(cp, filename, linenum);
238
239 switch (opcode)
240 {
241
242 case oForwardAgent:
243 intptr = &options->forward_agent;
244 parse_flag:
245 cp = strtok(NULL, WHITESPACE);
246 if (!cp)
247 fatal("%.200s line %d: Missing yes/no argument.", filename, linenum);
248 value = 0; /* To avoid compiler warning... */
249 if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)
250 value = 1;
251 else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0)
252 value = 0;
253 else
254 fatal("%.200s line %d: Bad yes/no argument.", filename, linenum);
255 if (*activep && *intptr == -1)
256 *intptr = value;
257 break;
258
259 case oForwardX11:
260 intptr = &options->forward_x11;
261 goto parse_flag;
262
263 case oGatewayPorts:
264 intptr = &options->gateway_ports;
265 goto parse_flag;
266
267 case oUsePrivilegedPort:
268 intptr = &options->use_privileged_port;
269 goto parse_flag;
270
271 case oRhostsAuthentication:
272 intptr = &options->rhosts_authentication;
273 goto parse_flag;
274
275 case oPasswordAuthentication:
276 intptr = &options->password_authentication;
277 goto parse_flag;
278
279 case oRSAAuthentication:
280 intptr = &options->rsa_authentication;
281 goto parse_flag;
282
283 case oRhostsRSAAuthentication:
284 intptr = &options->rhosts_rsa_authentication;
285 goto parse_flag;
286
287#ifdef KRB4
288 case oKerberosAuthentication:
289 intptr = &options->kerberos_authentication;
290 goto parse_flag;
291#endif /* KRB4 */
292
293#ifdef AFS
294 case oKerberosTgtPassing:
295 intptr = &options->kerberos_tgt_passing;
296 goto parse_flag;
297
298 case oAFSTokenPassing:
299 intptr = &options->afs_token_passing;
300 goto parse_flag;
301#endif
302
303 case oFallBackToRsh:
304 intptr = &options->fallback_to_rsh;
305 goto parse_flag;
306
307 case oUseRsh:
308 intptr = &options->use_rsh;
309 goto parse_flag;
310
311 case oBatchMode:
312 intptr = &options->batch_mode;
313 goto parse_flag;
314
315 case oCheckHostIP:
316 intptr = &options->check_host_ip;
317 goto parse_flag;
318
319 case oStrictHostKeyChecking:
320 intptr = &options->strict_host_key_checking;
321 cp = strtok(NULL, WHITESPACE);
322 if (!cp)
323 fatal("%.200s line %d: Missing yes/no argument.",
324 filename, linenum);
325 value = 0; /* To avoid compiler warning... */
326 if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)
327 value = 1;
328 else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0)
329 value = 0;
330 else if (strcmp(cp, "ask") == 0)
331 value = 2;
332 else
333 fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum);
334 if (*activep && *intptr == -1)
335 *intptr = value;
336 break;
337
338 case oCompression:
339 intptr = &options->compression;
340 goto parse_flag;
341
342 case oKeepAlives:
343 intptr = &options->keepalives;
344 goto parse_flag;
345
346 case oNumberOfPasswordPrompts:
347 intptr = &options->number_of_password_prompts;
348 goto parse_int;
349
350 case oTISAuthentication:
351 cp = strtok(NULL, WHITESPACE);
352 if (cp != 0 && (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0))
353 fprintf(stderr,
354 "%.99s line %d: Warning, TIS is not supported.\n",
355 filename,
356 linenum);
357 break;
358
359 case oCompressionLevel:
360 intptr = &options->compression_level;
361 goto parse_int;
362
363 case oIdentityFile:
364 cp = strtok(NULL, WHITESPACE);
365 if (!cp)
366 fatal("%.200s line %d: Missing argument.", filename, linenum);
367 if (*activep)
368 {
369 if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES)
370 fatal("%.200s line %d: Too many identity files specified (max %d).",
371 filename, linenum, SSH_MAX_IDENTITY_FILES);
372 options->identity_files[options->num_identity_files++] = xstrdup(cp);
373 }
374 break;
375
376 case oUser:
377 charptr = &options->user;
378 parse_string:
379 cp = strtok(NULL, WHITESPACE);
380 if (!cp)
381 fatal("%.200s line %d: Missing argument.", filename, linenum);
382 if (*activep && *charptr == NULL)
383 *charptr = xstrdup(cp);
384 break;
385
386 case oGlobalKnownHostsFile:
387 charptr = &options->system_hostfile;
388 goto parse_string;
389
390 case oUserKnownHostsFile:
391 charptr = &options->user_hostfile;
392 goto parse_string;
393
394 case oHostName:
395 charptr = &options->hostname;
396 goto parse_string;
397
398 case oProxyCommand:
399 charptr = &options->proxy_command;
400 string = xstrdup("");
401 while ((cp = strtok(NULL, WHITESPACE)) != NULL)
402 {
403 string = xrealloc(string, strlen(string) + strlen(cp) + 2);
404 strcat(string, " ");
405 strcat(string, cp);
406 }
407 if (*activep && *charptr == NULL)
408 *charptr = string;
409 else
410 xfree(string);
411 return;
412
413 case oPort:
414 intptr = &options->port;
415 parse_int:
416 cp = strtok(NULL, WHITESPACE);
417 if (!cp)
418 fatal("%.200s line %d: Missing argument.", filename, linenum);
419 if (cp[0] < '0' || cp[0] > '9')
420 fatal("%.200s line %d: Bad number.", filename, linenum);
421#if 0
422 value = atoi(cp);
423#else
424 {
425 char *ptr;
426 value = strtol(cp, &ptr, 0); /* Octal, decimal, or hex format? */
427 if (cp == ptr)
428 fatal("%.200s line %d: Bad number.", filename, linenum);
429 }
430#endif
431 if (*activep && *intptr == -1)
432 *intptr = value;
433 break;
434
435 case oConnectionAttempts:
436 intptr = &options->connection_attempts;
437 goto parse_int;
438
439 case oCipher:
440 intptr = &options->cipher;
441 cp = strtok(NULL, WHITESPACE);
442 value = cipher_number(cp);
443 if (value == -1)
444 fatal("%.200s line %d: Bad cipher.", filename, linenum);
445 if (*activep && *intptr == -1)
446 *intptr = value;
447 break;
448
449 case oRemoteForward:
450 cp = strtok(NULL, WHITESPACE);
451 if (!cp)
452 fatal("%.200s line %d: Missing argument.", filename, linenum);
453 if (cp[0] < '0' || cp[0] > '9')
454 fatal("%.200s line %d: Badly formatted port number.",
455 filename, linenum);
456 fwd_port = atoi(cp);
457 cp = strtok(NULL, WHITESPACE);
458 if (!cp)
459 fatal("%.200s line %d: Missing second argument.",
460 filename, linenum);
461 if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2)
462 fatal("%.200s line %d: Badly formatted host:port.",
463 filename, linenum);
464 if (*activep)
465 add_remote_forward(options, fwd_port, buf, fwd_host_port);
466 break;
467
468 case oLocalForward:
469 cp = strtok(NULL, WHITESPACE);
470 if (!cp)
471 fatal("%.200s line %d: Missing argument.", filename, linenum);
472 if (cp[0] < '0' || cp[0] > '9')
473 fatal("%.200s line %d: Badly formatted port number.",
474 filename, linenum);
475 fwd_port = atoi(cp);
476 cp = strtok(NULL, WHITESPACE);
477 if (!cp)
478 fatal("%.200s line %d: Missing second argument.",
479 filename, linenum);
480 if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2)
481 fatal("%.200s line %d: Badly formatted host:port.",
482 filename, linenum);
483 if (*activep)
484 add_local_forward(options, fwd_port, buf, fwd_host_port);
485 break;
486
487 case oHost:
488 *activep = 0;
489 while ((cp = strtok(NULL, WHITESPACE)) != NULL)
490 if (match_pattern(host, cp))
491 {
492 debug("Applying options for %.100s", cp);
493 *activep = 1;
494 break;
495 }
496 /* Avoid garbage check below, as strtok already returned NULL. */
497 return;
498
499 case oEscapeChar:
500 intptr = &options->escape_char;
501 cp = strtok(NULL, WHITESPACE);
502 if (!cp)
503 fatal("%.200s line %d: Missing argument.", filename, linenum);
504 if (cp[0] == '^' && cp[2] == 0 &&
505 (unsigned char)cp[1] >= 64 && (unsigned char)cp[1] < 128)
506 value = (unsigned char)cp[1] & 31;
507 else
508 if (strlen(cp) == 1)
509 value = (unsigned char)cp[0];
510 else
511 if (strcmp(cp, "none") == 0)
512 value = -2;
513 else
514 {
515 fatal("%.200s line %d: Bad escape character.",
516 filename, linenum);
517 /*NOTREACHED*/
518 value = 0; /* Avoid compiler warning. */
519 }
520 if (*activep && *intptr == -1)
521 *intptr = value;
522 break;
523
524 default:
525 fatal("parse_config_file: Unimplemented opcode %d", opcode);
526 }
527
528 /* Check that there is no garbage at end of line. */
529 if (strtok(NULL, WHITESPACE) != NULL)
530 fatal("%.200s line %d: garbage at end of line.",
531 filename, linenum);
532}
533
534
535/* Reads the config file and modifies the options accordingly. Options should
536 already be initialized before this call. This never returns if there
537 is an error. If the file does not exist, this returns immediately. */
538
539void read_config_file(const char *filename, const char *host, Options *options)
540{
541 FILE *f;
542 char line[1024];
543 int active, linenum;
544
545 /* Open the file. */
546 f = fopen(filename, "r");
547 if (!f)
548 return;
549
550 debug("Reading configuration data %.200s", filename);
551
552 /* Mark that we are now processing the options. This flag is turned on/off
553 by Host specifications. */
554 active = 1;
555 linenum = 0;
556 while (fgets(line, sizeof(line), f))
557 {
558 /* Update line number counter. */
559 linenum++;
560
561 process_config_line(options, host, line, filename, linenum, &active);
562 }
563 fclose(f);
564}
565
566/* Initializes options to special values that indicate that they have not
567 yet been set. Read_config_file will only set options with this value.
568 Options are processed in the following order: command line, user config
569 file, system config file. Last, fill_default_options is called. */
570
571void initialize_options(Options *options)
572{
573 memset(options, 'X', sizeof(*options));
574 options->forward_agent = -1;
575 options->forward_x11 = -1;
576 options->gateway_ports = -1;
577 options->use_privileged_port = -1;
578 options->rhosts_authentication = -1;
579 options->rsa_authentication = -1;
580#ifdef KRB4
581 options->kerberos_authentication = -1;
582#endif
583#ifdef AFS
584 options->kerberos_tgt_passing = -1;
585 options->afs_token_passing = -1;
586#endif
587 options->password_authentication = -1;
588 options->rhosts_rsa_authentication = -1;
589 options->fallback_to_rsh = -1;
590 options->use_rsh = -1;
591 options->batch_mode = -1;
592 options->check_host_ip = -1;
593 options->strict_host_key_checking = -1;
594 options->compression = -1;
595 options->keepalives = -1;
596 options->compression_level = -1;
597 options->port = -1;
598 options->connection_attempts = -1;
599 options->number_of_password_prompts = -1;
600 options->cipher = -1;
601 options->num_identity_files = 0;
602 options->hostname = NULL;
603 options->proxy_command = NULL;
604 options->user = NULL;
605 options->escape_char = -1;
606 options->system_hostfile = NULL;
607 options->user_hostfile = NULL;
608 options->num_local_forwards = 0;
609 options->num_remote_forwards = 0;
610}
611
612/* Called after processing other sources of option data, this fills those
613 options for which no value has been specified with their default values. */
614
615void fill_default_options(Options *options)
616{
617 if (options->forward_agent == -1)
618 options->forward_agent = 1;
619 if (options->forward_x11 == -1)
620 options->forward_x11 = 1;
621 if (options->gateway_ports == -1)
622 options->gateway_ports = 0;
623 if (options->use_privileged_port == -1)
624 options->use_privileged_port = 1;
625 if (options->rhosts_authentication == -1)
626 options->rhosts_authentication = 1;
627 if (options->rsa_authentication == -1)
628 options->rsa_authentication = 1;
629#ifdef KRB4
630 if (options->kerberos_authentication == -1)
631 options->kerberos_authentication = 1;
632#endif /* KRB4 */
633#ifdef AFS
634 if (options->kerberos_tgt_passing == -1)
635 options->kerberos_tgt_passing = 1;
636 if (options->afs_token_passing == -1)
637 options->afs_token_passing = 1;
638#endif /* AFS */
639 if (options->password_authentication == -1)
640 options->password_authentication = 1;
641 if (options->rhosts_rsa_authentication == -1)
642 options->rhosts_rsa_authentication = 1;
643 if (options->fallback_to_rsh == -1)
644 options->fallback_to_rsh = 1;
645 if (options->use_rsh == -1)
646 options->use_rsh = 0;
647 if (options->batch_mode == -1)
648 options->batch_mode = 0;
649 if (options->check_host_ip == -1)
650 options->check_host_ip = 1;
651 if (options->strict_host_key_checking == -1)
652 options->strict_host_key_checking = 2; /* 2 is default */
653 if (options->compression == -1)
654 options->compression = 0;
655 if (options->keepalives == -1)
656 options->keepalives = 1;
657 if (options->compression_level == -1)
658 options->compression_level = 6;
659 if (options->port == -1)
660 options->port = 0; /* Filled in ssh_connect. */
661 if (options->connection_attempts == -1)
662 options->connection_attempts = 4;
663 if (options->number_of_password_prompts == -1)
664 options->number_of_password_prompts = 3;
665 if (options->cipher == -1)
666 options->cipher = SSH_CIPHER_NOT_SET; /* Selected in ssh_login(). */
667 if (options->num_identity_files == 0)
668 {
669 options->identity_files[0] =
670 xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1);
671 sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY);
672 options->num_identity_files = 1;
673 }
674 if (options->escape_char == -1)
675 options->escape_char = '~';
676 if (options->system_hostfile == NULL)
677 options->system_hostfile = SSH_SYSTEM_HOSTFILE;
678 if (options->user_hostfile == NULL)
679 options->user_hostfile = SSH_USER_HOSTFILE;
680 /* options->proxy_command should not be set by default */
681 /* options->user will be set in the main program if appropriate */
682 /* options->hostname will be set in the main program if appropriate */
683}
684