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