summaryrefslogtreecommitdiff
path: root/readconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'readconf.c')
-rw-r--r--readconf.c578
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
118typedef enum { 125typedef 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
385int
386default_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 */
402static int
403execute_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 */
475static int
476match_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 */
592static void
593valid_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 */
372static OpCodes 622static OpCodes
373parse_token(const char *cp, const char *filename, int linenum, 623parse_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 */
640struct multistate {
641 char *key;
642 int value;
643};
644static const struct multistate multistate_flag[] = {
645 { "true", 1 },
646 { "false", 0 },
647 { "yes", 1 },
648 { "no", 0 },
649 { NULL, -1 }
650};
651static const struct multistate multistate_yesnoask[] = {
652 { "true", 1 },
653 { "false", 0 },
654 { "yes", 1 },
655 { "no", 0 },
656 { "ask", 2 },
657 { NULL, -1 }
658};
659static const struct multistate multistate_addressfamily[] = {
660 { "inet", AF_INET },
661 { "inet6", AF_INET6 },
662 { "any", AF_UNSPEC },
663 { NULL, -1 }
664};
665static 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};
675static 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};
684static 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};
693static 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
395int 707int
396process_config_line(Options *options, const char *host, 708process_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;
457parse_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;
574parse_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
1143int 1463int
1144read_config_file(const char *filename, const char *host, Options *options, 1464read_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)