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