diff options
Diffstat (limited to 'src/lib/libcrypto/x509v3/v3_ncons.c')
-rw-r--r-- | src/lib/libcrypto/x509v3/v3_ncons.c | 312 |
1 files changed, 299 insertions, 13 deletions
diff --git a/src/lib/libcrypto/x509v3/v3_ncons.c b/src/lib/libcrypto/x509v3/v3_ncons.c index 4e706be3e1..689df46acd 100644 --- a/src/lib/libcrypto/x509v3/v3_ncons.c +++ b/src/lib/libcrypto/x509v3/v3_ncons.c | |||
@@ -63,15 +63,22 @@ | |||
63 | #include <openssl/conf.h> | 63 | #include <openssl/conf.h> |
64 | #include <openssl/x509v3.h> | 64 | #include <openssl/x509v3.h> |
65 | 65 | ||
66 | static void *v2i_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | 66 | static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, |
67 | X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); | 67 | X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); |
68 | static int i2r_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | 68 | static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, |
69 | void *a, BIO *bp, int ind); | 69 | void *a, BIO *bp, int ind); |
70 | static int do_i2r_name_constraints(X509V3_EXT_METHOD *method, | 70 | static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, |
71 | STACK_OF(GENERAL_SUBTREE) *trees, | 71 | STACK_OF(GENERAL_SUBTREE) *trees, |
72 | BIO *bp, int ind, char *name); | 72 | BIO *bp, int ind, char *name); |
73 | static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip); | 73 | static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip); |
74 | 74 | ||
75 | static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc); | ||
76 | static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen); | ||
77 | static int nc_dn(X509_NAME *sub, X509_NAME *nm); | ||
78 | static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns); | ||
79 | static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml); | ||
80 | static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base); | ||
81 | |||
75 | const X509V3_EXT_METHOD v3_name_constraints = { | 82 | const X509V3_EXT_METHOD v3_name_constraints = { |
76 | NID_name_constraints, 0, | 83 | NID_name_constraints, 0, |
77 | ASN1_ITEM_ref(NAME_CONSTRAINTS), | 84 | ASN1_ITEM_ref(NAME_CONSTRAINTS), |
@@ -99,8 +106,8 @@ ASN1_SEQUENCE(NAME_CONSTRAINTS) = { | |||
99 | IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE) | 106 | IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE) |
100 | IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) | 107 | IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) |
101 | 108 | ||
102 | static void *v2i_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | 109 | static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, |
103 | X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) | 110 | X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) |
104 | { | 111 | { |
105 | int i; | 112 | int i; |
106 | CONF_VALUE tval, *val; | 113 | CONF_VALUE tval, *val; |
@@ -155,8 +162,8 @@ static void *v2i_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | |||
155 | 162 | ||
156 | 163 | ||
157 | 164 | ||
158 | static int i2r_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | 165 | static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, |
159 | void *a, BIO *bp, int ind) | 166 | BIO *bp, int ind) |
160 | { | 167 | { |
161 | NAME_CONSTRAINTS *ncons = a; | 168 | NAME_CONSTRAINTS *ncons = a; |
162 | do_i2r_name_constraints(method, ncons->permittedSubtrees, | 169 | do_i2r_name_constraints(method, ncons->permittedSubtrees, |
@@ -166,9 +173,9 @@ static int i2r_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method, | |||
166 | return 1; | 173 | return 1; |
167 | } | 174 | } |
168 | 175 | ||
169 | static int do_i2r_name_constraints(X509V3_EXT_METHOD *method, | 176 | static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method, |
170 | STACK_OF(GENERAL_SUBTREE) *trees, | 177 | STACK_OF(GENERAL_SUBTREE) *trees, |
171 | BIO *bp, int ind, char *name) | 178 | BIO *bp, int ind, char *name) |
172 | { | 179 | { |
173 | GENERAL_SUBTREE *tree; | 180 | GENERAL_SUBTREE *tree; |
174 | int i; | 181 | int i; |
@@ -218,3 +225,282 @@ static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) | |||
218 | return 1; | 225 | return 1; |
219 | } | 226 | } |
220 | 227 | ||
228 | /* Check a certificate conforms to a specified set of constraints. | ||
229 | * Return values: | ||
230 | * X509_V_OK: All constraints obeyed. | ||
231 | * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation. | ||
232 | * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation. | ||
233 | * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type. | ||
234 | * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type. | ||
235 | * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax. | ||
236 | * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name | ||
237 | |||
238 | */ | ||
239 | |||
240 | int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc) | ||
241 | { | ||
242 | int r, i; | ||
243 | X509_NAME *nm; | ||
244 | |||
245 | nm = X509_get_subject_name(x); | ||
246 | |||
247 | if (X509_NAME_entry_count(nm) > 0) | ||
248 | { | ||
249 | GENERAL_NAME gntmp; | ||
250 | gntmp.type = GEN_DIRNAME; | ||
251 | gntmp.d.directoryName = nm; | ||
252 | |||
253 | r = nc_match(&gntmp, nc); | ||
254 | |||
255 | if (r != X509_V_OK) | ||
256 | return r; | ||
257 | |||
258 | gntmp.type = GEN_EMAIL; | ||
259 | |||
260 | |||
261 | /* Process any email address attributes in subject name */ | ||
262 | |||
263 | for (i = -1;;) | ||
264 | { | ||
265 | X509_NAME_ENTRY *ne; | ||
266 | i = X509_NAME_get_index_by_NID(nm, | ||
267 | NID_pkcs9_emailAddress, | ||
268 | i); | ||
269 | if (i == -1) | ||
270 | break; | ||
271 | ne = X509_NAME_get_entry(nm, i); | ||
272 | gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne); | ||
273 | if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) | ||
274 | return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | ||
275 | |||
276 | r = nc_match(&gntmp, nc); | ||
277 | |||
278 | if (r != X509_V_OK) | ||
279 | return r; | ||
280 | } | ||
281 | |||
282 | } | ||
283 | |||
284 | for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) | ||
285 | { | ||
286 | GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i); | ||
287 | r = nc_match(gen, nc); | ||
288 | if (r != X509_V_OK) | ||
289 | return r; | ||
290 | } | ||
291 | |||
292 | return X509_V_OK; | ||
293 | |||
294 | } | ||
295 | |||
296 | static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) | ||
297 | { | ||
298 | GENERAL_SUBTREE *sub; | ||
299 | int i, r, match = 0; | ||
300 | |||
301 | /* Permitted subtrees: if any subtrees exist of matching the type | ||
302 | * at least one subtree must match. | ||
303 | */ | ||
304 | |||
305 | for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) | ||
306 | { | ||
307 | sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); | ||
308 | if (gen->type != sub->base->type) | ||
309 | continue; | ||
310 | if (sub->minimum || sub->maximum) | ||
311 | return X509_V_ERR_SUBTREE_MINMAX; | ||
312 | /* If we already have a match don't bother trying any more */ | ||
313 | if (match == 2) | ||
314 | continue; | ||
315 | if (match == 0) | ||
316 | match = 1; | ||
317 | r = nc_match_single(gen, sub->base); | ||
318 | if (r == X509_V_OK) | ||
319 | match = 2; | ||
320 | else if (r != X509_V_ERR_PERMITTED_VIOLATION) | ||
321 | return r; | ||
322 | } | ||
323 | |||
324 | if (match == 1) | ||
325 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
326 | |||
327 | /* Excluded subtrees: must not match any of these */ | ||
328 | |||
329 | for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) | ||
330 | { | ||
331 | sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); | ||
332 | if (gen->type != sub->base->type) | ||
333 | continue; | ||
334 | if (sub->minimum || sub->maximum) | ||
335 | return X509_V_ERR_SUBTREE_MINMAX; | ||
336 | |||
337 | r = nc_match_single(gen, sub->base); | ||
338 | if (r == X509_V_OK) | ||
339 | return X509_V_ERR_EXCLUDED_VIOLATION; | ||
340 | else if (r != X509_V_ERR_PERMITTED_VIOLATION) | ||
341 | return r; | ||
342 | |||
343 | } | ||
344 | |||
345 | return X509_V_OK; | ||
346 | |||
347 | } | ||
348 | |||
349 | static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) | ||
350 | { | ||
351 | switch(base->type) | ||
352 | { | ||
353 | case GEN_DIRNAME: | ||
354 | return nc_dn(gen->d.directoryName, base->d.directoryName); | ||
355 | |||
356 | case GEN_DNS: | ||
357 | return nc_dns(gen->d.dNSName, base->d.dNSName); | ||
358 | |||
359 | case GEN_EMAIL: | ||
360 | return nc_email(gen->d.rfc822Name, base->d.rfc822Name); | ||
361 | |||
362 | case GEN_URI: | ||
363 | return nc_uri(gen->d.uniformResourceIdentifier, | ||
364 | base->d.uniformResourceIdentifier); | ||
365 | |||
366 | default: | ||
367 | return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; | ||
368 | } | ||
369 | |||
370 | } | ||
371 | |||
372 | /* directoryName name constraint matching. | ||
373 | * The canonical encoding of X509_NAME makes this comparison easy. It is | ||
374 | * matched if the subtree is a subset of the name. | ||
375 | */ | ||
376 | |||
377 | static int nc_dn(X509_NAME *nm, X509_NAME *base) | ||
378 | { | ||
379 | /* Ensure canonical encodings are up to date. */ | ||
380 | if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) | ||
381 | return X509_V_ERR_OUT_OF_MEM; | ||
382 | if (base->modified && i2d_X509_NAME(base, NULL) < 0) | ||
383 | return X509_V_ERR_OUT_OF_MEM; | ||
384 | if (base->canon_enclen > nm->canon_enclen) | ||
385 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
386 | if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) | ||
387 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
388 | return X509_V_OK; | ||
389 | } | ||
390 | |||
391 | static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) | ||
392 | { | ||
393 | char *baseptr = (char *)base->data; | ||
394 | char *dnsptr = (char *)dns->data; | ||
395 | /* Empty matches everything */ | ||
396 | if (!*baseptr) | ||
397 | return X509_V_OK; | ||
398 | /* Otherwise can add zero or more components on the left so | ||
399 | * compare RHS and if dns is longer and expect '.' as preceding | ||
400 | * character. | ||
401 | */ | ||
402 | if (dns->length > base->length) | ||
403 | { | ||
404 | dnsptr += dns->length - base->length; | ||
405 | if (dnsptr[-1] != '.') | ||
406 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
407 | } | ||
408 | |||
409 | if (strcasecmp(baseptr, dnsptr)) | ||
410 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
411 | |||
412 | return X509_V_OK; | ||
413 | |||
414 | } | ||
415 | |||
416 | static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) | ||
417 | { | ||
418 | const char *baseptr = (char *)base->data; | ||
419 | const char *emlptr = (char *)eml->data; | ||
420 | |||
421 | const char *baseat = strchr(baseptr, '@'); | ||
422 | const char *emlat = strchr(emlptr, '@'); | ||
423 | if (!emlat) | ||
424 | return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | ||
425 | /* Special case: inital '.' is RHS match */ | ||
426 | if (!baseat && (*baseptr == '.')) | ||
427 | { | ||
428 | if (eml->length > base->length) | ||
429 | { | ||
430 | emlptr += eml->length - base->length; | ||
431 | if (!strcasecmp(baseptr, emlptr)) | ||
432 | return X509_V_OK; | ||
433 | } | ||
434 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
435 | } | ||
436 | |||
437 | /* If we have anything before '@' match local part */ | ||
438 | |||
439 | if (baseat) | ||
440 | { | ||
441 | if (baseat != baseptr) | ||
442 | { | ||
443 | if ((baseat - baseptr) != (emlat - emlptr)) | ||
444 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
445 | /* Case sensitive match of local part */ | ||
446 | if (strncmp(baseptr, emlptr, emlat - emlptr)) | ||
447 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
448 | } | ||
449 | /* Position base after '@' */ | ||
450 | baseptr = baseat + 1; | ||
451 | } | ||
452 | emlptr = emlat + 1; | ||
453 | /* Just have hostname left to match: case insensitive */ | ||
454 | if (strcasecmp(baseptr, emlptr)) | ||
455 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
456 | |||
457 | return X509_V_OK; | ||
458 | |||
459 | } | ||
460 | |||
461 | static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) | ||
462 | { | ||
463 | const char *baseptr = (char *)base->data; | ||
464 | const char *hostptr = (char *)uri->data; | ||
465 | const char *p = strchr(hostptr, ':'); | ||
466 | int hostlen; | ||
467 | /* Check for foo:// and skip past it */ | ||
468 | if (!p || (p[1] != '/') || (p[2] != '/')) | ||
469 | return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | ||
470 | hostptr = p + 3; | ||
471 | |||
472 | /* Determine length of hostname part of URI */ | ||
473 | |||
474 | /* Look for a port indicator as end of hostname first */ | ||
475 | |||
476 | p = strchr(hostptr, ':'); | ||
477 | /* Otherwise look for trailing slash */ | ||
478 | if (!p) | ||
479 | p = strchr(hostptr, '/'); | ||
480 | |||
481 | if (!p) | ||
482 | hostlen = strlen(hostptr); | ||
483 | else | ||
484 | hostlen = p - hostptr; | ||
485 | |||
486 | if (hostlen == 0) | ||
487 | return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | ||
488 | |||
489 | /* Special case: inital '.' is RHS match */ | ||
490 | if (*baseptr == '.') | ||
491 | { | ||
492 | if (hostlen > base->length) | ||
493 | { | ||
494 | p = hostptr + hostlen - base->length; | ||
495 | if (!strncasecmp(p, baseptr, base->length)) | ||
496 | return X509_V_OK; | ||
497 | } | ||
498 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
499 | } | ||
500 | |||
501 | if ((base->length != (int)hostlen) || strncasecmp(hostptr, baseptr, hostlen)) | ||
502 | return X509_V_ERR_PERMITTED_VIOLATION; | ||
503 | |||
504 | return X509_V_OK; | ||
505 | |||
506 | } | ||