diff options
author | Colin Watson <cjwatson@debian.org> | 2014-02-10 00:18:28 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2014-02-10 00:18:28 +0000 |
commit | 9a975a9faed7c4f334e8c8490db3e77e102f2b21 (patch) | |
tree | 764a885ec9a963f6a8b15de6e1765f16b9ac4738 /readconf.c | |
parent | ee196dab7c5f97f0b80c8099343a375bead92010 (diff) | |
parent | cdb6c90811caa5df2df856be9b0b16db020fe31d (diff) |
Import openssh_6.5p1.orig.tar.gz
Diffstat (limited to 'readconf.c')
-rw-r--r-- | readconf.c | 578 |
1 files changed, 461 insertions, 117 deletions
diff --git a/readconf.c b/readconf.c index 1464430a4..9c7e73d7d 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.c,v 1.204 2013/06/10 19:19:44 dtucker Exp $ */ | 1 | /* $OpenBSD: readconf.c,v 1.215 2013/12/06 13:39:49 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -17,6 +17,7 @@ | |||
17 | #include <sys/types.h> | 17 | #include <sys/types.h> |
18 | #include <sys/stat.h> | 18 | #include <sys/stat.h> |
19 | #include <sys/socket.h> | 19 | #include <sys/socket.h> |
20 | #include <sys/wait.h> | ||
20 | 21 | ||
21 | #include <netinet/in.h> | 22 | #include <netinet/in.h> |
22 | #include <netinet/in_systm.h> | 23 | #include <netinet/in_systm.h> |
@@ -24,7 +25,12 @@ | |||
24 | 25 | ||
25 | #include <ctype.h> | 26 | #include <ctype.h> |
26 | #include <errno.h> | 27 | #include <errno.h> |
28 | #include <fcntl.h> | ||
27 | #include <netdb.h> | 29 | #include <netdb.h> |
30 | #ifdef HAVE_PATHS_H | ||
31 | # include <paths.h> | ||
32 | #endif | ||
33 | #include <pwd.h> | ||
28 | #include <signal.h> | 34 | #include <signal.h> |
29 | #include <stdarg.h> | 35 | #include <stdarg.h> |
30 | #include <stdio.h> | 36 | #include <stdio.h> |
@@ -47,6 +53,7 @@ | |||
47 | #include "buffer.h" | 53 | #include "buffer.h" |
48 | #include "kex.h" | 54 | #include "kex.h" |
49 | #include "mac.h" | 55 | #include "mac.h" |
56 | #include "uidswap.h" | ||
50 | 57 | ||
51 | /* Format of the configuration file: | 58 | /* Format of the configuration file: |
52 | 59 | ||
@@ -115,12 +122,13 @@ | |||
115 | 122 | ||
116 | typedef enum { | 123 | typedef enum { |
117 | oBadOption, | 124 | oBadOption, |
125 | oHost, oMatch, | ||
118 | oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, | 126 | oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, |
119 | oGatewayPorts, oExitOnForwardFailure, | 127 | oGatewayPorts, oExitOnForwardFailure, |
120 | oPasswordAuthentication, oRSAAuthentication, | 128 | oPasswordAuthentication, oRSAAuthentication, |
121 | oChallengeResponseAuthentication, oXAuthLocation, | 129 | oChallengeResponseAuthentication, oXAuthLocation, |
122 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, | 130 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, |
123 | oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, | 131 | oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, |
124 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | 132 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
125 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | 133 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
126 | oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, | 134 | oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, |
@@ -137,7 +145,9 @@ typedef enum { | |||
137 | oHashKnownHosts, | 145 | oHashKnownHosts, |
138 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | 146 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, |
139 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, | 147 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, |
140 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, | 148 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, |
149 | oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | ||
150 | oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | ||
141 | oIgnoredUnknownOption, oDeprecated, oUnsupported | 151 | oIgnoredUnknownOption, oDeprecated, oUnsupported |
142 | } OpCodes; | 152 | } OpCodes; |
143 | 153 | ||
@@ -194,6 +204,7 @@ static struct { | |||
194 | { "localforward", oLocalForward }, | 204 | { "localforward", oLocalForward }, |
195 | { "user", oUser }, | 205 | { "user", oUser }, |
196 | { "host", oHost }, | 206 | { "host", oHost }, |
207 | { "match", oMatch }, | ||
197 | { "escapechar", oEscapeChar }, | 208 | { "escapechar", oEscapeChar }, |
198 | { "globalknownhostsfile", oGlobalKnownHostsFile }, | 209 | { "globalknownhostsfile", oGlobalKnownHostsFile }, |
199 | { "globalknownhostsfile2", oDeprecated }, | 210 | { "globalknownhostsfile2", oDeprecated }, |
@@ -249,6 +260,12 @@ static struct { | |||
249 | { "kexalgorithms", oKexAlgorithms }, | 260 | { "kexalgorithms", oKexAlgorithms }, |
250 | { "ipqos", oIPQoS }, | 261 | { "ipqos", oIPQoS }, |
251 | { "requesttty", oRequestTTY }, | 262 | { "requesttty", oRequestTTY }, |
263 | { "proxyusefdpass", oProxyUseFdpass }, | ||
264 | { "canonicaldomains", oCanonicalDomains }, | ||
265 | { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, | ||
266 | { "canonicalizehostname", oCanonicalizeHostname }, | ||
267 | { "canonicalizemaxdots", oCanonicalizeMaxDots }, | ||
268 | { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, | ||
252 | { "ignoreunknown", oIgnoreUnknown }, | 269 | { "ignoreunknown", oIgnoreUnknown }, |
253 | 270 | ||
254 | { NULL, oBadOption } | 271 | { NULL, oBadOption } |
@@ -348,10 +365,243 @@ add_identity_file(Options *options, const char *dir, const char *filename, | |||
348 | options->identity_files[options->num_identity_files++] = path; | 365 | options->identity_files[options->num_identity_files++] = path; |
349 | } | 366 | } |
350 | 367 | ||
368 | int | ||
369 | default_ssh_port(void) | ||
370 | { | ||
371 | static int port; | ||
372 | struct servent *sp; | ||
373 | |||
374 | if (port == 0) { | ||
375 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
376 | port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; | ||
377 | } | ||
378 | return port; | ||
379 | } | ||
380 | |||
351 | /* | 381 | /* |
352 | * Returns the number of the token pointed to by cp or oBadOption. | 382 | * Execute a command in a shell. |
383 | * Return its exit status or -1 on abnormal exit. | ||
353 | */ | 384 | */ |
385 | static int | ||
386 | execute_in_shell(const char *cmd) | ||
387 | { | ||
388 | char *shell, *command_string; | ||
389 | pid_t pid; | ||
390 | int devnull, status; | ||
391 | extern uid_t original_real_uid; | ||
392 | |||
393 | if ((shell = getenv("SHELL")) == NULL) | ||
394 | shell = _PATH_BSHELL; | ||
395 | |||
396 | /* | ||
397 | * Use "exec" to avoid "sh -c" processes on some platforms | ||
398 | * (e.g. Solaris) | ||
399 | */ | ||
400 | xasprintf(&command_string, "exec %s", cmd); | ||
401 | |||
402 | /* Need this to redirect subprocess stdin/out */ | ||
403 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) | ||
404 | fatal("open(/dev/null): %s", strerror(errno)); | ||
405 | |||
406 | debug("Executing command: '%.500s'", cmd); | ||
407 | |||
408 | /* Fork and execute the command. */ | ||
409 | if ((pid = fork()) == 0) { | ||
410 | char *argv[4]; | ||
411 | |||
412 | /* Child. Permanently give up superuser privileges. */ | ||
413 | permanently_drop_suid(original_real_uid); | ||
414 | |||
415 | /* Redirect child stdin and stdout. Leave stderr */ | ||
416 | if (dup2(devnull, STDIN_FILENO) == -1) | ||
417 | fatal("dup2: %s", strerror(errno)); | ||
418 | if (dup2(devnull, STDOUT_FILENO) == -1) | ||
419 | fatal("dup2: %s", strerror(errno)); | ||
420 | if (devnull > STDERR_FILENO) | ||
421 | close(devnull); | ||
422 | closefrom(STDERR_FILENO + 1); | ||
423 | |||
424 | argv[0] = shell; | ||
425 | argv[1] = "-c"; | ||
426 | argv[2] = command_string; | ||
427 | argv[3] = NULL; | ||
428 | |||
429 | execv(argv[0], argv); | ||
430 | error("Unable to execute '%.100s': %s", cmd, strerror(errno)); | ||
431 | /* Die with signal to make this error apparent to parent. */ | ||
432 | signal(SIGTERM, SIG_DFL); | ||
433 | kill(getpid(), SIGTERM); | ||
434 | _exit(1); | ||
435 | } | ||
436 | /* Parent. */ | ||
437 | if (pid < 0) | ||
438 | fatal("%s: fork: %.100s", __func__, strerror(errno)); | ||
354 | 439 | ||
440 | close(devnull); | ||
441 | free(command_string); | ||
442 | |||
443 | while (waitpid(pid, &status, 0) == -1) { | ||
444 | if (errno != EINTR && errno != EAGAIN) | ||
445 | fatal("%s: waitpid: %s", __func__, strerror(errno)); | ||
446 | } | ||
447 | if (!WIFEXITED(status)) { | ||
448 | error("command '%.100s' exited abnormally", cmd); | ||
449 | return -1; | ||
450 | } | ||
451 | debug3("command returned status %d", WEXITSTATUS(status)); | ||
452 | return WEXITSTATUS(status); | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * Parse and execute a Match directive. | ||
457 | */ | ||
458 | static int | ||
459 | match_cfg_line(Options *options, char **condition, struct passwd *pw, | ||
460 | const char *host_arg, const char *filename, int linenum) | ||
461 | { | ||
462 | char *arg, *attrib, *cmd, *cp = *condition, *host; | ||
463 | const char *ruser; | ||
464 | int r, port, result = 1, attributes = 0; | ||
465 | size_t len; | ||
466 | char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; | ||
467 | |||
468 | /* | ||
469 | * Configuration is likely to be incomplete at this point so we | ||
470 | * must be prepared to use default values. | ||
471 | */ | ||
472 | port = options->port <= 0 ? default_ssh_port() : options->port; | ||
473 | ruser = options->user == NULL ? pw->pw_name : options->user; | ||
474 | if (options->hostname != NULL) { | ||
475 | /* NB. Please keep in sync with ssh.c:main() */ | ||
476 | host = percent_expand(options->hostname, | ||
477 | "h", host_arg, (char *)NULL); | ||
478 | } else | ||
479 | host = xstrdup(host_arg); | ||
480 | |||
481 | debug3("checking match for '%s' host %s", cp, host); | ||
482 | while ((attrib = strdelim(&cp)) && *attrib != '\0') { | ||
483 | attributes++; | ||
484 | if (strcasecmp(attrib, "all") == 0) { | ||
485 | if (attributes != 1 || | ||
486 | ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { | ||
487 | error("'all' cannot be combined with other " | ||
488 | "Match attributes"); | ||
489 | result = -1; | ||
490 | goto out; | ||
491 | } | ||
492 | *condition = cp; | ||
493 | result = 1; | ||
494 | goto out; | ||
495 | } | ||
496 | if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { | ||
497 | error("Missing Match criteria for %s", attrib); | ||
498 | result = -1; | ||
499 | goto out; | ||
500 | } | ||
501 | len = strlen(arg); | ||
502 | if (strcasecmp(attrib, "host") == 0) { | ||
503 | if (match_hostname(host, arg, len) != 1) | ||
504 | result = 0; | ||
505 | else | ||
506 | debug("%.200s line %d: matched 'Host %.100s' ", | ||
507 | filename, linenum, host); | ||
508 | } else if (strcasecmp(attrib, "originalhost") == 0) { | ||
509 | if (match_hostname(host_arg, arg, len) != 1) | ||
510 | result = 0; | ||
511 | else | ||
512 | debug("%.200s line %d: matched " | ||
513 | "'OriginalHost %.100s' ", | ||
514 | filename, linenum, host_arg); | ||
515 | } else if (strcasecmp(attrib, "user") == 0) { | ||
516 | if (match_pattern_list(ruser, arg, len, 0) != 1) | ||
517 | result = 0; | ||
518 | else | ||
519 | debug("%.200s line %d: matched 'User %.100s' ", | ||
520 | filename, linenum, ruser); | ||
521 | } else if (strcasecmp(attrib, "localuser") == 0) { | ||
522 | if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) | ||
523 | result = 0; | ||
524 | else | ||
525 | debug("%.200s line %d: matched " | ||
526 | "'LocalUser %.100s' ", | ||
527 | filename, linenum, pw->pw_name); | ||
528 | } else if (strcasecmp(attrib, "exec") == 0) { | ||
529 | if (gethostname(thishost, sizeof(thishost)) == -1) | ||
530 | fatal("gethostname: %s", strerror(errno)); | ||
531 | strlcpy(shorthost, thishost, sizeof(shorthost)); | ||
532 | shorthost[strcspn(thishost, ".")] = '\0'; | ||
533 | snprintf(portstr, sizeof(portstr), "%d", port); | ||
534 | |||
535 | cmd = percent_expand(arg, | ||
536 | "L", shorthost, | ||
537 | "d", pw->pw_dir, | ||
538 | "h", host, | ||
539 | "l", thishost, | ||
540 | "n", host_arg, | ||
541 | "p", portstr, | ||
542 | "r", ruser, | ||
543 | "u", pw->pw_name, | ||
544 | (char *)NULL); | ||
545 | r = execute_in_shell(cmd); | ||
546 | if (r == -1) { | ||
547 | fatal("%.200s line %d: match exec '%.100s' " | ||
548 | "error", filename, linenum, cmd); | ||
549 | } else if (r == 0) { | ||
550 | debug("%.200s line %d: matched " | ||
551 | "'exec \"%.100s\"' ", | ||
552 | filename, linenum, cmd); | ||
553 | } else | ||
554 | result = 0; | ||
555 | free(cmd); | ||
556 | } else { | ||
557 | error("Unsupported Match attribute %s", attrib); | ||
558 | result = -1; | ||
559 | goto out; | ||
560 | } | ||
561 | } | ||
562 | if (attributes == 0) { | ||
563 | error("One or more attributes required for Match"); | ||
564 | result = -1; | ||
565 | goto out; | ||
566 | } | ||
567 | debug3("match %sfound", result ? "" : "not "); | ||
568 | *condition = cp; | ||
569 | out: | ||
570 | free(host); | ||
571 | return result; | ||
572 | } | ||
573 | |||
574 | /* Check and prepare a domain name: removes trailing '.' and lowercases */ | ||
575 | static void | ||
576 | valid_domain(char *name, const char *filename, int linenum) | ||
577 | { | ||
578 | size_t i, l = strlen(name); | ||
579 | u_char c, last = '\0'; | ||
580 | |||
581 | if (l == 0) | ||
582 | fatal("%s line %d: empty hostname suffix", filename, linenum); | ||
583 | if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) | ||
584 | fatal("%s line %d: hostname suffix \"%.100s\" " | ||
585 | "starts with invalid character", filename, linenum, name); | ||
586 | for (i = 0; i < l; i++) { | ||
587 | c = tolower((u_char)name[i]); | ||
588 | name[i] = (char)c; | ||
589 | if (last == '.' && c == '.') | ||
590 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
591 | "consecutive separators", filename, linenum, name); | ||
592 | if (c != '.' && c != '-' && !isalnum(c) && | ||
593 | c != '_') /* technically invalid, but common */ | ||
594 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
595 | "invalid characters", filename, linenum, name); | ||
596 | last = c; | ||
597 | } | ||
598 | if (name[l - 1] == '.') | ||
599 | name[l - 1] = '\0'; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * Returns the number of the token pointed to by cp or oBadOption. | ||
604 | */ | ||
355 | static OpCodes | 605 | static OpCodes |
356 | parse_token(const char *cp, const char *filename, int linenum, | 606 | parse_token(const char *cp, const char *filename, int linenum, |
357 | const char *ignored_unknown) | 607 | const char *ignored_unknown) |
@@ -369,25 +619,93 @@ parse_token(const char *cp, const char *filename, int linenum, | |||
369 | return oBadOption; | 619 | return oBadOption; |
370 | } | 620 | } |
371 | 621 | ||
622 | /* Multistate option parsing */ | ||
623 | struct multistate { | ||
624 | char *key; | ||
625 | int value; | ||
626 | }; | ||
627 | static const struct multistate multistate_flag[] = { | ||
628 | { "true", 1 }, | ||
629 | { "false", 0 }, | ||
630 | { "yes", 1 }, | ||
631 | { "no", 0 }, | ||
632 | { NULL, -1 } | ||
633 | }; | ||
634 | static const struct multistate multistate_yesnoask[] = { | ||
635 | { "true", 1 }, | ||
636 | { "false", 0 }, | ||
637 | { "yes", 1 }, | ||
638 | { "no", 0 }, | ||
639 | { "ask", 2 }, | ||
640 | { NULL, -1 } | ||
641 | }; | ||
642 | static const struct multistate multistate_addressfamily[] = { | ||
643 | { "inet", AF_INET }, | ||
644 | { "inet6", AF_INET6 }, | ||
645 | { "any", AF_UNSPEC }, | ||
646 | { NULL, -1 } | ||
647 | }; | ||
648 | static const struct multistate multistate_controlmaster[] = { | ||
649 | { "true", SSHCTL_MASTER_YES }, | ||
650 | { "yes", SSHCTL_MASTER_YES }, | ||
651 | { "false", SSHCTL_MASTER_NO }, | ||
652 | { "no", SSHCTL_MASTER_NO }, | ||
653 | { "auto", SSHCTL_MASTER_AUTO }, | ||
654 | { "ask", SSHCTL_MASTER_ASK }, | ||
655 | { "autoask", SSHCTL_MASTER_AUTO_ASK }, | ||
656 | { NULL, -1 } | ||
657 | }; | ||
658 | static const struct multistate multistate_tunnel[] = { | ||
659 | { "ethernet", SSH_TUNMODE_ETHERNET }, | ||
660 | { "point-to-point", SSH_TUNMODE_POINTOPOINT }, | ||
661 | { "true", SSH_TUNMODE_DEFAULT }, | ||
662 | { "yes", SSH_TUNMODE_DEFAULT }, | ||
663 | { "false", SSH_TUNMODE_NO }, | ||
664 | { "no", SSH_TUNMODE_NO }, | ||
665 | { NULL, -1 } | ||
666 | }; | ||
667 | static const struct multistate multistate_requesttty[] = { | ||
668 | { "true", REQUEST_TTY_YES }, | ||
669 | { "yes", REQUEST_TTY_YES }, | ||
670 | { "false", REQUEST_TTY_NO }, | ||
671 | { "no", REQUEST_TTY_NO }, | ||
672 | { "force", REQUEST_TTY_FORCE }, | ||
673 | { "auto", REQUEST_TTY_AUTO }, | ||
674 | { NULL, -1 } | ||
675 | }; | ||
676 | static const struct multistate multistate_canonicalizehostname[] = { | ||
677 | { "true", SSH_CANONICALISE_YES }, | ||
678 | { "false", SSH_CANONICALISE_NO }, | ||
679 | { "yes", SSH_CANONICALISE_YES }, | ||
680 | { "no", SSH_CANONICALISE_NO }, | ||
681 | { "always", SSH_CANONICALISE_ALWAYS }, | ||
682 | { NULL, -1 } | ||
683 | }; | ||
684 | |||
372 | /* | 685 | /* |
373 | * Processes a single option line as used in the configuration files. This | 686 | * Processes a single option line as used in the configuration files. This |
374 | * only sets those values that have not already been set. | 687 | * only sets those values that have not already been set. |
375 | */ | 688 | */ |
376 | #define WHITESPACE " \t\r\n" | 689 | #define WHITESPACE " \t\r\n" |
377 | |||
378 | int | 690 | int |
379 | process_config_line(Options *options, const char *host, | 691 | process_config_line(Options *options, struct passwd *pw, const char *host, |
380 | char *line, const char *filename, int linenum, | 692 | char *line, const char *filename, int linenum, int *activep, int userconfig) |
381 | int *activep, int userconfig) | ||
382 | { | 693 | { |
383 | char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; | 694 | char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; |
384 | char **cpptr, fwdarg[256]; | 695 | char **cpptr, fwdarg[256]; |
385 | u_int i, *uintptr, max_entries = 0; | 696 | u_int i, *uintptr, max_entries = 0; |
386 | int negated, opcode, *intptr, value, value2; | 697 | int negated, opcode, *intptr, value, value2, cmdline = 0; |
387 | LogLevel *log_level_ptr; | 698 | LogLevel *log_level_ptr; |
388 | long long val64; | 699 | long long val64; |
389 | size_t len; | 700 | size_t len; |
390 | Forward fwd; | 701 | Forward fwd; |
702 | const struct multistate *multistate_ptr; | ||
703 | struct allowed_cname *cname; | ||
704 | |||
705 | if (activep == NULL) { /* We are processing a command line directive */ | ||
706 | cmdline = 1; | ||
707 | activep = &cmdline; | ||
708 | } | ||
391 | 709 | ||
392 | /* Strip trailing whitespace */ | 710 | /* Strip trailing whitespace */ |
393 | for (len = strlen(line) - 1; len > 0; len--) { | 711 | for (len = strlen(line) - 1; len > 0; len--) { |
@@ -406,8 +724,7 @@ process_config_line(Options *options, const char *host, | |||
406 | if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') | 724 | if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') |
407 | return 0; | 725 | return 0; |
408 | /* Match lowercase keyword */ | 726 | /* Match lowercase keyword */ |
409 | for (i = 0; i < strlen(keyword); i++) | 727 | lowercase(keyword); |
410 | keyword[i] = tolower(keyword[i]); | ||
411 | 728 | ||
412 | opcode = parse_token(keyword, filename, linenum, | 729 | opcode = parse_token(keyword, filename, linenum, |
413 | options->ignored_unknown); | 730 | options->ignored_unknown); |
@@ -437,17 +754,23 @@ parse_time: | |||
437 | 754 | ||
438 | case oForwardAgent: | 755 | case oForwardAgent: |
439 | intptr = &options->forward_agent; | 756 | intptr = &options->forward_agent; |
440 | parse_flag: | 757 | parse_flag: |
758 | multistate_ptr = multistate_flag; | ||
759 | parse_multistate: | ||
441 | arg = strdelim(&s); | 760 | arg = strdelim(&s); |
442 | if (!arg || *arg == '\0') | 761 | if (!arg || *arg == '\0') |
443 | fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); | 762 | fatal("%s line %d: missing argument.", |
444 | value = 0; /* To avoid compiler warning... */ | 763 | filename, linenum); |
445 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | 764 | value = -1; |
446 | value = 1; | 765 | for (i = 0; multistate_ptr[i].key != NULL; i++) { |
447 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | 766 | if (strcasecmp(arg, multistate_ptr[i].key) == 0) { |
448 | value = 0; | 767 | value = multistate_ptr[i].value; |
449 | else | 768 | break; |
450 | fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); | 769 | } |
770 | } | ||
771 | if (value == -1) | ||
772 | fatal("%s line %d: unsupported option \"%s\".", | ||
773 | filename, linenum, arg); | ||
451 | if (*activep && *intptr == -1) | 774 | if (*activep && *intptr == -1) |
452 | *intptr = value; | 775 | *intptr = value; |
453 | break; | 776 | break; |
@@ -530,27 +853,13 @@ parse_flag: | |||
530 | 853 | ||
531 | case oVerifyHostKeyDNS: | 854 | case oVerifyHostKeyDNS: |
532 | intptr = &options->verify_host_key_dns; | 855 | intptr = &options->verify_host_key_dns; |
533 | goto parse_yesnoask; | 856 | multistate_ptr = multistate_yesnoask; |
857 | goto parse_multistate; | ||
534 | 858 | ||
535 | case oStrictHostKeyChecking: | 859 | case oStrictHostKeyChecking: |
536 | intptr = &options->strict_host_key_checking; | 860 | intptr = &options->strict_host_key_checking; |
537 | parse_yesnoask: | 861 | multistate_ptr = multistate_yesnoask; |
538 | arg = strdelim(&s); | 862 | goto parse_multistate; |
539 | if (!arg || *arg == '\0') | ||
540 | fatal("%.200s line %d: Missing yes/no/ask argument.", | ||
541 | filename, linenum); | ||
542 | value = 0; /* To avoid compiler warning... */ | ||
543 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | ||
544 | value = 1; | ||
545 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | ||
546 | value = 0; | ||
547 | else if (strcmp(arg, "ask") == 0) | ||
548 | value = 2; | ||
549 | else | ||
550 | fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); | ||
551 | if (*activep && *intptr == -1) | ||
552 | *intptr = value; | ||
553 | break; | ||
554 | 863 | ||
555 | case oCompression: | 864 | case oCompression: |
556 | intptr = &options->compression; | 865 | intptr = &options->compression; |
@@ -827,6 +1136,9 @@ parse_int: | |||
827 | goto parse_flag; | 1136 | goto parse_flag; |
828 | 1137 | ||
829 | case oHost: | 1138 | case oHost: |
1139 | if (cmdline) | ||
1140 | fatal("Host directive not supported as a command-line " | ||
1141 | "option"); | ||
830 | *activep = 0; | 1142 | *activep = 0; |
831 | arg2 = NULL; | 1143 | arg2 = NULL; |
832 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | 1144 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { |
@@ -853,6 +1165,18 @@ parse_int: | |||
853 | /* Avoid garbage check below, as strdelim is done. */ | 1165 | /* Avoid garbage check below, as strdelim is done. */ |
854 | return 0; | 1166 | return 0; |
855 | 1167 | ||
1168 | case oMatch: | ||
1169 | if (cmdline) | ||
1170 | fatal("Host directive not supported as a command-line " | ||
1171 | "option"); | ||
1172 | value = match_cfg_line(options, &s, pw, host, | ||
1173 | filename, linenum); | ||
1174 | if (value < 0) | ||
1175 | fatal("%.200s line %d: Bad Match condition", filename, | ||
1176 | linenum); | ||
1177 | *activep = value; | ||
1178 | break; | ||
1179 | |||
856 | case oEscapeChar: | 1180 | case oEscapeChar: |
857 | intptr = &options->escape_char; | 1181 | intptr = &options->escape_char; |
858 | arg = strdelim(&s); | 1182 | arg = strdelim(&s); |
@@ -876,22 +1200,9 @@ parse_int: | |||
876 | break; | 1200 | break; |
877 | 1201 | ||
878 | case oAddressFamily: | 1202 | case oAddressFamily: |
879 | arg = strdelim(&s); | ||
880 | if (!arg || *arg == '\0') | ||
881 | fatal("%s line %d: missing address family.", | ||
882 | filename, linenum); | ||
883 | intptr = &options->address_family; | 1203 | intptr = &options->address_family; |
884 | if (strcasecmp(arg, "inet") == 0) | 1204 | multistate_ptr = multistate_addressfamily; |
885 | value = AF_INET; | 1205 | goto parse_multistate; |
886 | else if (strcasecmp(arg, "inet6") == 0) | ||
887 | value = AF_INET6; | ||
888 | else if (strcasecmp(arg, "any") == 0) | ||
889 | value = AF_UNSPEC; | ||
890 | else | ||
891 | fatal("Unsupported AddressFamily \"%s\"", arg); | ||
892 | if (*activep && *intptr == -1) | ||
893 | *intptr = value; | ||
894 | break; | ||
895 | 1206 | ||
896 | case oEnableSSHKeysign: | 1207 | case oEnableSSHKeysign: |
897 | intptr = &options->enable_ssh_keysign; | 1208 | intptr = &options->enable_ssh_keysign; |
@@ -930,27 +1241,8 @@ parse_int: | |||
930 | 1241 | ||
931 | case oControlMaster: | 1242 | case oControlMaster: |
932 | intptr = &options->control_master; | 1243 | intptr = &options->control_master; |
933 | arg = strdelim(&s); | 1244 | multistate_ptr = multistate_controlmaster; |
934 | if (!arg || *arg == '\0') | 1245 | goto parse_multistate; |
935 | fatal("%.200s line %d: Missing ControlMaster argument.", | ||
936 | filename, linenum); | ||
937 | value = 0; /* To avoid compiler warning... */ | ||
938 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | ||
939 | value = SSHCTL_MASTER_YES; | ||
940 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | ||
941 | value = SSHCTL_MASTER_NO; | ||
942 | else if (strcmp(arg, "auto") == 0) | ||
943 | value = SSHCTL_MASTER_AUTO; | ||
944 | else if (strcmp(arg, "ask") == 0) | ||
945 | value = SSHCTL_MASTER_ASK; | ||
946 | else if (strcmp(arg, "autoask") == 0) | ||
947 | value = SSHCTL_MASTER_AUTO_ASK; | ||
948 | else | ||
949 | fatal("%.200s line %d: Bad ControlMaster argument.", | ||
950 | filename, linenum); | ||
951 | if (*activep && *intptr == -1) | ||
952 | *intptr = value; | ||
953 | break; | ||
954 | 1246 | ||
955 | case oControlPersist: | 1247 | case oControlPersist: |
956 | /* no/false/yes/true, or a time spec */ | 1248 | /* no/false/yes/true, or a time spec */ |
@@ -982,25 +1274,8 @@ parse_int: | |||
982 | 1274 | ||
983 | case oTunnel: | 1275 | case oTunnel: |
984 | intptr = &options->tun_open; | 1276 | intptr = &options->tun_open; |
985 | arg = strdelim(&s); | 1277 | multistate_ptr = multistate_tunnel; |
986 | if (!arg || *arg == '\0') | 1278 | goto parse_multistate; |
987 | fatal("%s line %d: Missing yes/point-to-point/" | ||
988 | "ethernet/no argument.", filename, linenum); | ||
989 | value = 0; /* silence compiler */ | ||
990 | if (strcasecmp(arg, "ethernet") == 0) | ||
991 | value = SSH_TUNMODE_ETHERNET; | ||
992 | else if (strcasecmp(arg, "point-to-point") == 0) | ||
993 | value = SSH_TUNMODE_POINTOPOINT; | ||
994 | else if (strcasecmp(arg, "yes") == 0) | ||
995 | value = SSH_TUNMODE_DEFAULT; | ||
996 | else if (strcasecmp(arg, "no") == 0) | ||
997 | value = SSH_TUNMODE_NO; | ||
998 | else | ||
999 | fatal("%s line %d: Bad yes/point-to-point/ethernet/" | ||
1000 | "no argument: %s", filename, linenum, arg); | ||
1001 | if (*activep) | ||
1002 | *intptr = value; | ||
1003 | break; | ||
1004 | 1279 | ||
1005 | case oTunnelDevice: | 1280 | case oTunnelDevice: |
1006 | arg = strdelim(&s); | 1281 | arg = strdelim(&s); |
@@ -1049,29 +1324,74 @@ parse_int: | |||
1049 | goto parse_flag; | 1324 | goto parse_flag; |
1050 | 1325 | ||
1051 | case oRequestTTY: | 1326 | case oRequestTTY: |
1052 | arg = strdelim(&s); | ||
1053 | if (!arg || *arg == '\0') | ||
1054 | fatal("%s line %d: missing argument.", | ||
1055 | filename, linenum); | ||
1056 | intptr = &options->request_tty; | 1327 | intptr = &options->request_tty; |
1057 | if (strcasecmp(arg, "yes") == 0) | 1328 | multistate_ptr = multistate_requesttty; |
1058 | value = REQUEST_TTY_YES; | 1329 | goto parse_multistate; |
1059 | else if (strcasecmp(arg, "no") == 0) | ||
1060 | value = REQUEST_TTY_NO; | ||
1061 | else if (strcasecmp(arg, "force") == 0) | ||
1062 | value = REQUEST_TTY_FORCE; | ||
1063 | else if (strcasecmp(arg, "auto") == 0) | ||
1064 | value = REQUEST_TTY_AUTO; | ||
1065 | else | ||
1066 | fatal("Unsupported RequestTTY \"%s\"", arg); | ||
1067 | if (*activep && *intptr == -1) | ||
1068 | *intptr = value; | ||
1069 | break; | ||
1070 | 1330 | ||
1071 | case oIgnoreUnknown: | 1331 | case oIgnoreUnknown: |
1072 | charptr = &options->ignored_unknown; | 1332 | charptr = &options->ignored_unknown; |
1073 | goto parse_string; | 1333 | goto parse_string; |
1074 | 1334 | ||
1335 | case oProxyUseFdpass: | ||
1336 | intptr = &options->proxy_use_fdpass; | ||
1337 | goto parse_flag; | ||
1338 | |||
1339 | case oCanonicalDomains: | ||
1340 | value = options->num_canonical_domains != 0; | ||
1341 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1342 | valid_domain(arg, filename, linenum); | ||
1343 | if (!*activep || value) | ||
1344 | continue; | ||
1345 | if (options->num_canonical_domains >= MAX_CANON_DOMAINS) | ||
1346 | fatal("%s line %d: too many hostname suffixes.", | ||
1347 | filename, linenum); | ||
1348 | options->canonical_domains[ | ||
1349 | options->num_canonical_domains++] = xstrdup(arg); | ||
1350 | } | ||
1351 | break; | ||
1352 | |||
1353 | case oCanonicalizePermittedCNAMEs: | ||
1354 | value = options->num_permitted_cnames != 0; | ||
1355 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1356 | /* Either '*' for everything or 'list:list' */ | ||
1357 | if (strcmp(arg, "*") == 0) | ||
1358 | arg2 = arg; | ||
1359 | else { | ||
1360 | lowercase(arg); | ||
1361 | if ((arg2 = strchr(arg, ':')) == NULL || | ||
1362 | arg2[1] == '\0') { | ||
1363 | fatal("%s line %d: " | ||
1364 | "Invalid permitted CNAME \"%s\"", | ||
1365 | filename, linenum, arg); | ||
1366 | } | ||
1367 | *arg2 = '\0'; | ||
1368 | arg2++; | ||
1369 | } | ||
1370 | if (!*activep || value) | ||
1371 | continue; | ||
1372 | if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) | ||
1373 | fatal("%s line %d: too many permitted CNAMEs.", | ||
1374 | filename, linenum); | ||
1375 | cname = options->permitted_cnames + | ||
1376 | options->num_permitted_cnames++; | ||
1377 | cname->source_list = xstrdup(arg); | ||
1378 | cname->target_list = xstrdup(arg2); | ||
1379 | } | ||
1380 | break; | ||
1381 | |||
1382 | case oCanonicalizeHostname: | ||
1383 | intptr = &options->canonicalize_hostname; | ||
1384 | multistate_ptr = multistate_canonicalizehostname; | ||
1385 | goto parse_multistate; | ||
1386 | |||
1387 | case oCanonicalizeMaxDots: | ||
1388 | intptr = &options->canonicalize_max_dots; | ||
1389 | goto parse_int; | ||
1390 | |||
1391 | case oCanonicalizeFallbackLocal: | ||
1392 | intptr = &options->canonicalize_fallback_local; | ||
1393 | goto parse_flag; | ||
1394 | |||
1075 | case oDeprecated: | 1395 | case oDeprecated: |
1076 | debug("%s line %d: Deprecated option \"%s\"", | 1396 | debug("%s line %d: Deprecated option \"%s\"", |
1077 | filename, linenum, keyword); | 1397 | filename, linenum, keyword); |
@@ -1102,8 +1422,8 @@ parse_int: | |||
1102 | */ | 1422 | */ |
1103 | 1423 | ||
1104 | int | 1424 | int |
1105 | read_config_file(const char *filename, const char *host, Options *options, | 1425 | read_config_file(const char *filename, struct passwd *pw, const char *host, |
1106 | int flags) | 1426 | Options *options, int flags) |
1107 | { | 1427 | { |
1108 | FILE *f; | 1428 | FILE *f; |
1109 | char line[1024]; | 1429 | char line[1024]; |
@@ -1134,8 +1454,8 @@ read_config_file(const char *filename, const char *host, Options *options, | |||
1134 | while (fgets(line, sizeof(line), f)) { | 1454 | while (fgets(line, sizeof(line), f)) { |
1135 | /* Update line number counter. */ | 1455 | /* Update line number counter. */ |
1136 | linenum++; | 1456 | linenum++; |
1137 | if (process_config_line(options, host, line, filename, linenum, | 1457 | if (process_config_line(options, pw, host, line, filename, |
1138 | &active, flags & SSHCONF_USERCONF) != 0) | 1458 | linenum, &active, flags & SSHCONF_USERCONF) != 0) |
1139 | bad_options++; | 1459 | bad_options++; |
1140 | } | 1460 | } |
1141 | fclose(f); | 1461 | fclose(f); |
@@ -1233,7 +1553,13 @@ initialize_options(Options * options) | |||
1233 | options->ip_qos_interactive = -1; | 1553 | options->ip_qos_interactive = -1; |
1234 | options->ip_qos_bulk = -1; | 1554 | options->ip_qos_bulk = -1; |
1235 | options->request_tty = -1; | 1555 | options->request_tty = -1; |
1556 | options->proxy_use_fdpass = -1; | ||
1236 | options->ignored_unknown = NULL; | 1557 | options->ignored_unknown = NULL; |
1558 | options->num_canonical_domains = 0; | ||
1559 | options->num_permitted_cnames = 0; | ||
1560 | options->canonicalize_max_dots = -1; | ||
1561 | options->canonicalize_fallback_local = -1; | ||
1562 | options->canonicalize_hostname = -1; | ||
1237 | } | 1563 | } |
1238 | 1564 | ||
1239 | /* | 1565 | /* |
@@ -1321,6 +1647,8 @@ fill_default_options(Options * options) | |||
1321 | add_identity_file(options, "~/", | 1647 | add_identity_file(options, "~/", |
1322 | _PATH_SSH_CLIENT_ID_ECDSA, 0); | 1648 | _PATH_SSH_CLIENT_ID_ECDSA, 0); |
1323 | #endif | 1649 | #endif |
1650 | add_identity_file(options, "~/", | ||
1651 | _PATH_SSH_CLIENT_ID_ED25519, 0); | ||
1324 | } | 1652 | } |
1325 | } | 1653 | } |
1326 | if (options->escape_char == -1) | 1654 | if (options->escape_char == -1) |
@@ -1385,8 +1713,24 @@ fill_default_options(Options * options) | |||
1385 | options->ip_qos_bulk = IPTOS_THROUGHPUT; | 1713 | options->ip_qos_bulk = IPTOS_THROUGHPUT; |
1386 | if (options->request_tty == -1) | 1714 | if (options->request_tty == -1) |
1387 | options->request_tty = REQUEST_TTY_AUTO; | 1715 | options->request_tty = REQUEST_TTY_AUTO; |
1388 | /* options->local_command should not be set by default */ | 1716 | if (options->proxy_use_fdpass == -1) |
1389 | /* options->proxy_command should not be set by default */ | 1717 | options->proxy_use_fdpass = 0; |
1718 | if (options->canonicalize_max_dots == -1) | ||
1719 | options->canonicalize_max_dots = 1; | ||
1720 | if (options->canonicalize_fallback_local == -1) | ||
1721 | options->canonicalize_fallback_local = 1; | ||
1722 | if (options->canonicalize_hostname == -1) | ||
1723 | options->canonicalize_hostname = SSH_CANONICALISE_NO; | ||
1724 | #define CLEAR_ON_NONE(v) \ | ||
1725 | do { \ | ||
1726 | if (v != NULL && strcasecmp(v, "none") == 0) { \ | ||
1727 | free(v); \ | ||
1728 | v = NULL; \ | ||
1729 | } \ | ||
1730 | } while(0) | ||
1731 | CLEAR_ON_NONE(options->local_command); | ||
1732 | CLEAR_ON_NONE(options->proxy_command); | ||
1733 | CLEAR_ON_NONE(options->control_path); | ||
1390 | /* options->user will be set in the main program if appropriate */ | 1734 | /* options->user will be set in the main program if appropriate */ |
1391 | /* options->hostname will be set in the main program if appropriate */ | 1735 | /* options->hostname will be set in the main program if appropriate */ |
1392 | /* options->host_key_alias should not be set by default */ | 1736 | /* options->host_key_alias should not be set by default */ |
@@ -1413,7 +1757,7 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) | |||
1413 | cp = p = xstrdup(fwdspec); | 1757 | cp = p = xstrdup(fwdspec); |
1414 | 1758 | ||
1415 | /* skip leading spaces */ | 1759 | /* skip leading spaces */ |
1416 | while (isspace(*cp)) | 1760 | while (isspace((u_char)*cp)) |
1417 | cp++; | 1761 | cp++; |
1418 | 1762 | ||
1419 | for (i = 0; i < 4; ++i) | 1763 | for (i = 0; i < 4; ++i) |