2 * Copyright 2003-2021 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
10 #include "internal/cryptlib.h"
11 #include "internal/numbers.h"
12 #include "internal/safe_math.h"
14 #include "crypto/asn1.h"
15 #include <openssl/asn1t.h>
16 #include <openssl/conf.h>
17 #include <openssl/x509v3.h>
18 #include <openssl/bn.h>
20 #include "crypto/x509.h"
21 #include "crypto/punycode.h"
24 OSSL_SAFE_MATH_SIGNED(int, int)
26 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
,
28 STACK_OF(CONF_VALUE
) *nval
);
29 static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
, void *a
,
31 static int do_i2r_name_constraints(const X509V3_EXT_METHOD
*method
,
32 STACK_OF(GENERAL_SUBTREE
) *trees
, BIO
*bp
,
33 int ind
, const char *name
);
34 static int print_nc_ipadd(BIO
*bp
, ASN1_OCTET_STRING
*ip
);
36 static int nc_match(GENERAL_NAME
*gen
, NAME_CONSTRAINTS
*nc
);
37 static int nc_match_single(GENERAL_NAME
*sub
, GENERAL_NAME
*gen
);
38 static int nc_dn(const X509_NAME
*sub
, const X509_NAME
*nm
);
39 static int nc_dns(ASN1_IA5STRING
*sub
, ASN1_IA5STRING
*dns
);
40 static int nc_email(ASN1_IA5STRING
*sub
, ASN1_IA5STRING
*eml
);
41 static int nc_email_eai(ASN1_TYPE
*emltype
, ASN1_IA5STRING
*base
);
42 static int nc_uri(ASN1_IA5STRING
*uri
, ASN1_IA5STRING
*base
);
43 static int nc_ip(ASN1_OCTET_STRING
*ip
, ASN1_OCTET_STRING
*base
);
45 const X509V3_EXT_METHOD ossl_v3_name_constraints
= {
46 NID_name_constraints
, 0,
47 ASN1_ITEM_ref(NAME_CONSTRAINTS
),
50 0, v2i_NAME_CONSTRAINTS
,
51 i2r_NAME_CONSTRAINTS
, 0,
55 ASN1_SEQUENCE(GENERAL_SUBTREE
) = {
56 ASN1_SIMPLE(GENERAL_SUBTREE
, base
, GENERAL_NAME
),
57 ASN1_IMP_OPT(GENERAL_SUBTREE
, minimum
, ASN1_INTEGER
, 0),
58 ASN1_IMP_OPT(GENERAL_SUBTREE
, maximum
, ASN1_INTEGER
, 1)
59 } ASN1_SEQUENCE_END(GENERAL_SUBTREE
)
61 ASN1_SEQUENCE(NAME_CONSTRAINTS
) = {
62 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS
, permittedSubtrees
,
64 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS
, excludedSubtrees
,
66 } ASN1_SEQUENCE_END(NAME_CONSTRAINTS
)
69 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE
)
70 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS
)
73 #define IA5_OFFSET_LEN(ia5base, offset) \
74 ((ia5base)->length - ((unsigned char *)(offset) - (ia5base)->data))
76 /* Like memchr but for ASN1_IA5STRING. Additionally you can specify the
77 * starting point to search from
79 # define ia5memchr(str, start, c) memchr(start, c, IA5_OFFSET_LEN(str, start))
81 /* Like memrrchr but for ASN1_IA5STRING */
82 static char *ia5memrchr(ASN1_IA5STRING
*str
, int c
)
86 for (i
= str
->length
; i
> 0 && str
->data
[i
- 1] != c
; i
--);
91 return (char *)&str
->data
[i
- 1];
95 * We cannot use strncasecmp here because that applies locale specific rules. It
96 * also doesn't work with ASN1_STRINGs that may have embedded NUL characters.
97 * For example in Turkish 'I' is not the uppercase character for 'i'. We need to
98 * do a simple ASCII case comparison ignoring the locale (that is why we use
99 * numeric constants below).
101 static int ia5ncasecmp(const char *s1
, const char *s2
, size_t n
)
103 for (; n
> 0; n
--, s1
++, s2
++) {
105 unsigned char c1
= (unsigned char)*s1
, c2
= (unsigned char)*s2
;
107 /* Convert to lower case */
108 if (c1
>= 0x41 /* A */ && c1
<= 0x5A /* Z */)
110 if (c2
>= 0x41 /* A */ && c2
<= 0x5A /* Z */)
127 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
,
128 X509V3_CTX
*ctx
, STACK_OF(CONF_VALUE
) *nval
)
131 CONF_VALUE tval
, *val
;
132 STACK_OF(GENERAL_SUBTREE
) **ptree
= NULL
;
133 NAME_CONSTRAINTS
*ncons
= NULL
;
134 GENERAL_SUBTREE
*sub
= NULL
;
136 ncons
= NAME_CONSTRAINTS_new();
139 for (i
= 0; i
< sk_CONF_VALUE_num(nval
); i
++) {
140 val
= sk_CONF_VALUE_value(nval
, i
);
141 if (HAS_PREFIX(val
->name
, "permitted") && val
->name
[9]) {
142 ptree
= &ncons
->permittedSubtrees
;
143 tval
.name
= val
->name
+ 10;
144 } else if (HAS_PREFIX(val
->name
, "excluded") && val
->name
[8]) {
145 ptree
= &ncons
->excludedSubtrees
;
146 tval
.name
= val
->name
+ 9;
148 ERR_raise(ERR_LIB_X509V3
, X509V3_R_INVALID_SYNTAX
);
151 tval
.value
= val
->value
;
152 sub
= GENERAL_SUBTREE_new();
155 if (!v2i_GENERAL_NAME_ex(sub
->base
, method
, ctx
, &tval
, 1))
158 *ptree
= sk_GENERAL_SUBTREE_new_null();
159 if (*ptree
== NULL
|| !sk_GENERAL_SUBTREE_push(*ptree
, sub
))
167 ERR_raise(ERR_LIB_X509V3
, ERR_R_MALLOC_FAILURE
);
169 NAME_CONSTRAINTS_free(ncons
);
170 GENERAL_SUBTREE_free(sub
);
175 static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
, void *a
,
178 NAME_CONSTRAINTS
*ncons
= a
;
179 do_i2r_name_constraints(method
, ncons
->permittedSubtrees
,
180 bp
, ind
, "Permitted");
181 if (ncons
->permittedSubtrees
&& ncons
->excludedSubtrees
)
183 do_i2r_name_constraints(method
, ncons
->excludedSubtrees
,
184 bp
, ind
, "Excluded");
188 static int do_i2r_name_constraints(const X509V3_EXT_METHOD
*method
,
189 STACK_OF(GENERAL_SUBTREE
) *trees
,
190 BIO
*bp
, int ind
, const char *name
)
192 GENERAL_SUBTREE
*tree
;
194 if (sk_GENERAL_SUBTREE_num(trees
) > 0)
195 BIO_printf(bp
, "%*s%s:\n", ind
, "", name
);
196 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(trees
); i
++) {
199 tree
= sk_GENERAL_SUBTREE_value(trees
, i
);
200 BIO_printf(bp
, "%*s", ind
+ 2, "");
201 if (tree
->base
->type
== GEN_IPADD
)
202 print_nc_ipadd(bp
, tree
->base
->d
.ip
);
204 GENERAL_NAME_print(bp
, tree
->base
);
209 static int print_nc_ipadd(BIO
*bp
, ASN1_OCTET_STRING
*ip
)
211 /* ip->length should be 8 or 32 and len1 == len2 == 4 or len1 == len2 == 16 */
212 int len1
= ip
->length
>= 16 ? 16 : ip
->length
>= 4 ? 4 : ip
->length
;
213 int len2
= ip
->length
- len1
;
214 char *ip1
= ossl_ipaddr_to_asc(ip
->data
, len1
);
215 char *ip2
= ossl_ipaddr_to_asc(ip
->data
+ len1
, len2
);
216 int ret
= ip1
!= NULL
&& ip2
!= NULL
217 && BIO_printf(bp
, "IP:%s/%s", ip1
, ip2
) > 0;
224 #define NAME_CHECK_MAX (1 << 20)
226 static int add_lengths(int *out
, int a
, int b
)
230 /* sk_FOO_num(NULL) returns -1 but is effectively 0 when iterating. */
236 *out
= safe_add_int(a
, b
, &err
);
241 * Check a certificate conforms to a specified set of constraints.
243 * X509_V_OK: All constraints obeyed.
244 * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
245 * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
246 * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
247 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
248 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
249 * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
252 int NAME_CONSTRAINTS_check(X509
*x
, NAME_CONSTRAINTS
*nc
)
254 int r
, i
, name_count
, constraint_count
;
257 nm
= X509_get_subject_name(x
);
260 * Guard against certificates with an excessive number of names or
261 * constraints causing a computationally expensive name constraints check.
263 if (!add_lengths(&name_count
, X509_NAME_entry_count(nm
),
264 sk_GENERAL_NAME_num(x
->altname
))
265 || !add_lengths(&constraint_count
,
266 sk_GENERAL_SUBTREE_num(nc
->permittedSubtrees
),
267 sk_GENERAL_SUBTREE_num(nc
->excludedSubtrees
))
268 || (name_count
> 0 && constraint_count
> NAME_CHECK_MAX
/ name_count
))
269 return X509_V_ERR_UNSPECIFIED
;
271 if (X509_NAME_entry_count(nm
) > 0) {
273 gntmp
.type
= GEN_DIRNAME
;
274 gntmp
.d
.directoryName
= nm
;
276 r
= nc_match(&gntmp
, nc
);
281 gntmp
.type
= GEN_EMAIL
;
283 /* Process any email address attributes in subject name */
286 const X509_NAME_ENTRY
*ne
;
288 i
= X509_NAME_get_index_by_NID(nm
, NID_pkcs9_emailAddress
, i
);
291 ne
= X509_NAME_get_entry(nm
, i
);
292 gntmp
.d
.rfc822Name
= X509_NAME_ENTRY_get_data(ne
);
293 if (gntmp
.d
.rfc822Name
->type
!= V_ASN1_IA5STRING
)
294 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
296 r
= nc_match(&gntmp
, nc
);
304 for (i
= 0; i
< sk_GENERAL_NAME_num(x
->altname
); i
++) {
305 GENERAL_NAME
*gen
= sk_GENERAL_NAME_value(x
->altname
, i
);
306 r
= nc_match(gen
, nc
);
315 static int cn2dnsid(ASN1_STRING
*cn
, unsigned char **dnsid
, size_t *idlen
)
318 unsigned char *utf8_value
;
322 /* Don't leave outputs uninitialized */
327 * Per RFC 6125, DNS-IDs representing internationalized domain names appear
328 * in certificates in A-label encoded form:
330 * https://tools.ietf.org/html/rfc6125#section-6.4.2
332 * The same applies to CNs which are intended to represent DNS names.
333 * However, while in the SAN DNS-IDs are IA5Strings, as CNs they may be
334 * needlessly encoded in 16-bit Unicode. We perform a conversion to UTF-8
335 * to ensure that we get an ASCII representation of any CNs that are
336 * representable as ASCII, but just not encoded as ASCII. The UTF-8 form
337 * may contain some non-ASCII octets, and that's fine, such CNs are not
338 * valid legacy DNS names.
340 * Note, 'int' is the return type of ASN1_STRING_to_UTF8() so that's what
341 * we must use for 'utf8_length'.
343 if ((utf8_length
= ASN1_STRING_to_UTF8(&utf8_value
, cn
)) < 0)
344 return X509_V_ERR_OUT_OF_MEM
;
347 * Some certificates have had names that include a *trailing* NUL byte.
348 * Remove these harmless NUL characters. They would otherwise yield false
349 * alarms with the following embedded NUL check.
351 while (utf8_length
> 0 && utf8_value
[utf8_length
- 1] == '\0')
354 /* Reject *embedded* NULs */
355 if (memchr(utf8_value
, 0, utf8_length
) != NULL
) {
356 OPENSSL_free(utf8_value
);
357 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
361 * XXX: Deviation from strict DNS name syntax, also check names with '_'
362 * Check DNS name syntax, any '-' or '.' must be internal,
363 * and on either side of each '.' we can't have a '-' or '.'.
365 * If the name has just one label, we don't consider it a DNS name. This
366 * means that "CN=sometld" cannot be precluded by DNS name constraints, but
367 * that is not a problem.
369 for (i
= 0; i
< utf8_length
; ++i
) {
370 unsigned char c
= utf8_value
[i
];
372 if ((c
>= 'a' && c
<= 'z')
373 || (c
>= 'A' && c
<= 'Z')
374 || (c
>= '0' && c
<= '9')
378 /* Dot and hyphen cannot be first or last. */
379 if (i
> 0 && i
< utf8_length
- 1) {
383 * Next to a dot the preceding and following characters must not be
384 * another dot or a hyphen. Otherwise, record that the name is
385 * plausible, since it has two or more labels.
388 && utf8_value
[i
+ 1] != '.'
389 && utf8_value
[i
- 1] != '-'
390 && utf8_value
[i
+ 1] != '-') {
401 *idlen
= (size_t)utf8_length
;
404 OPENSSL_free(utf8_value
);
409 * Check CN against DNS-ID name constraints.
411 int NAME_CONSTRAINTS_check_CN(X509
*x
, NAME_CONSTRAINTS
*nc
)
414 const X509_NAME
*nm
= X509_get_subject_name(x
);
419 stmp
.type
= V_ASN1_IA5STRING
;
420 gntmp
.type
= GEN_DNS
;
421 gntmp
.d
.dNSName
= &stmp
;
423 /* Process any commonName attributes in subject name */
428 unsigned char *idval
;
431 i
= X509_NAME_get_index_by_NID(nm
, NID_commonName
, i
);
434 ne
= X509_NAME_get_entry(nm
, i
);
435 cn
= X509_NAME_ENTRY_get_data(ne
);
437 /* Only process attributes that look like host names */
438 if ((r
= cn2dnsid(cn
, &idval
, &idlen
)) != X509_V_OK
)
445 r
= nc_match(&gntmp
, nc
);
454 * Return nonzero if the GeneralSubtree has valid 'minimum' field
455 * (must be absent or 0) and valid 'maximum' field (must be absent).
457 static int nc_minmax_valid(GENERAL_SUBTREE
*sub
) {
465 bn
= ASN1_INTEGER_to_BN(sub
->minimum
, NULL
);
466 if (bn
== NULL
|| !BN_is_zero(bn
))
474 static int nc_match(GENERAL_NAME
*gen
, NAME_CONSTRAINTS
*nc
)
476 GENERAL_SUBTREE
*sub
;
479 * We need to compare not gen->type field but an "effective" type because
480 * the otherName field may contain EAI email address treated specially
481 * according to RFC 8398, section 6
483 int effective_type
= ((gen
->type
== GEN_OTHERNAME
) &&
484 (OBJ_obj2nid(gen
->d
.otherName
->type_id
) ==
485 NID_id_on_SmtpUTF8Mailbox
)) ? GEN_EMAIL
: gen
->type
;
488 * Permitted subtrees: if any subtrees exist of matching the type at
489 * least one subtree must match.
492 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(nc
->permittedSubtrees
); i
++) {
493 sub
= sk_GENERAL_SUBTREE_value(nc
->permittedSubtrees
, i
);
494 if (effective_type
!= sub
->base
->type
)
496 if (!nc_minmax_valid(sub
))
497 return X509_V_ERR_SUBTREE_MINMAX
;
498 /* If we already have a match don't bother trying any more */
503 r
= nc_match_single(gen
, sub
->base
);
506 else if (r
!= X509_V_ERR_PERMITTED_VIOLATION
)
511 return X509_V_ERR_PERMITTED_VIOLATION
;
513 /* Excluded subtrees: must not match any of these */
515 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(nc
->excludedSubtrees
); i
++) {
516 sub
= sk_GENERAL_SUBTREE_value(nc
->excludedSubtrees
, i
);
517 if (effective_type
!= sub
->base
->type
)
519 if (!nc_minmax_valid(sub
))
520 return X509_V_ERR_SUBTREE_MINMAX
;
522 r
= nc_match_single(gen
, sub
->base
);
524 return X509_V_ERR_EXCLUDED_VIOLATION
;
525 else if (r
!= X509_V_ERR_PERMITTED_VIOLATION
)
534 static int nc_match_single(GENERAL_NAME
*gen
, GENERAL_NAME
*base
)
539 * We are here only when we have SmtpUTF8 name,
540 * so we match the value of othername with base->d.rfc822Name
542 return nc_email_eai(gen
->d
.otherName
->value
, base
->d
.rfc822Name
);
545 return nc_dn(gen
->d
.directoryName
, base
->d
.directoryName
);
548 return nc_dns(gen
->d
.dNSName
, base
->d
.dNSName
);
551 return nc_email(gen
->d
.rfc822Name
, base
->d
.rfc822Name
);
554 return nc_uri(gen
->d
.uniformResourceIdentifier
,
555 base
->d
.uniformResourceIdentifier
);
558 return nc_ip(gen
->d
.iPAddress
, base
->d
.iPAddress
);
561 return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE
;
567 * directoryName name constraint matching. The canonical encoding of
568 * X509_NAME makes this comparison easy. It is matched if the subtree is a
569 * subset of the name.
572 static int nc_dn(const X509_NAME
*nm
, const X509_NAME
*base
)
574 /* Ensure canonical encodings are up to date. */
575 if (nm
->modified
&& i2d_X509_NAME(nm
, NULL
) < 0)
576 return X509_V_ERR_OUT_OF_MEM
;
577 if (base
->modified
&& i2d_X509_NAME(base
, NULL
) < 0)
578 return X509_V_ERR_OUT_OF_MEM
;
579 if (base
->canon_enclen
> nm
->canon_enclen
)
580 return X509_V_ERR_PERMITTED_VIOLATION
;
581 if (memcmp(base
->canon_enc
, nm
->canon_enc
, base
->canon_enclen
))
582 return X509_V_ERR_PERMITTED_VIOLATION
;
586 static int nc_dns(ASN1_IA5STRING
*dns
, ASN1_IA5STRING
*base
)
588 char *baseptr
= (char *)base
->data
;
589 char *dnsptr
= (char *)dns
->data
;
591 /* Empty matches everything */
592 if (base
->length
== 0)
595 if (dns
->length
< base
->length
)
596 return X509_V_ERR_PERMITTED_VIOLATION
;
599 * Otherwise can add zero or more components on the left so compare RHS
600 * and if dns is longer and expect '.' as preceding character.
602 if (dns
->length
> base
->length
) {
603 dnsptr
+= dns
->length
- base
->length
;
604 if (*baseptr
!= '.' && dnsptr
[-1] != '.')
605 return X509_V_ERR_PERMITTED_VIOLATION
;
608 if (ia5ncasecmp(baseptr
, dnsptr
, base
->length
))
609 return X509_V_ERR_PERMITTED_VIOLATION
;
616 * This function implements comparison between ASCII/U-label in emltype
617 * and A-label in base according to RFC 8398, section 6.
618 * Convert base to U-label and ASCII-parts of domain names, for base
619 * Octet-to-octet comparison of `emltype` and `base` hostname parts
620 * (ASCII-parts should be compared in case-insensitive manner)
622 static int nc_email_eai(ASN1_TYPE
*emltype
, ASN1_IA5STRING
*base
)
624 ASN1_UTF8STRING
*eml
;
625 char *baseptr
= NULL
;
629 size_t size
= sizeof(ulabel
) - 1;
633 /* We do not accept embedded NUL characters */
634 if (base
->length
> 0 && memchr(base
->data
, 0, base
->length
) != NULL
)
635 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
637 /* 'base' may not be NUL terminated. Create a copy that is */
638 baseptr
= OPENSSL_strndup((char *)base
->data
, base
->length
);
640 return X509_V_ERR_OUT_OF_MEM
;
642 if (emltype
->type
!= V_ASN1_UTF8STRING
) {
643 ret
= X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
647 eml
= emltype
->value
.utf8string
;
648 emlptr
= (char *)eml
->data
;
649 emlat
= ia5memrchr(eml
, '@');
652 ret
= X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
656 memset(ulabel
, 0, sizeof(ulabel
));
657 /* Special case: initial '.' is RHS match */
658 if (*baseptr
== '.') {
661 if (ossl_a2ulabel(baseptr
, ulabel
+ 1, &size
) <= 0) {
662 ret
= X509_V_ERR_UNSPECIFIED
;
666 if ((size_t)eml
->length
> strlen(ulabel
)) {
667 emlptr
+= eml
->length
- (strlen(ulabel
));
669 if (ia5ncasecmp(ulabel
, emlptr
, strlen(ulabel
)) == 0)
672 ret
= X509_V_ERR_PERMITTED_VIOLATION
;
676 if (ossl_a2ulabel(baseptr
, ulabel
, &size
) <= 0) {
677 ret
= X509_V_ERR_UNSPECIFIED
;
680 /* Just have hostname left to match: case insensitive */
682 emlhostlen
= IA5_OFFSET_LEN(eml
, emlptr
);
683 if (emlhostlen
!= strlen(ulabel
)
684 || ia5ncasecmp(ulabel
, emlptr
, emlhostlen
) != 0) {
685 ret
= X509_V_ERR_PERMITTED_VIOLATION
;
690 OPENSSL_free(baseptr
);
694 static int nc_email(ASN1_IA5STRING
*eml
, ASN1_IA5STRING
*base
)
696 const char *baseptr
= (char *)base
->data
;
697 const char *emlptr
= (char *)eml
->data
;
698 const char *baseat
= ia5memrchr(base
, '@');
699 const char *emlat
= ia5memrchr(eml
, '@');
700 size_t basehostlen
, emlhostlen
;
703 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
704 /* Special case: initial '.' is RHS match */
705 if (!baseat
&& base
->length
> 0 && (*baseptr
== '.')) {
706 if (eml
->length
> base
->length
) {
707 emlptr
+= eml
->length
- base
->length
;
708 if (ia5ncasecmp(baseptr
, emlptr
, base
->length
) == 0)
711 return X509_V_ERR_PERMITTED_VIOLATION
;
714 /* If we have anything before '@' match local part */
717 if (baseat
!= baseptr
) {
718 if ((baseat
- baseptr
) != (emlat
- emlptr
))
719 return X509_V_ERR_PERMITTED_VIOLATION
;
720 if (memchr(baseptr
, 0, baseat
- baseptr
) ||
721 memchr(emlptr
, 0, emlat
- emlptr
))
722 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
723 /* Case sensitive match of local part */
724 if (strncmp(baseptr
, emlptr
, emlat
- emlptr
))
725 return X509_V_ERR_PERMITTED_VIOLATION
;
727 /* Position base after '@' */
728 baseptr
= baseat
+ 1;
731 basehostlen
= IA5_OFFSET_LEN(base
, baseptr
);
732 emlhostlen
= IA5_OFFSET_LEN(eml
, emlptr
);
733 /* Just have hostname left to match: case insensitive */
734 if (basehostlen
!= emlhostlen
|| ia5ncasecmp(baseptr
, emlptr
, emlhostlen
))
735 return X509_V_ERR_PERMITTED_VIOLATION
;
741 static int nc_uri(ASN1_IA5STRING
*uri
, ASN1_IA5STRING
*base
)
743 const char *baseptr
= (char *)base
->data
;
744 const char *hostptr
= (char *)uri
->data
;
745 const char *p
= ia5memchr(uri
, (char *)uri
->data
, ':');
748 /* Check for foo:// and skip past it */
750 || IA5_OFFSET_LEN(uri
, p
) < 3
753 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
756 /* Determine length of hostname part of URI */
758 /* Look for a port indicator as end of hostname first */
760 p
= ia5memchr(uri
, hostptr
, ':');
761 /* Otherwise look for trailing slash */
763 p
= ia5memchr(uri
, hostptr
, '/');
766 hostlen
= IA5_OFFSET_LEN(uri
, hostptr
);
768 hostlen
= p
- hostptr
;
771 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
773 /* Special case: initial '.' is RHS match */
774 if (base
->length
> 0 && *baseptr
== '.') {
775 if (hostlen
> base
->length
) {
776 p
= hostptr
+ hostlen
- base
->length
;
777 if (ia5ncasecmp(p
, baseptr
, base
->length
) == 0)
780 return X509_V_ERR_PERMITTED_VIOLATION
;
783 if ((base
->length
!= (int)hostlen
)
784 || ia5ncasecmp(hostptr
, baseptr
, hostlen
))
785 return X509_V_ERR_PERMITTED_VIOLATION
;
791 static int nc_ip(ASN1_OCTET_STRING
*ip
, ASN1_OCTET_STRING
*base
)
793 int hostlen
, baselen
, i
;
794 unsigned char *hostptr
, *baseptr
, *maskptr
;
796 hostlen
= ip
->length
;
797 baseptr
= base
->data
;
798 baselen
= base
->length
;
800 /* Invalid if not IPv4 or IPv6 */
801 if (!((hostlen
== 4) || (hostlen
== 16)))
802 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
803 if (!((baselen
== 8) || (baselen
== 32)))
804 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
806 /* Do not match IPv4 with IPv6 */
807 if (hostlen
* 2 != baselen
)
808 return X509_V_ERR_PERMITTED_VIOLATION
;
810 maskptr
= base
->data
+ hostlen
;
812 /* Considering possible not aligned base ipAddress */
813 /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */
814 for (i
= 0; i
< hostlen
; i
++)
815 if ((hostptr
[i
] & maskptr
[i
]) != (baseptr
[i
] & maskptr
[i
]))
816 return X509_V_ERR_PERMITTED_VIOLATION
;