diff options
author | beck <> | 2021-04-27 03:35:29 +0000 |
---|---|---|
committer | beck <> | 2021-04-27 03:35:29 +0000 |
commit | 05d12eaff3684c531b3d36e69fa663830294b6bd (patch) | |
tree | 4a1f8797d9eb5049de6166919597895e05f86d6c | |
parent | ebe128ca73ce7d178a186b93684c8bf8577f3b80 (diff) | |
download | openbsd-05d12eaff3684c531b3d36e69fa663830294b6bd.tar.gz openbsd-05d12eaff3684c531b3d36e69fa663830294b6bd.tar.bz2 openbsd-05d12eaff3684c531b3d36e69fa663830294b6bd.zip |
Relax SAN DNSname validation and constraints to permit non leading *
wildcards. While we may choose not to support them the standards
appear to permit them optionally so we can't declare a certificate
containing them invalid. Noticed by jeremy@, and Steffan Ulrich
and others. Modify the regression tests to test these cases and
not check the SAN DNSnames as "hostnames" anymore (which don't support
wildcards).
ok jsing@, tb@
-rw-r--r-- | src/lib/libcrypto/x509/x509_constraints.c | 40 | ||||
-rw-r--r-- | src/regress/lib/libcrypto/x509/constraints.c | 12 |
2 files changed, 25 insertions, 27 deletions
diff --git a/src/lib/libcrypto/x509/x509_constraints.c b/src/lib/libcrypto/x509/x509_constraints.c index 5fbcef304f..fade58c620 100644 --- a/src/lib/libcrypto/x509/x509_constraints.c +++ b/src/lib/libcrypto/x509/x509_constraints.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: x509_constraints.c,v 1.15 2021/03/12 15:57:30 tb Exp $ */ | 1 | /* $OpenBSD: x509_constraints.c,v 1.16 2021/04/27 03:35:29 beck Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2020 Bob Beck <beck@openbsd.org> | 3 | * Copyright (c) 2020 Bob Beck <beck@openbsd.org> |
4 | * | 4 | * |
@@ -169,13 +169,15 @@ x509_constraints_names_dup(struct x509_constraints_names *names) | |||
169 | /* | 169 | /* |
170 | * Validate that the name contains only a hostname consisting of RFC | 170 | * Validate that the name contains only a hostname consisting of RFC |
171 | * 5890 compliant A-labels (see RFC 6066 section 3). This is more | 171 | * 5890 compliant A-labels (see RFC 6066 section 3). This is more |
172 | * permissive to allow for a leading '*' for a SAN DNSname wildcard, | 172 | * permissive to allow for a leading '.' for a subdomain based |
173 | * or a leading '.' for a subdomain based constraint, as well as | 173 | * constraint, as well as allowing for '_' which is commonly accepted |
174 | * allowing for '_' which is commonly accepted by nonconformant | 174 | * by nonconformant DNS implementaitons. |
175 | * DNS implementaitons. | 175 | * |
176 | * if "wildcards" is set it allows '*' to occur in the string at the end of a | ||
177 | * component. | ||
176 | */ | 178 | */ |
177 | static int | 179 | static int |
178 | x509_constraints_valid_domain_internal(uint8_t *name, size_t len) | 180 | x509_constraints_valid_domain_internal(uint8_t *name, size_t len, int wildcards) |
179 | { | 181 | { |
180 | uint8_t prev, c = 0; | 182 | uint8_t prev, c = 0; |
181 | int component = 0; | 183 | int component = 0; |
@@ -198,8 +200,8 @@ x509_constraints_valid_domain_internal(uint8_t *name, size_t len) | |||
198 | if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*') | 200 | if (!isalnum(c) && c != '-' && c != '.' && c != '_' && c != '*') |
199 | return 0; | 201 | return 0; |
200 | 202 | ||
201 | /* '*' can only be the first thing. */ | 203 | /* if it is a '*', fail if not wildcards */ |
202 | if (c == '*' && !first) | 204 | if (!wildcards && c == '*') |
203 | return 0; | 205 | return 0; |
204 | 206 | ||
205 | /* '-' must not start a component or be at the end. */ | 207 | /* '-' must not start a component or be at the end. */ |
@@ -221,6 +223,13 @@ x509_constraints_valid_domain_internal(uint8_t *name, size_t len) | |||
221 | component = 0; | 223 | component = 0; |
222 | continue; | 224 | continue; |
223 | } | 225 | } |
226 | /* | ||
227 | * Wildcards can only occur at the end of a component. | ||
228 | * c*.com is valid, c*c.com is not. | ||
229 | */ | ||
230 | if (prev == '*') | ||
231 | return 0; | ||
232 | |||
224 | /* Components must be 63 chars or less. */ | 233 | /* Components must be 63 chars or less. */ |
225 | if (++component > 63) | 234 | if (++component > 63) |
226 | return 0; | 235 | return 0; |
@@ -233,15 +242,13 @@ x509_constraints_valid_domain(uint8_t *name, size_t len) | |||
233 | { | 242 | { |
234 | if (len == 0) | 243 | if (len == 0) |
235 | return 0; | 244 | return 0; |
236 | if (name[0] == '*') /* wildcard not allowed in a domain name */ | ||
237 | return 0; | ||
238 | /* | 245 | /* |
239 | * A domain may not be less than two characters, so you can't | 246 | * A domain may not be less than two characters, so you can't |
240 | * have a require subdomain name with less than that. | 247 | * have a require subdomain name with less than that. |
241 | */ | 248 | */ |
242 | if (len < 3 && name[0] == '.') | 249 | if (len < 3 && name[0] == '.') |
243 | return 0; | 250 | return 0; |
244 | return x509_constraints_valid_domain_internal(name, len); | 251 | return x509_constraints_valid_domain_internal(name, len, 0); |
245 | } | 252 | } |
246 | 253 | ||
247 | int | 254 | int |
@@ -252,15 +259,13 @@ x509_constraints_valid_host(uint8_t *name, size_t len) | |||
252 | 259 | ||
253 | if (len == 0) | 260 | if (len == 0) |
254 | return 0; | 261 | return 0; |
255 | if (name[0] == '*') /* wildcard not allowed in a host name */ | ||
256 | return 0; | ||
257 | if (name[0] == '.') /* leading . not allowed in a host name*/ | 262 | if (name[0] == '.') /* leading . not allowed in a host name*/ |
258 | return 0; | 263 | return 0; |
259 | if (inet_pton(AF_INET, name, &sin4) == 1) | 264 | if (inet_pton(AF_INET, name, &sin4) == 1) |
260 | return 0; | 265 | return 0; |
261 | if (inet_pton(AF_INET6, name, &sin6) == 1) | 266 | if (inet_pton(AF_INET6, name, &sin6) == 1) |
262 | return 0; | 267 | return 0; |
263 | return x509_constraints_valid_domain_internal(name, len); | 268 | return x509_constraints_valid_domain_internal(name, len, 0); |
264 | } | 269 | } |
265 | 270 | ||
266 | int | 271 | int |
@@ -283,7 +288,7 @@ x509_constraints_valid_sandns(uint8_t *name, size_t len) | |||
283 | if (len >= 4 && name[0] == '*' && name[1] != '.') | 288 | if (len >= 4 && name[0] == '*' && name[1] != '.') |
284 | return 0; | 289 | return 0; |
285 | 290 | ||
286 | return x509_constraints_valid_domain_internal(name, len); | 291 | return x509_constraints_valid_domain_internal(name, len, 1); |
287 | } | 292 | } |
288 | 293 | ||
289 | static inline int | 294 | static inline int |
@@ -431,16 +436,13 @@ x509_constraints_valid_domain_constraint(uint8_t *constraint, size_t len) | |||
431 | if (len == 0) | 436 | if (len == 0) |
432 | return 1; /* empty constraints match */ | 437 | return 1; /* empty constraints match */ |
433 | 438 | ||
434 | if (constraint[0] == '*') /* wildcard not allowed in a constraint */ | ||
435 | return 0; | ||
436 | |||
437 | /* | 439 | /* |
438 | * A domain may not be less than two characters, so you | 440 | * A domain may not be less than two characters, so you |
439 | * can't match a single domain of less than that | 441 | * can't match a single domain of less than that |
440 | */ | 442 | */ |
441 | if (len < 3 && constraint[0] == '.') | 443 | if (len < 3 && constraint[0] == '.') |
442 | return 0; | 444 | return 0; |
443 | return x509_constraints_valid_domain_internal(constraint, len); | 445 | return x509_constraints_valid_domain_internal(constraint, len, 0); |
444 | } | 446 | } |
445 | 447 | ||
446 | /* | 448 | /* |
diff --git a/src/regress/lib/libcrypto/x509/constraints.c b/src/regress/lib/libcrypto/x509/constraints.c index 7eef55d591..c4dedeb1fa 100644 --- a/src/regress/lib/libcrypto/x509/constraints.c +++ b/src/regress/lib/libcrypto/x509/constraints.c | |||
@@ -49,6 +49,8 @@ unsigned char *valid_hostnames[] = { | |||
49 | unsigned char *valid_sandns_names[] = { | 49 | unsigned char *valid_sandns_names[] = { |
50 | "*.ca", | 50 | "*.ca", |
51 | "*.op3nbsd.org", | 51 | "*.op3nbsd.org", |
52 | "c*.openbsd.org", | ||
53 | "foo.*.d*.c*.openbsd.org", | ||
52 | NULL, | 54 | NULL, |
53 | }; | 55 | }; |
54 | 56 | ||
@@ -96,6 +98,7 @@ unsigned char *invalid_hostnames[] = { | |||
96 | "openbsd\n.org", | 98 | "openbsd\n.org", |
97 | "open\178bsd.org", | 99 | "open\178bsd.org", |
98 | "open\255bsd.org", | 100 | "open\255bsd.org", |
101 | "*.openbsd.org", | ||
99 | NULL, | 102 | NULL, |
100 | }; | 103 | }; |
101 | 104 | ||
@@ -110,10 +113,10 @@ unsigned char *invalid_sandns_names[] = { | |||
110 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." | 113 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." |
111 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", | 114 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", |
112 | "*.-p3nbsd.org", | 115 | "*.-p3nbsd.org", |
113 | "a*.openbsd.org", | ||
114 | "*.*..openbsd.org", | 116 | "*.*..openbsd.org", |
115 | "*..openbsd.org", | 117 | "*..openbsd.org", |
116 | ".openbsd.org", | 118 | ".openbsd.org", |
119 | "c*c.openbsd.org", | ||
117 | NULL, | 120 | NULL, |
118 | }; | 121 | }; |
119 | 122 | ||
@@ -254,13 +257,6 @@ test_invalid_hostnames(void) | |||
254 | failure = 1; | 257 | failure = 1; |
255 | goto done; | 258 | goto done; |
256 | } | 259 | } |
257 | if (x509_constraints_valid_sandns(invalid_hostnames[i], | ||
258 | strlen(invalid_hostnames[i]))) { | ||
259 | FAIL("Invalid sandns '%s' accepted\n", | ||
260 | invalid_hostnames[i]); | ||
261 | failure = 1; | ||
262 | goto done; | ||
263 | } | ||
264 | } | 260 | } |
265 | if (x509_constraints_valid_host(nulhost, | 261 | if (x509_constraints_valid_host(nulhost, |
266 | strlen(nulhost) + 1)) { | 262 | strlen(nulhost) + 1)) { |