diff options
author | Damien Miller <djm@mindrot.org> | 2013-10-17 11:47:23 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2013-10-17 11:47:23 +1100 |
commit | 0faf747e2f77f0f7083bcd59cbed30c4b5448444 (patch) | |
tree | 1f1b80f60be01d61f284070affc314d1b97b6b69 /readconf.c | |
parent | d77b81f856e078714ec6b0f86f61c20249b7ead4 (diff) |
- djm@cvs.openbsd.org 2013/10/16 02:31:47
[readconf.c readconf.h roaming_client.c ssh.1 ssh.c ssh_config.5]
[sshconnect.c sshconnect.h]
Implement client-side hostname canonicalisation to allow an explicit
search path of domain suffixes to use to convert unqualified host names
to fully-qualified ones for host key matching.
This is particularly useful for host certificates, which would otherwise
need to list unqualified names alongside fully-qualified ones (and this
causes a number of problems).
"looks fine" markus@
Diffstat (limited to 'readconf.c')
-rw-r--r-- | readconf.c | 113 |
1 files changed, 112 insertions, 1 deletions
diff --git a/readconf.c b/readconf.c index 9340effd0..de8eb7cd3 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: readconf.c,v 1.207 2013/10/14 23:28:23 djm Exp $ */ | 1 | /* $OpenBSD: readconf.c,v 1.208 2013/10/16 02:31:45 djm 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 |
@@ -144,6 +144,8 @@ typedef enum { | |||
144 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, | 144 | oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, |
145 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, | 145 | oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication, |
146 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, | 146 | oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, |
147 | oCanonicalDomains, oCanonicaliseHostname, oCanonicaliseMaxDots, | ||
148 | oCanonicaliseFallbackLocal, oCanonicalisePermittedCNAMEs, | ||
147 | oIgnoredUnknownOption, oDeprecated, oUnsupported | 149 | oIgnoredUnknownOption, oDeprecated, oUnsupported |
148 | } OpCodes; | 150 | } OpCodes; |
149 | 151 | ||
@@ -257,6 +259,11 @@ static struct { | |||
257 | { "ipqos", oIPQoS }, | 259 | { "ipqos", oIPQoS }, |
258 | { "requesttty", oRequestTTY }, | 260 | { "requesttty", oRequestTTY }, |
259 | { "proxyusefdpass", oProxyUseFdpass }, | 261 | { "proxyusefdpass", oProxyUseFdpass }, |
262 | { "canonicaldomains", oCanonicalDomains }, | ||
263 | { "canonicalisefallbacklocal", oCanonicaliseFallbackLocal }, | ||
264 | { "canonicalisehostname", oCanonicaliseHostname }, | ||
265 | { "canonicalisemaxdots", oCanonicaliseMaxDots }, | ||
266 | { "canonicalisepermittedcnames", oCanonicalisePermittedCNAMEs }, | ||
260 | { "ignoreunknown", oIgnoreUnknown }, | 267 | { "ignoreunknown", oIgnoreUnknown }, |
261 | 268 | ||
262 | { NULL, oBadOption } | 269 | { NULL, oBadOption } |
@@ -535,6 +542,34 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, | |||
535 | return result; | 542 | return result; |
536 | } | 543 | } |
537 | 544 | ||
545 | /* Check and prepare a domain name: removes trailing '.' and lowercases */ | ||
546 | static void | ||
547 | valid_domain(char *name, const char *filename, int linenum) | ||
548 | { | ||
549 | size_t i, l = strlen(name); | ||
550 | u_char c, last = '\0'; | ||
551 | |||
552 | if (l == 0) | ||
553 | fatal("%s line %d: empty hostname suffix", filename, linenum); | ||
554 | if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) | ||
555 | fatal("%s line %d: hostname suffix \"%.100s\" " | ||
556 | "starts with invalid character", filename, linenum, name); | ||
557 | for (i = 0; i < l; i++) { | ||
558 | c = tolower((u_char)name[i]); | ||
559 | name[i] = (char)c; | ||
560 | if (last == '.' && c == '.') | ||
561 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
562 | "consecutive separators", filename, linenum, name); | ||
563 | if (c != '.' && c != '-' && !isalnum(c) && | ||
564 | c != '_') /* technically invalid, but common */ | ||
565 | fatal("%s line %d: hostname suffix \"%.100s\" contains " | ||
566 | "invalid characters", filename, linenum, name); | ||
567 | last = c; | ||
568 | } | ||
569 | if (name[l - 1] == '.') | ||
570 | name[l - 1] = '\0'; | ||
571 | } | ||
572 | |||
538 | /* | 573 | /* |
539 | * Returns the number of the token pointed to by cp or oBadOption. | 574 | * Returns the number of the token pointed to by cp or oBadOption. |
540 | */ | 575 | */ |
@@ -609,6 +644,14 @@ static const struct multistate multistate_requesttty[] = { | |||
609 | { "auto", REQUEST_TTY_AUTO }, | 644 | { "auto", REQUEST_TTY_AUTO }, |
610 | { NULL, -1 } | 645 | { NULL, -1 } |
611 | }; | 646 | }; |
647 | static const struct multistate multistate_canonicalisehostname[] = { | ||
648 | { "true", SSH_CANONICALISE_YES }, | ||
649 | { "false", SSH_CANONICALISE_NO }, | ||
650 | { "yes", SSH_CANONICALISE_YES }, | ||
651 | { "no", SSH_CANONICALISE_NO }, | ||
652 | { "always", SSH_CANONICALISE_ALWAYS }, | ||
653 | { NULL, -1 } | ||
654 | }; | ||
612 | 655 | ||
613 | /* | 656 | /* |
614 | * Processes a single option line as used in the configuration files. This | 657 | * Processes a single option line as used in the configuration files. This |
@@ -628,6 +671,7 @@ process_config_line(Options *options, struct passwd *pw, const char *host, | |||
628 | size_t len; | 671 | size_t len; |
629 | Forward fwd; | 672 | Forward fwd; |
630 | const struct multistate *multistate_ptr; | 673 | const struct multistate *multistate_ptr; |
674 | struct allowed_cname *cname; | ||
631 | 675 | ||
632 | if (activep == NULL) { /* We are processing a command line directive */ | 676 | if (activep == NULL) { /* We are processing a command line directive */ |
633 | cmdline = 1; | 677 | cmdline = 1; |
@@ -1263,6 +1307,62 @@ parse_int: | |||
1263 | intptr = &options->proxy_use_fdpass; | 1307 | intptr = &options->proxy_use_fdpass; |
1264 | goto parse_flag; | 1308 | goto parse_flag; |
1265 | 1309 | ||
1310 | case oCanonicalDomains: | ||
1311 | value = options->num_canonical_domains != 0; | ||
1312 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1313 | valid_domain(arg, filename, linenum); | ||
1314 | if (!*activep || value) | ||
1315 | continue; | ||
1316 | if (options->num_canonical_domains >= MAX_CANON_DOMAINS) | ||
1317 | fatal("%s line %d: too many hostname suffixes.", | ||
1318 | filename, linenum); | ||
1319 | options->canonical_domains[ | ||
1320 | options->num_canonical_domains++] = xstrdup(arg); | ||
1321 | } | ||
1322 | break; | ||
1323 | |||
1324 | case oCanonicalisePermittedCNAMEs: | ||
1325 | value = options->num_permitted_cnames != 0; | ||
1326 | while ((arg = strdelim(&s)) != NULL && *arg != '\0') { | ||
1327 | /* Either '*' for everything or 'list:list' */ | ||
1328 | if (strcmp(arg, "*") == 0) | ||
1329 | arg2 = arg; | ||
1330 | else { | ||
1331 | lowercase(arg); | ||
1332 | if ((arg2 = strchr(arg, ':')) == NULL || | ||
1333 | arg2[1] == '\0') { | ||
1334 | fatal("%s line %d: " | ||
1335 | "Invalid permitted CNAME \"%s\"", | ||
1336 | filename, linenum, arg); | ||
1337 | } | ||
1338 | *arg2 = '\0'; | ||
1339 | arg2++; | ||
1340 | } | ||
1341 | if (!*activep || value) | ||
1342 | continue; | ||
1343 | if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) | ||
1344 | fatal("%s line %d: too many permitted CNAMEs.", | ||
1345 | filename, linenum); | ||
1346 | cname = options->permitted_cnames + | ||
1347 | options->num_permitted_cnames++; | ||
1348 | cname->source_list = xstrdup(arg); | ||
1349 | cname->target_list = xstrdup(arg2); | ||
1350 | } | ||
1351 | break; | ||
1352 | |||
1353 | case oCanonicaliseHostname: | ||
1354 | intptr = &options->canonicalise_hostname; | ||
1355 | multistate_ptr = multistate_canonicalisehostname; | ||
1356 | goto parse_multistate; | ||
1357 | |||
1358 | case oCanonicaliseMaxDots: | ||
1359 | intptr = &options->canonicalise_max_dots; | ||
1360 | goto parse_int; | ||
1361 | |||
1362 | case oCanonicaliseFallbackLocal: | ||
1363 | intptr = &options->canonicalise_fallback_local; | ||
1364 | goto parse_flag; | ||
1365 | |||
1266 | case oDeprecated: | 1366 | case oDeprecated: |
1267 | debug("%s line %d: Deprecated option \"%s\"", | 1367 | debug("%s line %d: Deprecated option \"%s\"", |
1268 | filename, linenum, keyword); | 1368 | filename, linenum, keyword); |
@@ -1426,6 +1526,11 @@ initialize_options(Options * options) | |||
1426 | options->request_tty = -1; | 1526 | options->request_tty = -1; |
1427 | options->proxy_use_fdpass = -1; | 1527 | options->proxy_use_fdpass = -1; |
1428 | options->ignored_unknown = NULL; | 1528 | options->ignored_unknown = NULL; |
1529 | options->num_canonical_domains = 0; | ||
1530 | options->num_permitted_cnames = 0; | ||
1531 | options->canonicalise_max_dots = -1; | ||
1532 | options->canonicalise_fallback_local = -1; | ||
1533 | options->canonicalise_hostname = -1; | ||
1429 | } | 1534 | } |
1430 | 1535 | ||
1431 | /* | 1536 | /* |
@@ -1579,6 +1684,12 @@ fill_default_options(Options * options) | |||
1579 | options->request_tty = REQUEST_TTY_AUTO; | 1684 | options->request_tty = REQUEST_TTY_AUTO; |
1580 | if (options->proxy_use_fdpass == -1) | 1685 | if (options->proxy_use_fdpass == -1) |
1581 | options->proxy_use_fdpass = 0; | 1686 | options->proxy_use_fdpass = 0; |
1687 | if (options->canonicalise_max_dots == -1) | ||
1688 | options->canonicalise_max_dots = 1; | ||
1689 | if (options->canonicalise_fallback_local == -1) | ||
1690 | options->canonicalise_fallback_local = 1; | ||
1691 | if (options->canonicalise_hostname == -1) | ||
1692 | options->canonicalise_hostname = SSH_CANONICALISE_NO; | ||
1582 | #define CLEAR_ON_NONE(v) \ | 1693 | #define CLEAR_ON_NONE(v) \ |
1583 | do { \ | 1694 | do { \ |
1584 | if (v != NULL && strcasecmp(v, "none") == 0) { \ | 1695 | if (v != NULL && strcasecmp(v, "none") == 0) { \ |