diff options
author | Colin Watson <cjwatson@debian.org> | 2014-02-10 00:27:24 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2014-02-10 02:40:28 +0000 |
commit | a2b8818c5d21cfcba443625251f691a2ea3a29c7 (patch) | |
tree | 8fe1fe448cde57eecf71a7bcd57186661b90313f /readconf.c | |
parent | d399ecd8eb7d4aed3b7ba0d2727e619607fb901b (diff) | |
parent | ee8d8b97cc2c6081df3af453a228992b87309ec4 (diff) |
Merge 6.5p1.
* New upstream release (http://www.openssh.com/txt/release-6.5,
LP: #1275068):
- ssh(1): Add support for client-side hostname canonicalisation using a
set of DNS suffixes and rules in ssh_config(5). This allows
unqualified names to be canonicalised to fully-qualified domain names
to eliminate ambiguity when looking up keys in known_hosts or checking
host certificate names (closes: #115286).
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 e1e82c5ad..6ac8beae0 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> |
@@ -49,6 +55,7 @@ | |||
49 | #include "buffer.h" | 55 | #include "buffer.h" |
50 | #include "kex.h" | 56 | #include "kex.h" |
51 | #include "mac.h" | 57 | #include "mac.h" |
58 | #include "uidswap.h" | ||
52 | 59 | ||
53 | /* Format of the configuration file: | 60 | /* Format of the configuration file: |
54 | 61 | ||
@@ -117,12 +124,13 @@ | |||
117 | 124 | ||
118 | typedef enum { | 125 | typedef enum { |
119 | oBadOption, | 126 | oBadOption, |
127 | oHost, oMatch, | ||
120 | oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, | 128 | oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, |
121 | oGatewayPorts, oExitOnForwardFailure, | 129 | oGatewayPorts, oExitOnForwardFailure, |
122 | oPasswordAuthentication, oRSAAuthentication, | 130 | oPasswordAuthentication, oRSAAuthentication, |
123 | oChallengeResponseAuthentication, oXAuthLocation, | 131 | oChallengeResponseAuthentication, oXAuthLocation, |
124 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, | 132 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, |
125 | oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, | 133 | oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, |
126 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | 134 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
127 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | 135 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
128 | oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, | 136 | oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, |
@@ -141,7 +149,9 @@ typedef enum { | |||
141 | oHashKnownHosts, | 149 | oHashKnownHosts, |
142 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | 150 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, |
143 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, | 151 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, |
144 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, | 152 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, |
153 | oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, | ||
154 | oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, | ||
145 | oProtocolKeepAlives, oSetupTimeOut, | 155 | oProtocolKeepAlives, oSetupTimeOut, |
146 | oIgnoredUnknownOption, oDeprecated, oUnsupported | 156 | oIgnoredUnknownOption, oDeprecated, oUnsupported |
147 | } OpCodes; | 157 | } OpCodes; |
@@ -209,6 +219,7 @@ static struct { | |||
209 | { "localforward", oLocalForward }, | 219 | { "localforward", oLocalForward }, |
210 | { "user", oUser }, | 220 | { "user", oUser }, |
211 | { "host", oHost }, | 221 | { "host", oHost }, |
222 | { "match", oMatch }, | ||
212 | { "escapechar", oEscapeChar }, | 223 | { "escapechar", oEscapeChar }, |
213 | { "globalknownhostsfile", oGlobalKnownHostsFile }, | 224 | { "globalknownhostsfile", oGlobalKnownHostsFile }, |
214 | { "globalknownhostsfile2", oDeprecated }, | 225 | { "globalknownhostsfile2", oDeprecated }, |
@@ -264,6 +275,12 @@ static struct { | |||
264 | { "kexalgorithms", oKexAlgorithms }, | 275 | { "kexalgorithms", oKexAlgorithms }, |
265 | { "ipqos", oIPQoS }, | 276 | { "ipqos", oIPQoS }, |
266 | { "requesttty", oRequestTTY }, | 277 | { "requesttty", oRequestTTY }, |
278 | { "proxyusefdpass", oProxyUseFdpass }, | ||
279 | { "canonicaldomains", oCanonicalDomains }, | ||
280 | { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, | ||
281 | { "canonicalizehostname", oCanonicalizeHostname }, | ||
282 | { "canonicalizemaxdots", oCanonicalizeMaxDots }, | ||
283 | { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, | ||
267 | { "ignoreunknown", oIgnoreUnknown }, | 284 | { "ignoreunknown", oIgnoreUnknown }, |
268 | { "protocolkeepalives", oProtocolKeepAlives }, | 285 | { "protocolkeepalives", oProtocolKeepAlives }, |
269 | { "setuptimeout", oSetupTimeOut }, | 286 | { "setuptimeout", oSetupTimeOut }, |
@@ -365,10 +382,243 @@ add_identity_file(Options *options, const char *dir, const char *filename, | |||
365 | options->identity_files[options->num_identity_files++] = path; | 382 | options->identity_files[options->num_identity_files++] = path; |
366 | } | 383 | } |
367 | 384 | ||
385 | int | ||
386 | default_ssh_port(void) | ||
387 | { | ||
388 | static int port; | ||
389 | struct servent *sp; | ||
390 | |||
391 | if (port == 0) { | ||
392 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
393 | port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; | ||
394 | } | ||
395 | return port; | ||
396 | } | ||
397 | |||
368 | /* | 398 | /* |
369 | * Returns the number of the token pointed to by cp or oBadOption. | 399 | * Execute a command in a shell. |
400 | * Return its exit status or -1 on abnormal exit. | ||
370 | */ | 401 | */ |
402 | static int | ||
403 | execute_in_shell(const char *cmd) | ||
404 | { | ||
405 | char *shell, *command_string; | ||
406 | pid_t pid; | ||
407 | int devnull, status; | ||
408 | extern uid_t original_real_uid; | ||
409 | |||
410 | if ((shell = getenv("SHELL")) == NULL) | ||
411 | shell = _PATH_BSHELL; | ||
412 | |||
413 | /* | ||
414 | * Use "exec" to avoid "sh -c" processes on some platforms | ||
415 | * (e.g. Solaris) | ||
416 | */ | ||
417 | xasprintf(&command_string, "exec %s", cmd); | ||
418 | |||
419 | /* Need this to redirect subprocess stdin/out */ | ||
420 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) | ||
421 | fatal("open(/dev/null): %s", strerror(errno)); | ||
422 | |||
423 | debug("Executing command: '%.500s'", cmd); | ||
424 | |||
425 | /* Fork and execute the command. */ | ||
426 | if ((pid = fork()) == 0) { | ||
427 | char *argv[4]; | ||
428 | |||
429 | /* Child. Permanently give up superuser privileges. */ | ||
430 | permanently_drop_suid(original_real_uid); | ||
431 | |||
432 | /* Redirect child stdin and stdout. Leave stderr */ | ||
433 | if (dup2(devnull, STDIN_FILENO) == -1) | ||
434 | fatal("dup2: %s", strerror(errno)); | ||
435 | if (dup2(devnull, STDOUT_FILENO) == -1) | ||
436 | fatal("dup2: %s", strerror(errno)); | ||
437 | if (devnull > STDERR_FILENO) | ||
438 | close(devnull); | ||
439 | closefrom(STDERR_FILENO + 1); | ||
440 | |||
441 | argv[0] = shell; | ||
442 | argv[1] = "-c"; | ||
443 | argv[2] = command_string; | ||
444 | argv[3] = NULL; | ||
445 | |||
446 | execv(argv[0], argv); | ||
447 | error("Unable to execute '%.100s': %s", cmd, strerror(errno)); | ||
448 | /* Die with signal to make this error apparent to parent. */ | ||
449 | signal(SIGTERM, SIG_DFL); | ||
450 | kill(getpid(), SIGTERM); | ||
451 | _exit(1); | ||
452 | } | ||
453 | /* Parent. */ | ||
454 | if (pid < 0) | ||
455 | fatal("%s: fork: %.100s", __func__, strerror(errno)); | ||
456 | |||
457 | close(devnull); | ||
458 | free(command_string); | ||
459 | |||
460 | while (waitpid(pid, &status, 0) == -1) { | ||
461 | if (errno != EINTR && errno != EAGAIN) | ||
462 | fatal("%s: waitpid: %s", __func__, strerror(errno)); | ||
463 | } | ||
464 | if (!WIFEXITED(status)) { | ||
465 | error("command '%.100s' exited abnormally", cmd); | ||
466 | return -1; | ||
467 | } | ||
468 | debug3("command returned status %d", WEXITSTATUS(status)); | ||
469 | return WEXITSTATUS(status); | ||
470 | } | ||
471 | |||
472 | /* | ||
473 | * Parse and execute a Match directive. | ||
474 | */ | ||
475 | static int | ||
476 | match_cfg_line(Options *options, char **condition, struct passwd *pw, | ||
477 | const char *host_arg, const char *filename, int linenum) | ||
478 | { | ||
479 | char *arg, *attrib, *cmd, *cp = *condition, *host; | ||
480 | const char *ruser; | ||
481 | int r, port, result = 1, attributes = 0; | ||
482 | size_t len; | ||
483 | char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; | ||
484 | |||
485 | /* | ||
486 | * Configuration is likely to be incomplete at this point so we | ||
487 | * must be prepared to use default values. | ||
488 | */ | ||
489 | port = options->port <= 0 ? default_ssh_port() : options->port; | ||
490 | ruser = options->user == NULL ? pw->pw_name : options->user; | ||
491 | if (options->hostname != NULL) { | ||
492 | /* NB. Please keep in sync with ssh.c:main() */ | ||
493 | host = percent_expand(options->hostname, | ||
494 | "h", host_arg, (char *)NULL); | ||
495 | } else | ||
496 | host = xstrdup(host_arg); | ||
497 | |||
498 | debug3("checking match for '%s' host %s", cp, host); | ||
499 | while ((attrib = strdelim(&cp)) && *attrib != '\0') { | ||
500 | attributes++; | ||
501 | if (strcasecmp(attrib, "all") == 0) { | ||
502 | if (attributes != 1 || | ||
503 | ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { | ||
504 | error("'all' cannot be combined with other " | ||
505 | "Match attributes"); | ||
506 | result = -1; | ||
507 | goto out; | ||
508 | } | ||
509 | *condition = cp; | ||
510 | result = 1; | ||
511 | goto out; | ||
512 | } | ||
513 | if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { | ||
514 | error("Missing Match criteria for %s", attrib); | ||
515 | result = -1; | ||
516 | goto out; | ||
517 | } | ||
518 | len = strlen(arg); | ||
519 | if (strcasecmp(attrib, "host") == 0) { | ||
520 | if (match_hostname(host, arg, len) != 1) | ||
521 | result = 0; | ||
522 | else | ||
523 | debug("%.200s line %d: matched 'Host %.100s' ", | ||
524 | filename, linenum, host); | ||
525 | } else if (strcasecmp(attrib, "originalhost") == 0) { | ||
526 | if (match_hostname(host_arg, arg, len) != 1) | ||
527 | result = 0; | ||
528 | else | ||
529 | debug("%.200s line %d: matched " | ||
530 | "'OriginalHost %.100s' ", | ||
531 | filename, linenum, host_arg); | ||
532 | } else if (strcasecmp(attrib, "user") == 0) { | ||
533 | if (match_pattern_list(ruser, arg, len, 0) != 1) | ||
534 | result = 0; | ||
535 | else | ||
536 | debug("%.200s line %d: matched 'User %.100s' ", | ||
537 | filename, linenum, ruser); | ||
538 | } else if (strcasecmp(attrib, "localuser") == 0) { | ||
539 | if (match_pattern_list(pw->pw_name, arg, len, 0) != 1) | ||
540 | result = 0; | ||
541 | else | ||
542 | debug("%.200s line %d: matched " | ||
543 | "'LocalUser %.100s' ", | ||
544 | filename, linenum, pw->pw_name); | ||
545 | } else if (strcasecmp(attrib, "exec") == 0) { | ||
546 | if (gethostname(thishost, sizeof(thishost)) == -1) | ||
547 | fatal("gethostname: %s", strerror(errno)); | ||
548 | strlcpy(shorthost, thishost, sizeof(shorthost)); | ||
549 | shorthost[strcspn(thishost, ".")] = '\0'; | ||
550 | snprintf(portstr, sizeof(portstr), "%d", port); | ||
551 | |||
552 | cmd = percent_expand(arg, | ||
553 | "L", shorthost, | ||
554 | "d", pw->pw_dir, | ||
555 | "h", host, | ||
556 | "l", thishost, | ||
557 | "n", host_arg, | ||
558 | "p", portstr, | ||
559 | "r", ruser, | ||
560 | "u", pw->pw_name, | ||
561 | (char *)NULL); | ||
562 | r = execute_in_shell(cmd); | ||
563 | if (r == -1) { | ||
564 | fatal("%.200s line %d: match exec '%.100s' " | ||
565 | "error", filename, linenum, cmd); | ||
566 | } else if (r == 0) { | ||
567 | debug("%.200s line %d: matched " | ||
568 | "'exec \"%.100s\"' ", | ||
569 | filename, linenum, cmd); | ||
570 | } else | ||
571 | result = 0; | ||
572 | free(cmd); | ||
573 | } else { | ||
574 | error("Unsupported Match attribute %s", attrib); | ||
575 | result = -1; | ||
576 | goto out; | ||
577 | } | ||
578 | } | ||
579 | if (attributes == 0) { | ||
580 | error("One or more attributes required for Match"); | ||
581 | result = -1; | ||
582 | goto out; | ||
583 | } | ||
584 | debug3("match %sfound", result ? "" : "not "); | ||
585 | *condition = cp; | ||
586 | out: | ||
587 | free(host); | ||
588 | return result; | ||
589 | } | ||
590 | |||
591 | /* Check and prepare a domain name: removes trailing '.' and lowercases */ | ||
592 | static void | ||
593 | valid_domain(char *name, const char *filename, int linenum) | ||
594 | { | ||
595 | size_t i, l = strlen(name); | ||
596 | u_char c, last = '\0'; | ||
597 | |||
598 | if (l == 0) | ||
599 | fatal("%s line %d: empty hostname suffix", filename, linenum); | ||
600 | if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) | ||
601 | fatal("%s line %d: hostname suffix \"%.100s\" " | ||
602 | "starts with invalid character", filename, linenum, name); | ||
603 | for (i = 0; i < l; i++) { | ||
604 | c = tolower((u_char)name[i]); | ||
605 | name[i] = (char)c; | ||
606 | if (last == '.' && c == '.') | ||
607 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
608 | "consecutive separators", filename, linenum, name); | ||
609 | if (c != '.' && c != '-' && !isalnum(c) && | ||
610 | c != '_') /* technically invalid, but common */ | ||
611 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
612 | "invalid characters", filename, linenum, name); | ||
613 | last = c; | ||
614 | } | ||
615 | if (name[l - 1] == '.') | ||
616 | name[l - 1] = '\0'; | ||
617 | } | ||
371 | 618 | ||
619 | /* | ||
620 | * Returns the number of the token pointed to by cp or oBadOption. | ||
621 | */ | ||
372 | static OpCodes | 622 | static OpCodes |
373 | parse_token(const char *cp, const char *filename, int linenum, | 623 | parse_token(const char *cp, const char *filename, int linenum, |
374 | const char *ignored_unknown) | 624 | const char *ignored_unknown) |
@@ -386,25 +636,93 @@ parse_token(const char *cp, const char *filename, int linenum, | |||
386 | return oBadOption; | 636 | return oBadOption; |
387 | } | 637 | } |
388 | 638 | ||
639 | /* Multistate option parsing */ | ||
640 | struct multistate { | ||
641 | char *key; | ||
642 | int value; | ||
643 | }; | ||
644 | static const struct multistate multistate_flag[] = { | ||
645 | { "true", 1 }, | ||
646 | { "false", 0 }, | ||
647 | { "yes", 1 }, | ||
648 | { "no", 0 }, | ||
649 | { NULL, -1 } | ||
650 | }; | ||
651 | static const struct multistate multistate_yesnoask[] = { | ||
652 | { "true", 1 }, | ||
653 | { "false", 0 }, | ||
654 | { "yes", 1 }, | ||
655 | { "no", 0 }, | ||
656 | { "ask", 2 }, | ||
657 | { NULL, -1 } | ||
658 | }; | ||
659 | static const struct multistate multistate_addressfamily[] = { | ||
660 | { "inet", AF_INET }, | ||
661 | { "inet6", AF_INET6 }, | ||
662 | { "any", AF_UNSPEC }, | ||
663 | { NULL, -1 } | ||
664 | }; | ||
665 | static const struct multistate multistate_controlmaster[] = { | ||
666 | { "true", SSHCTL_MASTER_YES }, | ||
667 | { "yes", SSHCTL_MASTER_YES }, | ||
668 | { "false", SSHCTL_MASTER_NO }, | ||
669 | { "no", SSHCTL_MASTER_NO }, | ||
670 | { "auto", SSHCTL_MASTER_AUTO }, | ||
671 | { "ask", SSHCTL_MASTER_ASK }, | ||
672 | { "autoask", SSHCTL_MASTER_AUTO_ASK }, | ||
673 | { NULL, -1 } | ||
674 | }; | ||
675 | static const struct multistate multistate_tunnel[] = { | ||
676 | { "ethernet", SSH_TUNMODE_ETHERNET }, | ||
677 | { "point-to-point", SSH_TUNMODE_POINTOPOINT }, | ||
678 | { "true", SSH_TUNMODE_DEFAULT }, | ||
679 | { "yes", SSH_TUNMODE_DEFAULT }, | ||
680 | { "false", SSH_TUNMODE_NO }, | ||
681 | { "no", SSH_TUNMODE_NO }, | ||
682 | { NULL, -1 } | ||
683 | }; | ||
684 | static const struct multistate multistate_requesttty[] = { | ||
685 | { "true", REQUEST_TTY_YES }, | ||
686 | { "yes", REQUEST_TTY_YES }, | ||
687 | { "false", REQUEST_TTY_NO }, | ||
688 | { "no", REQUEST_TTY_NO }, | ||
689 | { "force", REQUEST_TTY_FORCE }, | ||
690 | { "auto", REQUEST_TTY_AUTO }, | ||
691 | { NULL, -1 } | ||
692 | }; | ||
693 | static const struct multistate multistate_canonicalizehostname[] = { | ||
694 | { "true", SSH_CANONICALISE_YES }, | ||
695 | { "false", SSH_CANONICALISE_NO }, | ||
696 | { "yes", SSH_CANONICALISE_YES }, | ||
697 | { "no", SSH_CANONICALISE_NO }, | ||
698 | { "always", SSH_CANONICALISE_ALWAYS }, | ||
699 | { NULL, -1 } | ||
700 | }; | ||
701 | |||
389 | /* | 702 | /* |
390 | * Processes a single option line as used in the configuration files. This | 703 | * Processes a single option line as used in the configuration files. This |
391 | * only sets those values that have not already been set. | 704 | * only sets those values that have not already been set. |
392 | */ | 705 | */ |
393 | #define WHITESPACE " \t\r\n" | 706 | #define WHITESPACE " \t\r\n" |
394 | |||
395 | int | 707 | int |
396 | process_config_line(Options *options, const char *host, | 708 | process_config_line(Options *options, struct passwd *pw, const char *host, |
397 | char *line, const char *filename, int linenum, | 709 | char *line, const char *filename, int linenum, int *activep, int userconfig) |
398 | int *activep, int userconfig) | ||
399 | { | 710 | { |
400 | char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; | 711 | char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; |
401 | char **cpptr, fwdarg[256]; | 712 | char **cpptr, fwdarg[256]; |
402 | u_int i, *uintptr, max_entries = 0; | 713 | u_int i, *uintptr, max_entries = 0; |
403 | int negated, opcode, *intptr, value, value2; | 714 | int negated, opcode, *intptr, value, value2, cmdline = 0; |
404 | LogLevel *log_level_ptr; | 715 | LogLevel *log_level_ptr; |
405 | long long val64; | 716 | long long val64; |
406 | size_t len; | 717 | size_t len; |
407 | Forward fwd; | 718 | Forward fwd; |
719 | const struct multistate *multistate_ptr; | ||
720 | struct allowed_cname *cname; | ||
721 | |||
722 | if (activep == NULL) { /* We are processing a command line directive */ | ||
723 | cmdline = 1; | ||
724 | activep = &cmdline; | ||
725 | } | ||
408 | 726 | ||
409 | /* Strip trailing whitespace */ | 727 | /* Strip trailing whitespace */ |
410 | for (len = strlen(line) - 1; len > 0; len--) { | 728 | for (len = strlen(line) - 1; len > 0; len--) { |
@@ -423,8 +741,7 @@ process_config_line(Options *options, const char *host, | |||
423 | if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') | 741 | if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') |
424 | return 0; | 742 | return 0; |
425 | /* Match lowercase keyword */ | 743 | /* Match lowercase keyword */ |
426 | for (i = 0; i < strlen(keyword); i++) | 744 | lowercase(keyword); |
427 | keyword[i] = tolower(keyword[i]); | ||
428 | 745 | ||
429 | opcode = parse_token(keyword, filename, linenum, | 746 | opcode = parse_token(keyword, filename, linenum, |
430 | options->ignored_unknown); | 747 | options->ignored_unknown); |
@@ -454,17 +771,23 @@ parse_time: | |||
454 | 771 | ||
455 | case oForwardAgent: | 772 | case oForwardAgent: |
456 | intptr = &options->forward_agent; | 773 | intptr = &options->forward_agent; |
457 | parse_flag: | 774 | parse_flag: |
775 | multistate_ptr = multistate_flag; | ||
776 | parse_multistate: | ||
458 | arg = strdelim(&s); | 777 | arg = strdelim(&s); |
459 | if (!arg || *arg == '\0') | 778 | if (!arg || *arg == '\0') |
460 | fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); | 779 | fatal("%s line %d: missing argument.", |
461 | value = 0; /* To avoid compiler warning... */ | 780 | filename, linenum); |
462 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | 781 | value = -1; |
463 | value = 1; | 782 | for (i = 0; multistate_ptr[i].key != NULL; i++) { |
464 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | 783 | if (strcasecmp(arg, multistate_ptr[i].key) == 0) { |
465 | value = 0; | 784 | value = multistate_ptr[i].value; |
466 | else | 785 | break; |
467 | fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); | 786 | } |
787 | } | ||
788 | if (value == -1) | ||
789 | fatal("%s line %d: unsupported option \"%s\".", | ||
790 | filename, linenum, arg); | ||
468 | if (*activep && *intptr == -1) | 791 | if (*activep && *intptr == -1) |
469 | *intptr = value; | 792 | *intptr = value; |
470 | break; | 793 | break; |
@@ -567,27 +890,13 @@ parse_flag: | |||
567 | 890 | ||
568 | case oVerifyHostKeyDNS: | 891 | case oVerifyHostKeyDNS: |
569 | intptr = &options->verify_host_key_dns; | 892 | intptr = &options->verify_host_key_dns; |
570 | goto parse_yesnoask; | 893 | multistate_ptr = multistate_yesnoask; |
894 | goto parse_multistate; | ||
571 | 895 | ||
572 | case oStrictHostKeyChecking: | 896 | case oStrictHostKeyChecking: |
573 | intptr = &options->strict_host_key_checking; | 897 | intptr = &options->strict_host_key_checking; |
574 | parse_yesnoask: | 898 | multistate_ptr = multistate_yesnoask; |
575 | arg = strdelim(&s); | 899 | goto parse_multistate; |
576 | if (!arg || *arg == '\0') | ||
577 | fatal("%.200s line %d: Missing yes/no/ask argument.", | ||
578 | filename, linenum); | ||
579 | value = 0; /* To avoid compiler warning... */ | ||
580 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | ||
581 | value = 1; | ||
582 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | ||
583 | value = 0; | ||
584 | else if (strcmp(arg, "ask") == 0) | ||
585 | value = 2; | ||
586 | else | ||
587 | fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); | ||
588 | if (*activep && *intptr == -1) | ||
589 | *intptr = value; | ||
590 | break; | ||
591 | 900 | ||
592 | case oCompression: | 901 | case oCompression: |
593 | intptr = &options->compression; | 902 | intptr = &options->compression; |
@@ -864,6 +1173,9 @@ parse_int: | |||
864 | goto parse_flag; | 1173 | goto parse_flag; |
865 | 1174 | ||
866 | case oHost: | 1175 | case oHost: |
1176 | if (cmdline) | ||
1177 | fatal("Host directive not supported as a command-line " | ||
1178 | "option"); | ||
867 | *activep = 0; | 1179 | *activep = 0; |
868 | arg2 = NULL; | 1180 | arg2 = NULL; |
869 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | 1181 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { |
@@ -890,6 +1202,18 @@ parse_int: | |||
890 | /* Avoid garbage check below, as strdelim is done. */ | 1202 | /* Avoid garbage check below, as strdelim is done. */ |
891 | return 0; | 1203 | return 0; |
892 | 1204 | ||
1205 | case oMatch: | ||
1206 | if (cmdline) | ||
1207 | fatal("Host directive not supported as a command-line " | ||
1208 | "option"); | ||
1209 | value = match_cfg_line(options, &s, pw, host, | ||
1210 | filename, linenum); | ||
1211 | if (value < 0) | ||
1212 | fatal("%.200s line %d: Bad Match condition", filename, | ||
1213 | linenum); | ||
1214 | *activep = value; | ||
1215 | break; | ||
1216 | |||
893 | case oEscapeChar: | 1217 | case oEscapeChar: |
894 | intptr = &options->escape_char; | 1218 | intptr = &options->escape_char; |
895 | arg = strdelim(&s); | 1219 | arg = strdelim(&s); |
@@ -913,22 +1237,9 @@ parse_int: | |||
913 | break; | 1237 | break; |
914 | 1238 | ||
915 | case oAddressFamily: | 1239 | case oAddressFamily: |
916 | arg = strdelim(&s); | ||
917 | if (!arg || *arg == '\0') | ||
918 | fatal("%s line %d: missing address family.", | ||
919 | filename, linenum); | ||
920 | intptr = &options->address_family; | 1240 | intptr = &options->address_family; |
921 | if (strcasecmp(arg, "inet") == 0) | 1241 | multistate_ptr = multistate_addressfamily; |
922 | value = AF_INET; | 1242 | goto parse_multistate; |
923 | else if (strcasecmp(arg, "inet6") == 0) | ||
924 | value = AF_INET6; | ||
925 | else if (strcasecmp(arg, "any") == 0) | ||
926 | value = AF_UNSPEC; | ||
927 | else | ||
928 | fatal("Unsupported AddressFamily \"%s\"", arg); | ||
929 | if (*activep && *intptr == -1) | ||
930 | *intptr = value; | ||
931 | break; | ||
932 | 1243 | ||
933 | case oEnableSSHKeysign: | 1244 | case oEnableSSHKeysign: |
934 | intptr = &options->enable_ssh_keysign; | 1245 | intptr = &options->enable_ssh_keysign; |
@@ -969,27 +1280,8 @@ parse_int: | |||
969 | 1280 | ||
970 | case oControlMaster: | 1281 | case oControlMaster: |
971 | intptr = &options->control_master; | 1282 | intptr = &options->control_master; |
972 | arg = strdelim(&s); | 1283 | multistate_ptr = multistate_controlmaster; |
973 | if (!arg || *arg == '\0') | 1284 | goto parse_multistate; |
974 | fatal("%.200s line %d: Missing ControlMaster argument.", | ||
975 | filename, linenum); | ||
976 | value = 0; /* To avoid compiler warning... */ | ||
977 | if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) | ||
978 | value = SSHCTL_MASTER_YES; | ||
979 | else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) | ||
980 | value = SSHCTL_MASTER_NO; | ||
981 | else if (strcmp(arg, "auto") == 0) | ||
982 | value = SSHCTL_MASTER_AUTO; | ||
983 | else if (strcmp(arg, "ask") == 0) | ||
984 | value = SSHCTL_MASTER_ASK; | ||
985 | else if (strcmp(arg, "autoask") == 0) | ||
986 | value = SSHCTL_MASTER_AUTO_ASK; | ||
987 | else | ||
988 | fatal("%.200s line %d: Bad ControlMaster argument.", | ||
989 | filename, linenum); | ||
990 | if (*activep && *intptr == -1) | ||
991 | *intptr = value; | ||
992 | break; | ||
993 | 1285 | ||
994 | case oControlPersist: | 1286 | case oControlPersist: |
995 | /* no/false/yes/true, or a time spec */ | 1287 | /* no/false/yes/true, or a time spec */ |
@@ -1021,25 +1313,8 @@ parse_int: | |||
1021 | 1313 | ||
1022 | case oTunnel: | 1314 | case oTunnel: |
1023 | intptr = &options->tun_open; | 1315 | intptr = &options->tun_open; |
1024 | arg = strdelim(&s); | 1316 | multistate_ptr = multistate_tunnel; |
1025 | if (!arg || *arg == '\0') | 1317 | goto parse_multistate; |
1026 | fatal("%s line %d: Missing yes/point-to-point/" | ||
1027 | "ethernet/no argument.", filename, linenum); | ||
1028 | value = 0; /* silence compiler */ | ||
1029 | if (strcasecmp(arg, "ethernet") == 0) | ||
1030 | value = SSH_TUNMODE_ETHERNET; | ||
1031 | else if (strcasecmp(arg, "point-to-point") == 0) | ||
1032 | value = SSH_TUNMODE_POINTOPOINT; | ||
1033 | else if (strcasecmp(arg, "yes") == 0) | ||
1034 | value = SSH_TUNMODE_DEFAULT; | ||
1035 | else if (strcasecmp(arg, "no") == 0) | ||
1036 | value = SSH_TUNMODE_NO; | ||
1037 | else | ||
1038 | fatal("%s line %d: Bad yes/point-to-point/ethernet/" | ||
1039 | "no argument: %s", filename, linenum, arg); | ||
1040 | if (*activep) | ||
1041 | *intptr = value; | ||
1042 | break; | ||
1043 | 1318 | ||
1044 | case oTunnelDevice: | 1319 | case oTunnelDevice: |
1045 | arg = strdelim(&s); | 1320 | arg = strdelim(&s); |
@@ -1088,29 +1363,74 @@ parse_int: | |||
1088 | goto parse_flag; | 1363 | goto parse_flag; |
1089 | 1364 | ||
1090 | case oRequestTTY: | 1365 | case oRequestTTY: |
1091 | arg = strdelim(&s); | ||
1092 | if (!arg || *arg == '\0') | ||
1093 | fatal("%s line %d: missing argument.", | ||
1094 | filename, linenum); | ||
1095 | intptr = &options->request_tty; | 1366 | intptr = &options->request_tty; |
1096 | if (strcasecmp(arg, "yes") == 0) | 1367 | multistate_ptr = multistate_requesttty; |
1097 | value = REQUEST_TTY_YES; | 1368 | goto parse_multistate; |
1098 | else if (strcasecmp(arg, "no") == 0) | ||
1099 | value = REQUEST_TTY_NO; | ||
1100 | else if (strcasecmp(arg, "force") == 0) | ||
1101 | value = REQUEST_TTY_FORCE; | ||
1102 | else if (strcasecmp(arg, "auto") == 0) | ||
1103 | value = REQUEST_TTY_AUTO; | ||
1104 | else | ||
1105 | fatal("Unsupported RequestTTY \"%s\"", arg); | ||
1106 | if (*activep && *intptr == -1) | ||
1107 | *intptr = value; | ||
1108 | break; | ||
1109 | 1369 | ||
1110 | case oIgnoreUnknown: | 1370 | case oIgnoreUnknown: |
1111 | charptr = &options->ignored_unknown; | 1371 | charptr = &options->ignored_unknown; |
1112 | goto parse_string; | 1372 | goto parse_string; |
1113 | 1373 | ||
1374 | case oProxyUseFdpass: | ||
1375 | intptr = &options->proxy_use_fdpass; | ||
1376 | goto parse_flag; | ||
1377 | |||
1378 | case oCanonicalDomains: | ||
1379 | value = options->num_canonical_domains != 0; | ||
1380 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1381 | valid_domain(arg, filename, linenum); | ||
1382 | if (!*activep || value) | ||
1383 | continue; | ||
1384 | if (options->num_canonical_domains >= MAX_CANON_DOMAINS) | ||
1385 | fatal("%s line %d: too many hostname suffixes.", | ||
1386 | filename, linenum); | ||
1387 | options->canonical_domains[ | ||
1388 | options->num_canonical_domains++] = xstrdup(arg); | ||
1389 | } | ||
1390 | break; | ||
1391 | |||
1392 | case oCanonicalizePermittedCNAMEs: | ||
1393 | value = options->num_permitted_cnames != 0; | ||
1394 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1395 | /* Either '*' for everything or 'list:list' */ | ||
1396 | if (strcmp(arg, "*") == 0) | ||
1397 | arg2 = arg; | ||
1398 | else { | ||
1399 | lowercase(arg); | ||
1400 | if ((arg2 = strchr(arg, ':')) == NULL || | ||
1401 | arg2[1] == '\0') { | ||
1402 | fatal("%s line %d: " | ||
1403 | "Invalid permitted CNAME \"%s\"", | ||
1404 | filename, linenum, arg); | ||
1405 | } | ||
1406 | *arg2 = '\0'; | ||
1407 | arg2++; | ||
1408 | } | ||
1409 | if (!*activep || value) | ||
1410 | continue; | ||
1411 | if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) | ||
1412 | fatal("%s line %d: too many permitted CNAMEs.", | ||
1413 | filename, linenum); | ||
1414 | cname = options->permitted_cnames + | ||
1415 | options->num_permitted_cnames++; | ||
1416 | cname->source_list = xstrdup(arg); | ||
1417 | cname->target_list = xstrdup(arg2); | ||
1418 | } | ||
1419 | break; | ||
1420 | |||
1421 | case oCanonicalizeHostname: | ||
1422 | intptr = &options->canonicalize_hostname; | ||
1423 | multistate_ptr = multistate_canonicalizehostname; | ||
1424 | goto parse_multistate; | ||
1425 | |||
1426 | case oCanonicalizeMaxDots: | ||
1427 | intptr = &options->canonicalize_max_dots; | ||
1428 | goto parse_int; | ||
1429 | |||
1430 | case oCanonicalizeFallbackLocal: | ||
1431 | intptr = &options->canonicalize_fallback_local; | ||
1432 | goto parse_flag; | ||
1433 | |||
1114 | case oDeprecated: | 1434 | case oDeprecated: |
1115 | debug("%s line %d: Deprecated option \"%s\"", | 1435 | debug("%s line %d: Deprecated option \"%s\"", |
1116 | filename, linenum, keyword); | 1436 | filename, linenum, keyword); |
@@ -1141,8 +1461,8 @@ parse_int: | |||
1141 | */ | 1461 | */ |
1142 | 1462 | ||
1143 | int | 1463 | int |
1144 | read_config_file(const char *filename, const char *host, Options *options, | 1464 | read_config_file(const char *filename, struct passwd *pw, const char *host, |
1145 | int flags) | 1465 | Options *options, int flags) |
1146 | { | 1466 | { |
1147 | FILE *f; | 1467 | FILE *f; |
1148 | char line[1024]; | 1468 | char line[1024]; |
@@ -1172,8 +1492,8 @@ read_config_file(const char *filename, const char *host, Options *options, | |||
1172 | while (fgets(line, sizeof(line), f)) { | 1492 | while (fgets(line, sizeof(line), f)) { |
1173 | /* Update line number counter. */ | 1493 | /* Update line number counter. */ |
1174 | linenum++; | 1494 | linenum++; |
1175 | if (process_config_line(options, host, line, filename, linenum, | 1495 | if (process_config_line(options, pw, host, line, filename, |
1176 | &active, flags & SSHCONF_USERCONF) != 0) | 1496 | linenum, &active, flags & SSHCONF_USERCONF) != 0) |
1177 | bad_options++; | 1497 | bad_options++; |
1178 | } | 1498 | } |
1179 | fclose(f); | 1499 | fclose(f); |
@@ -1276,7 +1596,13 @@ initialize_options(Options * options) | |||
1276 | options->ip_qos_interactive = -1; | 1596 | options->ip_qos_interactive = -1; |
1277 | options->ip_qos_bulk = -1; | 1597 | options->ip_qos_bulk = -1; |
1278 | options->request_tty = -1; | 1598 | options->request_tty = -1; |
1599 | options->proxy_use_fdpass = -1; | ||
1279 | options->ignored_unknown = NULL; | 1600 | options->ignored_unknown = NULL; |
1601 | options->num_canonical_domains = 0; | ||
1602 | options->num_permitted_cnames = 0; | ||
1603 | options->canonicalize_max_dots = -1; | ||
1604 | options->canonicalize_fallback_local = -1; | ||
1605 | options->canonicalize_hostname = -1; | ||
1280 | } | 1606 | } |
1281 | 1607 | ||
1282 | /* | 1608 | /* |
@@ -1370,6 +1696,8 @@ fill_default_options(Options * options) | |||
1370 | add_identity_file(options, "~/", | 1696 | add_identity_file(options, "~/", |
1371 | _PATH_SSH_CLIENT_ID_ECDSA, 0); | 1697 | _PATH_SSH_CLIENT_ID_ECDSA, 0); |
1372 | #endif | 1698 | #endif |
1699 | add_identity_file(options, "~/", | ||
1700 | _PATH_SSH_CLIENT_ID_ED25519, 0); | ||
1373 | } | 1701 | } |
1374 | } | 1702 | } |
1375 | if (options->escape_char == -1) | 1703 | if (options->escape_char == -1) |
@@ -1439,8 +1767,24 @@ fill_default_options(Options * options) | |||
1439 | options->ip_qos_bulk = IPTOS_THROUGHPUT; | 1767 | options->ip_qos_bulk = IPTOS_THROUGHPUT; |
1440 | if (options->request_tty == -1) | 1768 | if (options->request_tty == -1) |
1441 | options->request_tty = REQUEST_TTY_AUTO; | 1769 | options->request_tty = REQUEST_TTY_AUTO; |
1442 | /* options->local_command should not be set by default */ | 1770 | if (options->proxy_use_fdpass == -1) |
1443 | /* options->proxy_command should not be set by default */ | 1771 | options->proxy_use_fdpass = 0; |
1772 | if (options->canonicalize_max_dots == -1) | ||
1773 | options->canonicalize_max_dots = 1; | ||
1774 | if (options->canonicalize_fallback_local == -1) | ||
1775 | options->canonicalize_fallback_local = 1; | ||
1776 | if (options->canonicalize_hostname == -1) | ||
1777 | options->canonicalize_hostname = SSH_CANONICALISE_NO; | ||
1778 | #define CLEAR_ON_NONE(v) \ | ||
1779 | do { \ | ||
1780 | if (v != NULL && strcasecmp(v, "none") == 0) { \ | ||
1781 | free(v); \ | ||
1782 | v = NULL; \ | ||
1783 | } \ | ||
1784 | } while(0) | ||
1785 | CLEAR_ON_NONE(options->local_command); | ||
1786 | CLEAR_ON_NONE(options->proxy_command); | ||
1787 | CLEAR_ON_NONE(options->control_path); | ||
1444 | /* options->user will be set in the main program if appropriate */ | 1788 | /* options->user will be set in the main program if appropriate */ |
1445 | /* options->hostname will be set in the main program if appropriate */ | 1789 | /* options->hostname will be set in the main program if appropriate */ |
1446 | /* options->host_key_alias should not be set by default */ | 1790 | /* options->host_key_alias should not be set by default */ |
@@ -1467,7 +1811,7 @@ parse_forward(Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) | |||
1467 | cp = p = xstrdup(fwdspec); | 1811 | cp = p = xstrdup(fwdspec); |
1468 | 1812 | ||
1469 | /* skip leading spaces */ | 1813 | /* skip leading spaces */ |
1470 | while (isspace(*cp)) | 1814 | while (isspace((u_char)*cp)) |
1471 | cp++; | 1815 | cp++; |
1472 | 1816 | ||
1473 | for (i = 0; i < 4; ++i) | 1817 | for (i = 0; i < 4; ++i) |