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 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
116typedef enum { 123typedef 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
368int
369default_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 */
385static int
386execute_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 */
458static int
459match_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 */
575static void
576valid_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 */
355static OpCodes 605static OpCodes
356parse_token(const char *cp, const char *filename, int linenum, 606parse_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 */
623struct multistate {
624 char *key;
625 int value;
626};
627static const struct multistate multistate_flag[] = {
628 { "true", 1 },
629 { "false", 0 },
630 { "yes", 1 },
631 { "no", 0 },
632 { NULL, -1 }
633};
634static const struct multistate multistate_yesnoask[] = {
635 { "true", 1 },
636 { "false", 0 },
637 { "yes", 1 },
638 { "no", 0 },
639 { "ask", 2 },
640 { NULL, -1 }
641};
642static const struct multistate multistate_addressfamily[] = {
643 { "inet", AF_INET },
644 { "inet6", AF_INET6 },
645 { "any", AF_UNSPEC },
646 { NULL, -1 }
647};
648static 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};
658static 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};
667static 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};
676static 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
378int 690int
379process_config_line(Options *options, const char *host, 691process_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;
440parse_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;
537parse_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
1104int 1424int
1105read_config_file(const char *filename, const char *host, Options *options, 1425read_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)