2 * Copyright 2003-2023 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(int effective_type
, GENERAL_NAME
*sub
,
39 static int nc_dn(const X509_NAME
*sub
, const X509_NAME
*nm
);
40 static int nc_dns(ASN1_IA5STRING
*sub
, ASN1_IA5STRING
*dns
);
41 static int nc_email(ASN1_IA5STRING
*sub
, ASN1_IA5STRING
*eml
);
42 static int nc_email_eai(ASN1_TYPE
*emltype
, ASN1_IA5STRING
*base
);
43 static int nc_uri(ASN1_IA5STRING
*uri
, ASN1_IA5STRING
*base
);
44 static int nc_ip(ASN1_OCTET_STRING
*ip
, ASN1_OCTET_STRING
*base
);
46 const X509V3_EXT_METHOD ossl_v3_name_constraints
= {
47 NID_name_constraints
, 0,
48 ASN1_ITEM_ref(NAME_CONSTRAINTS
),
51 0, v2i_NAME_CONSTRAINTS
,
52 i2r_NAME_CONSTRAINTS
, 0,
56 ASN1_SEQUENCE(GENERAL_SUBTREE
) = {
57 ASN1_SIMPLE(GENERAL_SUBTREE
, base
, GENERAL_NAME
),
58 ASN1_IMP_OPT(GENERAL_SUBTREE
, minimum
, ASN1_INTEGER
, 0),
59 ASN1_IMP_OPT(GENERAL_SUBTREE
, maximum
, ASN1_INTEGER
, 1)
60 } ASN1_SEQUENCE_END(GENERAL_SUBTREE
)
62 ASN1_SEQUENCE(NAME_CONSTRAINTS
) = {
63 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS
, permittedSubtrees
,
65 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS
, excludedSubtrees
,
67 } ASN1_SEQUENCE_END(NAME_CONSTRAINTS
)
70 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE
)
71 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS
)
74 #define IA5_OFFSET_LEN(ia5base, offset) \
75 ((ia5base)->length - ((unsigned char *)(offset) - (ia5base)->data))
77 /* Like memchr but for ASN1_IA5STRING. Additionally you can specify the
78 * starting point to search from
80 # define ia5memchr(str, start, c) memchr(start, c, IA5_OFFSET_LEN(str, start))
82 /* Like memrrchr but for ASN1_IA5STRING */
83 static char *ia5memrchr(ASN1_IA5STRING
*str
, int c
)
87 for (i
= str
->length
; i
> 0 && str
->data
[i
- 1] != c
; i
--);
92 return (char *)&str
->data
[i
- 1];
96 * We cannot use strncasecmp here because that applies locale specific rules. It
97 * also doesn't work with ASN1_STRINGs that may have embedded NUL characters.
98 * For example in Turkish 'I' is not the uppercase character for 'i'. We need to
99 * do a simple ASCII case comparison ignoring the locale (that is why we use
100 * numeric constants below).
102 static int ia5ncasecmp(const char *s1
, const char *s2
, size_t n
)
104 for (; n
> 0; n
--, s1
++, s2
++) {
106 unsigned char c1
= (unsigned char)*s1
, c2
= (unsigned char)*s2
;
108 /* Convert to lower case */
109 if (c1
>= 0x41 /* A */ && c1
<= 0x5A /* Z */)
111 if (c2
>= 0x41 /* A */ && c2
<= 0x5A /* Z */)
128 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
,
129 X509V3_CTX
*ctx
, STACK_OF(CONF_VALUE
) *nval
)
132 CONF_VALUE tval
, *val
;
133 STACK_OF(GENERAL_SUBTREE
) **ptree
= NULL
;
134 NAME_CONSTRAINTS
*ncons
= NULL
;
135 GENERAL_SUBTREE
*sub
= NULL
;
137 ncons
= NAME_CONSTRAINTS_new();
139 ERR_raise(ERR_LIB_X509V3
, ERR_R_ASN1_LIB
);
142 for (i
= 0; i
< sk_CONF_VALUE_num(nval
); i
++) {
143 val
= sk_CONF_VALUE_value(nval
, i
);
144 if (HAS_PREFIX(val
->name
, "permitted") && val
->name
[9]) {
145 ptree
= &ncons
->permittedSubtrees
;
146 tval
.name
= val
->name
+ 10;
147 } else if (HAS_PREFIX(val
->name
, "excluded") && val
->name
[8]) {
148 ptree
= &ncons
->excludedSubtrees
;
149 tval
.name
= val
->name
+ 9;
151 ERR_raise(ERR_LIB_X509V3
, X509V3_R_INVALID_SYNTAX
);
154 tval
.value
= val
->value
;
155 sub
= GENERAL_SUBTREE_new();
157 ERR_raise(ERR_LIB_X509V3
, ERR_R_ASN1_LIB
);
160 if (!v2i_GENERAL_NAME_ex(sub
->base
, method
, ctx
, &tval
, 1)) {
161 ERR_raise(ERR_LIB_X509V3
, ERR_R_X509V3_LIB
);
165 *ptree
= sk_GENERAL_SUBTREE_new_null();
166 if (*ptree
== NULL
|| !sk_GENERAL_SUBTREE_push(*ptree
, sub
)) {
167 ERR_raise(ERR_LIB_X509V3
, ERR_R_CRYPTO_LIB
);
176 NAME_CONSTRAINTS_free(ncons
);
177 GENERAL_SUBTREE_free(sub
);
182 static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD
*method
, void *a
,
185 NAME_CONSTRAINTS
*ncons
= a
;
186 do_i2r_name_constraints(method
, ncons
->permittedSubtrees
,
187 bp
, ind
, "Permitted");
188 if (ncons
->permittedSubtrees
&& ncons
->excludedSubtrees
)
190 do_i2r_name_constraints(method
, ncons
->excludedSubtrees
,
191 bp
, ind
, "Excluded");
195 static int do_i2r_name_constraints(const X509V3_EXT_METHOD
*method
,
196 STACK_OF(GENERAL_SUBTREE
) *trees
,
197 BIO
*bp
, int ind
, const char *name
)
199 GENERAL_SUBTREE
*tree
;
201 if (sk_GENERAL_SUBTREE_num(trees
) > 0)
202 BIO_printf(bp
, "%*s%s:\n", ind
, "", name
);
203 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(trees
); i
++) {
206 tree
= sk_GENERAL_SUBTREE_value(trees
, i
);
207 BIO_printf(bp
, "%*s", ind
+ 2, "");
208 if (tree
->base
->type
== GEN_IPADD
)
209 print_nc_ipadd(bp
, tree
->base
->d
.ip
);
211 GENERAL_NAME_print(bp
, tree
->base
);
216 static int print_nc_ipadd(BIO
*bp
, ASN1_OCTET_STRING
*ip
)
218 /* ip->length should be 8 or 32 and len1 == len2 == 4 or len1 == len2 == 16 */
219 int len1
= ip
->length
>= 16 ? 16 : ip
->length
>= 4 ? 4 : ip
->length
;
220 int len2
= ip
->length
- len1
;
221 char *ip1
= ossl_ipaddr_to_asc(ip
->data
, len1
);
222 char *ip2
= ossl_ipaddr_to_asc(ip
->data
+ len1
, len2
);
223 int ret
= ip1
!= NULL
&& ip2
!= NULL
224 && BIO_printf(bp
, "IP:%s/%s", ip1
, ip2
) > 0;
231 #define NAME_CHECK_MAX (1 << 20)
233 static int add_lengths(int *out
, int a
, int b
)
237 /* sk_FOO_num(NULL) returns -1 but is effectively 0 when iterating. */
243 *out
= safe_add_int(a
, b
, &err
);
248 * Check a certificate conforms to a specified set of constraints.
250 * X509_V_OK: All constraints obeyed.
251 * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
252 * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
253 * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
254 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
255 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
256 * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
259 int NAME_CONSTRAINTS_check(X509
*x
, NAME_CONSTRAINTS
*nc
)
261 int r
, i
, name_count
, constraint_count
;
264 nm
= X509_get_subject_name(x
);
267 * Guard against certificates with an excessive number of names or
268 * constraints causing a computationally expensive name constraints check.
270 if (!add_lengths(&name_count
, X509_NAME_entry_count(nm
),
271 sk_GENERAL_NAME_num(x
->altname
))
272 || !add_lengths(&constraint_count
,
273 sk_GENERAL_SUBTREE_num(nc
->permittedSubtrees
),
274 sk_GENERAL_SUBTREE_num(nc
->excludedSubtrees
))
275 || (name_count
> 0 && constraint_count
> NAME_CHECK_MAX
/ name_count
))
276 return X509_V_ERR_UNSPECIFIED
;
278 if (X509_NAME_entry_count(nm
) > 0) {
280 gntmp
.type
= GEN_DIRNAME
;
281 gntmp
.d
.directoryName
= nm
;
283 r
= nc_match(&gntmp
, nc
);
288 gntmp
.type
= GEN_EMAIL
;
290 /* Process any email address attributes in subject name */
293 const X509_NAME_ENTRY
*ne
;
295 i
= X509_NAME_get_index_by_NID(nm
, NID_pkcs9_emailAddress
, i
);
298 ne
= X509_NAME_get_entry(nm
, i
);
299 gntmp
.d
.rfc822Name
= X509_NAME_ENTRY_get_data(ne
);
300 if (gntmp
.d
.rfc822Name
->type
!= V_ASN1_IA5STRING
)
301 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
303 r
= nc_match(&gntmp
, nc
);
311 for (i
= 0; i
< sk_GENERAL_NAME_num(x
->altname
); i
++) {
312 GENERAL_NAME
*gen
= sk_GENERAL_NAME_value(x
->altname
, i
);
313 r
= nc_match(gen
, nc
);
322 static int cn2dnsid(ASN1_STRING
*cn
, unsigned char **dnsid
, size_t *idlen
)
325 unsigned char *utf8_value
;
329 /* Don't leave outputs uninitialized */
334 * Per RFC 6125, DNS-IDs representing internationalized domain names appear
335 * in certificates in A-label encoded form:
337 * https://tools.ietf.org/html/rfc6125#section-6.4.2
339 * The same applies to CNs which are intended to represent DNS names.
340 * However, while in the SAN DNS-IDs are IA5Strings, as CNs they may be
341 * needlessly encoded in 16-bit Unicode. We perform a conversion to UTF-8
342 * to ensure that we get an ASCII representation of any CNs that are
343 * representable as ASCII, but just not encoded as ASCII. The UTF-8 form
344 * may contain some non-ASCII octets, and that's fine, such CNs are not
345 * valid legacy DNS names.
347 * Note, 'int' is the return type of ASN1_STRING_to_UTF8() so that's what
348 * we must use for 'utf8_length'.
350 if ((utf8_length
= ASN1_STRING_to_UTF8(&utf8_value
, cn
)) < 0)
351 return X509_V_ERR_OUT_OF_MEM
;
354 * Some certificates have had names that include a *trailing* NUL byte.
355 * Remove these harmless NUL characters. They would otherwise yield false
356 * alarms with the following embedded NUL check.
358 while (utf8_length
> 0 && utf8_value
[utf8_length
- 1] == '\0')
361 /* Reject *embedded* NULs */
362 if (memchr(utf8_value
, 0, utf8_length
) != NULL
) {
363 OPENSSL_free(utf8_value
);
364 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
368 * XXX: Deviation from strict DNS name syntax, also check names with '_'
369 * Check DNS name syntax, any '-' or '.' must be internal,
370 * and on either side of each '.' we can't have a '-' or '.'.
372 * If the name has just one label, we don't consider it a DNS name. This
373 * means that "CN=sometld" cannot be precluded by DNS name constraints, but
374 * that is not a problem.
376 for (i
= 0; i
< utf8_length
; ++i
) {
377 unsigned char c
= utf8_value
[i
];
379 if ((c
>= 'a' && c
<= 'z')
380 || (c
>= 'A' && c
<= 'Z')
381 || (c
>= '0' && c
<= '9')
385 /* Dot and hyphen cannot be first or last. */
386 if (i
> 0 && i
< utf8_length
- 1) {
390 * Next to a dot the preceding and following characters must not be
391 * another dot or a hyphen. Otherwise, record that the name is
392 * plausible, since it has two or more labels.
395 && utf8_value
[i
+ 1] != '.'
396 && utf8_value
[i
- 1] != '-'
397 && utf8_value
[i
+ 1] != '-') {
408 *idlen
= (size_t)utf8_length
;
411 OPENSSL_free(utf8_value
);
416 * Check CN against DNS-ID name constraints.
418 int NAME_CONSTRAINTS_check_CN(X509
*x
, NAME_CONSTRAINTS
*nc
)
421 const X509_NAME
*nm
= X509_get_subject_name(x
);
426 stmp
.type
= V_ASN1_IA5STRING
;
427 gntmp
.type
= GEN_DNS
;
428 gntmp
.d
.dNSName
= &stmp
;
430 /* Process any commonName attributes in subject name */
435 unsigned char *idval
;
438 i
= X509_NAME_get_index_by_NID(nm
, NID_commonName
, i
);
441 ne
= X509_NAME_get_entry(nm
, i
);
442 cn
= X509_NAME_ENTRY_get_data(ne
);
444 /* Only process attributes that look like hostnames */
445 if ((r
= cn2dnsid(cn
, &idval
, &idlen
)) != X509_V_OK
)
452 r
= nc_match(&gntmp
, nc
);
461 * Return nonzero if the GeneralSubtree has valid 'minimum' field
462 * (must be absent or 0) and valid 'maximum' field (must be absent).
464 static int nc_minmax_valid(GENERAL_SUBTREE
*sub
) {
472 bn
= ASN1_INTEGER_to_BN(sub
->minimum
, NULL
);
473 if (bn
== NULL
|| !BN_is_zero(bn
))
481 static int nc_match(GENERAL_NAME
*gen
, NAME_CONSTRAINTS
*nc
)
483 GENERAL_SUBTREE
*sub
;
485 int effective_type
= gen
->type
;
488 * We need to compare not gen->type field but an "effective" type because
489 * the otherName field may contain EAI email address treated specially
490 * according to RFC 8398, section 6
492 if (effective_type
== GEN_OTHERNAME
&&
493 (OBJ_obj2nid(gen
->d
.otherName
->type_id
) == NID_id_on_SmtpUTF8Mailbox
)) {
494 effective_type
= GEN_EMAIL
;
498 * Permitted subtrees: if any subtrees exist of matching the type at
499 * least one subtree must match.
502 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(nc
->permittedSubtrees
); i
++) {
503 sub
= sk_GENERAL_SUBTREE_value(nc
->permittedSubtrees
, i
);
504 if (effective_type
!= sub
->base
->type
505 || (effective_type
== GEN_OTHERNAME
&&
506 OBJ_cmp(gen
->d
.otherName
->type_id
,
507 sub
->base
->d
.otherName
->type_id
) != 0))
509 if (!nc_minmax_valid(sub
))
510 return X509_V_ERR_SUBTREE_MINMAX
;
511 /* If we already have a match don't bother trying any more */
516 r
= nc_match_single(effective_type
, gen
, sub
->base
);
519 else if (r
!= X509_V_ERR_PERMITTED_VIOLATION
)
524 return X509_V_ERR_PERMITTED_VIOLATION
;
526 /* Excluded subtrees: must not match any of these */
528 for (i
= 0; i
< sk_GENERAL_SUBTREE_num(nc
->excludedSubtrees
); i
++) {
529 sub
= sk_GENERAL_SUBTREE_value(nc
->excludedSubtrees
, i
);
530 if (effective_type
!= sub
->base
->type
531 || (effective_type
== GEN_OTHERNAME
&&
532 OBJ_cmp(gen
->d
.otherName
->type_id
,
533 sub
->base
->d
.otherName
->type_id
) != 0))
535 if (!nc_minmax_valid(sub
))
536 return X509_V_ERR_SUBTREE_MINMAX
;
538 r
= nc_match_single(effective_type
, gen
, sub
->base
);
540 return X509_V_ERR_EXCLUDED_VIOLATION
;
541 else if (r
!= X509_V_ERR_PERMITTED_VIOLATION
)
550 static int nc_match_single(int effective_type
, GENERAL_NAME
*gen
,
555 switch (effective_type
) {
558 * We are here only when we have SmtpUTF8 name,
559 * so we match the value of othername with base->d.rfc822Name
561 return nc_email_eai(gen
->d
.otherName
->value
, base
->d
.rfc822Name
);
564 return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE
;
568 return nc_dn(gen
->d
.directoryName
, base
->d
.directoryName
);
571 return nc_dns(gen
->d
.dNSName
, base
->d
.dNSName
);
574 return nc_email(gen
->d
.rfc822Name
, base
->d
.rfc822Name
);
577 return nc_uri(gen
->d
.uniformResourceIdentifier
,
578 base
->d
.uniformResourceIdentifier
);
581 return nc_ip(gen
->d
.iPAddress
, base
->d
.iPAddress
);
584 return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE
;
590 * directoryName name constraint matching. The canonical encoding of
591 * X509_NAME makes this comparison easy. It is matched if the subtree is a
592 * subset of the name.
595 static int nc_dn(const X509_NAME
*nm
, const X509_NAME
*base
)
597 /* Ensure canonical encodings are up to date. */
598 if (nm
->modified
&& i2d_X509_NAME(nm
, NULL
) < 0)
599 return X509_V_ERR_OUT_OF_MEM
;
600 if (base
->modified
&& i2d_X509_NAME(base
, NULL
) < 0)
601 return X509_V_ERR_OUT_OF_MEM
;
602 if (base
->canon_enclen
> nm
->canon_enclen
)
603 return X509_V_ERR_PERMITTED_VIOLATION
;
604 if (memcmp(base
->canon_enc
, nm
->canon_enc
, base
->canon_enclen
))
605 return X509_V_ERR_PERMITTED_VIOLATION
;
609 static int nc_dns(ASN1_IA5STRING
*dns
, ASN1_IA5STRING
*base
)
611 char *baseptr
= (char *)base
->data
;
612 char *dnsptr
= (char *)dns
->data
;
614 /* Empty matches everything */
615 if (base
->length
== 0)
618 if (dns
->length
< base
->length
)
619 return X509_V_ERR_PERMITTED_VIOLATION
;
622 * Otherwise can add zero or more components on the left so compare RHS
623 * and if dns is longer and expect '.' as preceding character.
625 if (dns
->length
> base
->length
) {
626 dnsptr
+= dns
->length
- base
->length
;
627 if (*baseptr
!= '.' && dnsptr
[-1] != '.')
628 return X509_V_ERR_PERMITTED_VIOLATION
;
631 if (ia5ncasecmp(baseptr
, dnsptr
, base
->length
))
632 return X509_V_ERR_PERMITTED_VIOLATION
;
639 * This function implements comparison between ASCII/U-label in emltype
640 * and A-label in base according to RFC 8398, section 6.
641 * Convert base to U-label and ASCII-parts of domain names, for base
642 * Octet-to-octet comparison of `emltype` and `base` hostname parts
643 * (ASCII-parts should be compared in case-insensitive manner)
645 static int nc_email_eai(ASN1_TYPE
*emltype
, ASN1_IA5STRING
*base
)
647 ASN1_UTF8STRING
*eml
;
648 char *baseptr
= NULL
;
652 size_t size
= sizeof(ulabel
);
656 /* We do not accept embedded NUL characters */
657 if (base
->length
> 0 && memchr(base
->data
, 0, base
->length
) != NULL
)
658 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
660 /* 'base' may not be NUL terminated. Create a copy that is */
661 baseptr
= OPENSSL_strndup((char *)base
->data
, base
->length
);
663 return X509_V_ERR_OUT_OF_MEM
;
665 if (emltype
->type
!= V_ASN1_UTF8STRING
) {
666 ret
= X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
670 eml
= emltype
->value
.utf8string
;
671 emlptr
= (char *)eml
->data
;
672 emlat
= ia5memrchr(eml
, '@');
675 ret
= X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
679 /* Special case: initial '.' is RHS match */
680 if (*baseptr
== '.') {
682 if (ossl_a2ulabel(baseptr
, ulabel
+ 1, size
- 1) <= 0) {
683 ret
= X509_V_ERR_UNSPECIFIED
;
687 if ((size_t)eml
->length
> strlen(ulabel
)) {
688 emlptr
+= eml
->length
- strlen(ulabel
);
690 if (ia5ncasecmp(ulabel
, emlptr
, strlen(ulabel
)) == 0)
693 ret
= X509_V_ERR_PERMITTED_VIOLATION
;
697 if (ossl_a2ulabel(baseptr
, ulabel
, size
) <= 0) {
698 ret
= X509_V_ERR_UNSPECIFIED
;
701 /* Just have hostname left to match: case insensitive */
703 emlhostlen
= IA5_OFFSET_LEN(eml
, emlptr
);
704 if (emlhostlen
!= strlen(ulabel
)
705 || ia5ncasecmp(ulabel
, emlptr
, emlhostlen
) != 0) {
706 ret
= X509_V_ERR_PERMITTED_VIOLATION
;
711 OPENSSL_free(baseptr
);
715 static int nc_email(ASN1_IA5STRING
*eml
, ASN1_IA5STRING
*base
)
717 const char *baseptr
= (char *)base
->data
;
718 const char *emlptr
= (char *)eml
->data
;
719 const char *baseat
= ia5memrchr(base
, '@');
720 const char *emlat
= ia5memrchr(eml
, '@');
721 size_t basehostlen
, emlhostlen
;
724 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
725 /* Special case: initial '.' is RHS match */
726 if (!baseat
&& base
->length
> 0 && (*baseptr
== '.')) {
727 if (eml
->length
> base
->length
) {
728 emlptr
+= eml
->length
- base
->length
;
729 if (ia5ncasecmp(baseptr
, emlptr
, base
->length
) == 0)
732 return X509_V_ERR_PERMITTED_VIOLATION
;
735 /* If we have anything before '@' match local part */
738 if (baseat
!= baseptr
) {
739 if ((baseat
- baseptr
) != (emlat
- emlptr
))
740 return X509_V_ERR_PERMITTED_VIOLATION
;
741 if (memchr(baseptr
, 0, baseat
- baseptr
) ||
742 memchr(emlptr
, 0, emlat
- emlptr
))
743 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
744 /* Case sensitive match of local part */
745 if (strncmp(baseptr
, emlptr
, emlat
- emlptr
))
746 return X509_V_ERR_PERMITTED_VIOLATION
;
748 /* Position base after '@' */
749 baseptr
= baseat
+ 1;
752 basehostlen
= IA5_OFFSET_LEN(base
, baseptr
);
753 emlhostlen
= IA5_OFFSET_LEN(eml
, emlptr
);
754 /* Just have hostname left to match: case insensitive */
755 if (basehostlen
!= emlhostlen
|| ia5ncasecmp(baseptr
, emlptr
, emlhostlen
))
756 return X509_V_ERR_PERMITTED_VIOLATION
;
762 static int nc_uri(ASN1_IA5STRING
*uri
, ASN1_IA5STRING
*base
)
764 const char *baseptr
= (char *)base
->data
;
765 const char *hostptr
= (char *)uri
->data
;
766 const char *p
= ia5memchr(uri
, (char *)uri
->data
, ':');
769 /* Check for foo:// and skip past it */
771 || IA5_OFFSET_LEN(uri
, p
) < 3
774 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
777 /* Determine length of hostname part of URI */
779 /* Look for a port indicator as end of hostname first */
781 p
= ia5memchr(uri
, hostptr
, ':');
782 /* Otherwise look for trailing slash */
784 p
= ia5memchr(uri
, hostptr
, '/');
787 hostlen
= IA5_OFFSET_LEN(uri
, hostptr
);
789 hostlen
= p
- hostptr
;
792 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
794 /* Special case: initial '.' is RHS match */
795 if (base
->length
> 0 && *baseptr
== '.') {
796 if (hostlen
> base
->length
) {
797 p
= hostptr
+ hostlen
- base
->length
;
798 if (ia5ncasecmp(p
, baseptr
, base
->length
) == 0)
801 return X509_V_ERR_PERMITTED_VIOLATION
;
804 if ((base
->length
!= (int)hostlen
)
805 || ia5ncasecmp(hostptr
, baseptr
, hostlen
))
806 return X509_V_ERR_PERMITTED_VIOLATION
;
812 static int nc_ip(ASN1_OCTET_STRING
*ip
, ASN1_OCTET_STRING
*base
)
814 int hostlen
, baselen
, i
;
815 unsigned char *hostptr
, *baseptr
, *maskptr
;
817 hostlen
= ip
->length
;
818 baseptr
= base
->data
;
819 baselen
= base
->length
;
821 /* Invalid if not IPv4 or IPv6 */
822 if (!((hostlen
== 4) || (hostlen
== 16)))
823 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
824 if (!((baselen
== 8) || (baselen
== 32)))
825 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
;
827 /* Do not match IPv4 with IPv6 */
828 if (hostlen
* 2 != baselen
)
829 return X509_V_ERR_PERMITTED_VIOLATION
;
831 maskptr
= base
->data
+ hostlen
;
833 /* Considering possible not aligned base ipAddress */
834 /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */
835 for (i
= 0; i
< hostlen
; i
++)
836 if ((hostptr
[i
] & maskptr
[i
]) != (baseptr
[i
] & maskptr
[i
]))
837 return X509_V_ERR_PERMITTED_VIOLATION
;