63
63
#include <openssl/conf.h>
64
64
#include <openssl/x509v3.h>
66
static void *v2i_NAME_CONSTRAINTS(X509V3_EXT_METHOD *method,
67
X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
68
static int i2r_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);
68
static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
69
69
void *a, BIO *bp, int ind);
70
static int do_i2r_name_constraints(X509V3_EXT_METHOD *method,
71
STACK_OF(GENERAL_SUBTREE) *trees,
72
BIO *bp, int ind, char *name);
70
static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
71
STACK_OF(GENERAL_SUBTREE) *trees,
72
BIO *bp, int ind, char *name);
73
73
static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
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);
75
82
const X509V3_EXT_METHOD v3_name_constraints = {
76
83
NID_name_constraints, 0,
77
84
ASN1_ITEM_ref(NAME_CONSTRAINTS),
227
/* Check a certificate conforms to a specified set of constraints.
229
* X509_V_OK: All constraints obeyed.
230
* X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
231
* X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
232
* X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
233
* X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
234
* X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
235
* X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
239
int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
244
nm = X509_get_subject_name(x);
246
if (X509_NAME_entry_count(nm) > 0)
249
gntmp.type = GEN_DIRNAME;
250
gntmp.d.directoryName = nm;
252
r = nc_match(&gntmp, nc);
257
gntmp.type = GEN_EMAIL;
260
/* Process any email address attributes in subject name */
265
i = X509_NAME_get_index_by_NID(nm,
266
NID_pkcs9_emailAddress,
270
ne = X509_NAME_get_entry(nm, i);
271
gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
272
if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
273
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
275
r = nc_match(&gntmp, nc);
283
for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++)
285
GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i);
286
r = nc_match(gen, nc);
295
static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
297
GENERAL_SUBTREE *sub;
300
/* Permitted subtrees: if any subtrees exist of matching the type
301
* at least one subtree must match.
304
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++)
306
sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
307
if (gen->type != sub->base->type)
309
if (sub->minimum || sub->maximum)
310
return X509_V_ERR_SUBTREE_MINMAX;
311
/* If we already have a match don't bother trying any more */
316
r = nc_match_single(gen, sub->base);
319
else if (r != X509_V_ERR_PERMITTED_VIOLATION)
324
return X509_V_ERR_PERMITTED_VIOLATION;
326
/* Excluded subtrees: must not match any of these */
328
for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++)
330
sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
331
if (gen->type != sub->base->type)
333
if (sub->minimum || sub->maximum)
334
return X509_V_ERR_SUBTREE_MINMAX;
336
r = nc_match_single(gen, sub->base);
338
return X509_V_ERR_EXCLUDED_VIOLATION;
339
else if (r != X509_V_ERR_PERMITTED_VIOLATION)
348
static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
353
return nc_dn(gen->d.directoryName, base->d.directoryName);
356
return nc_dns(gen->d.dNSName, base->d.dNSName);
359
return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
362
return nc_uri(gen->d.uniformResourceIdentifier,
363
base->d.uniformResourceIdentifier);
366
return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
371
/* directoryName name constraint matching.
372
* The canonical encoding of X509_NAME makes this comparison easy. It is
373
* matched if the subtree is a subset of the name.
376
static int nc_dn(X509_NAME *nm, X509_NAME *base)
378
/* Ensure canonical encodings are up to date. */
379
if (nm->modified && i2d_X509_NAME(nm, NULL) < 0)
380
return X509_V_ERR_OUT_OF_MEM;
381
if (base->modified && i2d_X509_NAME(base, NULL) < 0)
382
return X509_V_ERR_OUT_OF_MEM;
383
if (base->canon_enclen > nm->canon_enclen)
384
return X509_V_ERR_PERMITTED_VIOLATION;
385
if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
386
return X509_V_ERR_PERMITTED_VIOLATION;
390
static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
392
char *baseptr = (char *)base->data;
393
char *dnsptr = (char *)dns->data;
394
/* Empty matches everything */
397
/* Otherwise can add zero or more components on the left so
398
* compare RHS and if dns is longer and expect '.' as preceding
401
if (dns->length > base->length)
403
dnsptr += dns->length - base->length;
404
if (dnsptr[-1] != '.')
405
return X509_V_ERR_PERMITTED_VIOLATION;
408
if (strcasecmp(baseptr, dnsptr))
409
return X509_V_ERR_PERMITTED_VIOLATION;
415
static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
417
const char *baseptr = (char *)base->data;
418
const char *emlptr = (char *)eml->data;
420
const char *baseat = strchr(baseptr, '@');
421
const char *emlat = strchr(emlptr, '@');
423
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
424
/* Special case: inital '.' is RHS match */
425
if (!baseat && (*baseptr == '.'))
427
if (eml->length > base->length)
429
emlptr += eml->length - base->length;
430
if (!strcasecmp(baseptr, emlptr))
433
return X509_V_ERR_PERMITTED_VIOLATION;
436
/* If we have anything before '@' match local part */
440
if (baseat != baseptr)
442
if ((baseat - baseptr) != (emlat - emlptr))
443
return X509_V_ERR_PERMITTED_VIOLATION;
444
/* Case sensitive match of local part */
445
if (strncmp(baseptr, emlptr, emlat - emlptr))
446
return X509_V_ERR_PERMITTED_VIOLATION;
448
/* Position base after '@' */
449
baseptr = baseat + 1;
452
/* Just have hostname left to match: case insensitive */
453
if (strcasecmp(baseptr, emlptr))
454
return X509_V_ERR_PERMITTED_VIOLATION;
460
static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
462
const char *baseptr = (char *)base->data;
463
const char *hostptr = (char *)uri->data;
464
const char *p = strchr(hostptr, ':');
466
/* Check for foo:// and skip past it */
467
if (!p || (p[1] != '/') || (p[2] != '/'))
468
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
471
/* Determine length of hostname part of URI */
473
/* Look for a port indicator as end of hostname first */
475
p = strchr(hostptr, ':');
476
/* Otherwise look for trailing slash */
478
p = strchr(hostptr, '/');
481
hostlen = strlen(hostptr);
483
hostlen = p - hostptr;
486
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
488
/* Special case: inital '.' is RHS match */
491
if (hostlen > base->length)
493
p = hostptr + hostlen - base->length;
494
if (!strncasecmp(p, baseptr, base->length))
497
return X509_V_ERR_PERMITTED_VIOLATION;
500
if ((base->length != (int)hostlen) || strncasecmp(hostptr, baseptr, hostlen))
501
return X509_V_ERR_PERMITTED_VIOLATION;