xref: /third_party/openssl/crypto/x509/v3_ncons.c (revision e1051a39)
1e1051a39Sopenharmony_ci/*
2e1051a39Sopenharmony_ci * Copyright 2003-2023 The OpenSSL Project Authors. All Rights Reserved.
3e1051a39Sopenharmony_ci *
4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
5e1051a39Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at
7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html
8e1051a39Sopenharmony_ci */
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ci#include "internal/cryptlib.h"
11e1051a39Sopenharmony_ci#include "internal/numbers.h"
12e1051a39Sopenharmony_ci#include <stdio.h>
13e1051a39Sopenharmony_ci#include "crypto/asn1.h"
14e1051a39Sopenharmony_ci#include <openssl/asn1t.h>
15e1051a39Sopenharmony_ci#include <openssl/conf.h>
16e1051a39Sopenharmony_ci#include <openssl/x509v3.h>
17e1051a39Sopenharmony_ci#include <openssl/bn.h>
18e1051a39Sopenharmony_ci
19e1051a39Sopenharmony_ci#include "crypto/x509.h"
20e1051a39Sopenharmony_ci#include "crypto/punycode.h"
21e1051a39Sopenharmony_ci#include "ext_dat.h"
22e1051a39Sopenharmony_ci
23e1051a39Sopenharmony_cistatic void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
24e1051a39Sopenharmony_ci                                  X509V3_CTX *ctx,
25e1051a39Sopenharmony_ci                                  STACK_OF(CONF_VALUE) *nval);
26e1051a39Sopenharmony_cistatic int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
27e1051a39Sopenharmony_ci                                BIO *bp, int ind);
28e1051a39Sopenharmony_cistatic int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
29e1051a39Sopenharmony_ci                                   STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp,
30e1051a39Sopenharmony_ci                                   int ind, const char *name);
31e1051a39Sopenharmony_cistatic int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
32e1051a39Sopenharmony_ci
33e1051a39Sopenharmony_cistatic int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
34e1051a39Sopenharmony_cistatic int nc_match_single(int effective_type, GENERAL_NAME *sub,
35e1051a39Sopenharmony_ci                           GENERAL_NAME *gen);
36e1051a39Sopenharmony_cistatic int nc_dn(const X509_NAME *sub, const X509_NAME *nm);
37e1051a39Sopenharmony_cistatic int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
38e1051a39Sopenharmony_cistatic int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
39e1051a39Sopenharmony_cistatic int nc_email_eai(ASN1_TYPE *emltype, ASN1_IA5STRING *base);
40e1051a39Sopenharmony_cistatic int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
41e1051a39Sopenharmony_cistatic int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base);
42e1051a39Sopenharmony_ci
43e1051a39Sopenharmony_ciconst X509V3_EXT_METHOD ossl_v3_name_constraints = {
44e1051a39Sopenharmony_ci    NID_name_constraints, 0,
45e1051a39Sopenharmony_ci    ASN1_ITEM_ref(NAME_CONSTRAINTS),
46e1051a39Sopenharmony_ci    0, 0, 0, 0,
47e1051a39Sopenharmony_ci    0, 0,
48e1051a39Sopenharmony_ci    0, v2i_NAME_CONSTRAINTS,
49e1051a39Sopenharmony_ci    i2r_NAME_CONSTRAINTS, 0,
50e1051a39Sopenharmony_ci    NULL
51e1051a39Sopenharmony_ci};
52e1051a39Sopenharmony_ci
53e1051a39Sopenharmony_ciASN1_SEQUENCE(GENERAL_SUBTREE) = {
54e1051a39Sopenharmony_ci        ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME),
55e1051a39Sopenharmony_ci        ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0),
56e1051a39Sopenharmony_ci        ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1)
57e1051a39Sopenharmony_ci} ASN1_SEQUENCE_END(GENERAL_SUBTREE)
58e1051a39Sopenharmony_ci
59e1051a39Sopenharmony_ciASN1_SEQUENCE(NAME_CONSTRAINTS) = {
60e1051a39Sopenharmony_ci        ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees,
61e1051a39Sopenharmony_ci                                                        GENERAL_SUBTREE, 0),
62e1051a39Sopenharmony_ci        ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees,
63e1051a39Sopenharmony_ci                                                        GENERAL_SUBTREE, 1),
64e1051a39Sopenharmony_ci} ASN1_SEQUENCE_END(NAME_CONSTRAINTS)
65e1051a39Sopenharmony_ci
66e1051a39Sopenharmony_ci
67e1051a39Sopenharmony_ciIMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
68e1051a39Sopenharmony_ciIMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
69e1051a39Sopenharmony_ci
70e1051a39Sopenharmony_ci
71e1051a39Sopenharmony_ci#define IA5_OFFSET_LEN(ia5base, offset) \
72e1051a39Sopenharmony_ci    ((ia5base)->length - ((unsigned char *)(offset) - (ia5base)->data))
73e1051a39Sopenharmony_ci
74e1051a39Sopenharmony_ci/* Like memchr but for ASN1_IA5STRING. Additionally you can specify the
75e1051a39Sopenharmony_ci * starting point to search from
76e1051a39Sopenharmony_ci */
77e1051a39Sopenharmony_ci# define ia5memchr(str, start, c) memchr(start, c, IA5_OFFSET_LEN(str, start))
78e1051a39Sopenharmony_ci
79e1051a39Sopenharmony_ci/* Like memrrchr but for ASN1_IA5STRING */
80e1051a39Sopenharmony_cistatic char *ia5memrchr(ASN1_IA5STRING *str, int c)
81e1051a39Sopenharmony_ci{
82e1051a39Sopenharmony_ci    int i;
83e1051a39Sopenharmony_ci
84e1051a39Sopenharmony_ci    for (i = str->length; i > 0 && str->data[i - 1] != c; i--);
85e1051a39Sopenharmony_ci
86e1051a39Sopenharmony_ci    if (i == 0)
87e1051a39Sopenharmony_ci        return NULL;
88e1051a39Sopenharmony_ci
89e1051a39Sopenharmony_ci    return (char *)&str->data[i - 1];
90e1051a39Sopenharmony_ci}
91e1051a39Sopenharmony_ci
92e1051a39Sopenharmony_ci/*
93e1051a39Sopenharmony_ci * We cannot use strncasecmp here because that applies locale specific rules. It
94e1051a39Sopenharmony_ci * also doesn't work with ASN1_STRINGs that may have embedded NUL characters.
95e1051a39Sopenharmony_ci * For example in Turkish 'I' is not the uppercase character for 'i'. We need to
96e1051a39Sopenharmony_ci * do a simple ASCII case comparison ignoring the locale (that is why we use
97e1051a39Sopenharmony_ci * numeric constants below).
98e1051a39Sopenharmony_ci */
99e1051a39Sopenharmony_cistatic int ia5ncasecmp(const char *s1, const char *s2, size_t n)
100e1051a39Sopenharmony_ci{
101e1051a39Sopenharmony_ci    for (; n > 0; n--, s1++, s2++) {
102e1051a39Sopenharmony_ci        if (*s1 != *s2) {
103e1051a39Sopenharmony_ci            unsigned char c1 = (unsigned char)*s1, c2 = (unsigned char)*s2;
104e1051a39Sopenharmony_ci
105e1051a39Sopenharmony_ci            /* Convert to lower case */
106e1051a39Sopenharmony_ci            if (c1 >= 0x41 /* A */ && c1 <= 0x5A /* Z */)
107e1051a39Sopenharmony_ci                c1 += 0x20;
108e1051a39Sopenharmony_ci            if (c2 >= 0x41 /* A */ && c2 <= 0x5A /* Z */)
109e1051a39Sopenharmony_ci                c2 += 0x20;
110e1051a39Sopenharmony_ci
111e1051a39Sopenharmony_ci            if (c1 == c2)
112e1051a39Sopenharmony_ci                continue;
113e1051a39Sopenharmony_ci
114e1051a39Sopenharmony_ci            if (c1 < c2)
115e1051a39Sopenharmony_ci                return -1;
116e1051a39Sopenharmony_ci
117e1051a39Sopenharmony_ci            /* c1 > c2 */
118e1051a39Sopenharmony_ci            return 1;
119e1051a39Sopenharmony_ci        }
120e1051a39Sopenharmony_ci    }
121e1051a39Sopenharmony_ci
122e1051a39Sopenharmony_ci    return 0;
123e1051a39Sopenharmony_ci}
124e1051a39Sopenharmony_ci
125e1051a39Sopenharmony_cistatic void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
126e1051a39Sopenharmony_ci                                  X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
127e1051a39Sopenharmony_ci{
128e1051a39Sopenharmony_ci    int i;
129e1051a39Sopenharmony_ci    CONF_VALUE tval, *val;
130e1051a39Sopenharmony_ci    STACK_OF(GENERAL_SUBTREE) **ptree = NULL;
131e1051a39Sopenharmony_ci    NAME_CONSTRAINTS *ncons = NULL;
132e1051a39Sopenharmony_ci    GENERAL_SUBTREE *sub = NULL;
133e1051a39Sopenharmony_ci
134e1051a39Sopenharmony_ci    ncons = NAME_CONSTRAINTS_new();
135e1051a39Sopenharmony_ci    if (ncons == NULL)
136e1051a39Sopenharmony_ci        goto memerr;
137e1051a39Sopenharmony_ci    for (i = 0; i < sk_CONF_VALUE_num(nval); i++) {
138e1051a39Sopenharmony_ci        val = sk_CONF_VALUE_value(nval, i);
139e1051a39Sopenharmony_ci        if (strncmp(val->name, "permitted", 9) == 0 && val->name[9]) {
140e1051a39Sopenharmony_ci            ptree = &ncons->permittedSubtrees;
141e1051a39Sopenharmony_ci            tval.name = val->name + 10;
142e1051a39Sopenharmony_ci        } else if (strncmp(val->name, "excluded", 8) == 0 && val->name[8]) {
143e1051a39Sopenharmony_ci            ptree = &ncons->excludedSubtrees;
144e1051a39Sopenharmony_ci            tval.name = val->name + 9;
145e1051a39Sopenharmony_ci        } else {
146e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX);
147e1051a39Sopenharmony_ci            goto err;
148e1051a39Sopenharmony_ci        }
149e1051a39Sopenharmony_ci        tval.value = val->value;
150e1051a39Sopenharmony_ci        sub = GENERAL_SUBTREE_new();
151e1051a39Sopenharmony_ci        if (sub == NULL)
152e1051a39Sopenharmony_ci            goto memerr;
153e1051a39Sopenharmony_ci        if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1))
154e1051a39Sopenharmony_ci            goto err;
155e1051a39Sopenharmony_ci        if (*ptree == NULL)
156e1051a39Sopenharmony_ci            *ptree = sk_GENERAL_SUBTREE_new_null();
157e1051a39Sopenharmony_ci        if (*ptree == NULL || !sk_GENERAL_SUBTREE_push(*ptree, sub))
158e1051a39Sopenharmony_ci            goto memerr;
159e1051a39Sopenharmony_ci        sub = NULL;
160e1051a39Sopenharmony_ci    }
161e1051a39Sopenharmony_ci
162e1051a39Sopenharmony_ci    return ncons;
163e1051a39Sopenharmony_ci
164e1051a39Sopenharmony_ci memerr:
165e1051a39Sopenharmony_ci    ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE);
166e1051a39Sopenharmony_ci err:
167e1051a39Sopenharmony_ci    NAME_CONSTRAINTS_free(ncons);
168e1051a39Sopenharmony_ci    GENERAL_SUBTREE_free(sub);
169e1051a39Sopenharmony_ci
170e1051a39Sopenharmony_ci    return NULL;
171e1051a39Sopenharmony_ci}
172e1051a39Sopenharmony_ci
173e1051a39Sopenharmony_cistatic int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
174e1051a39Sopenharmony_ci                                BIO *bp, int ind)
175e1051a39Sopenharmony_ci{
176e1051a39Sopenharmony_ci    NAME_CONSTRAINTS *ncons = a;
177e1051a39Sopenharmony_ci    do_i2r_name_constraints(method, ncons->permittedSubtrees,
178e1051a39Sopenharmony_ci                            bp, ind, "Permitted");
179e1051a39Sopenharmony_ci    if (ncons->permittedSubtrees && ncons->excludedSubtrees)
180e1051a39Sopenharmony_ci        BIO_puts(bp, "\n");
181e1051a39Sopenharmony_ci    do_i2r_name_constraints(method, ncons->excludedSubtrees,
182e1051a39Sopenharmony_ci                            bp, ind, "Excluded");
183e1051a39Sopenharmony_ci    return 1;
184e1051a39Sopenharmony_ci}
185e1051a39Sopenharmony_ci
186e1051a39Sopenharmony_cistatic int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
187e1051a39Sopenharmony_ci                                   STACK_OF(GENERAL_SUBTREE) *trees,
188e1051a39Sopenharmony_ci                                   BIO *bp, int ind, const char *name)
189e1051a39Sopenharmony_ci{
190e1051a39Sopenharmony_ci    GENERAL_SUBTREE *tree;
191e1051a39Sopenharmony_ci    int i;
192e1051a39Sopenharmony_ci    if (sk_GENERAL_SUBTREE_num(trees) > 0)
193e1051a39Sopenharmony_ci        BIO_printf(bp, "%*s%s:\n", ind, "", name);
194e1051a39Sopenharmony_ci    for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) {
195e1051a39Sopenharmony_ci        if (i > 0)
196e1051a39Sopenharmony_ci            BIO_puts(bp, "\n");
197e1051a39Sopenharmony_ci        tree = sk_GENERAL_SUBTREE_value(trees, i);
198e1051a39Sopenharmony_ci        BIO_printf(bp, "%*s", ind + 2, "");
199e1051a39Sopenharmony_ci        if (tree->base->type == GEN_IPADD)
200e1051a39Sopenharmony_ci            print_nc_ipadd(bp, tree->base->d.ip);
201e1051a39Sopenharmony_ci        else
202e1051a39Sopenharmony_ci            GENERAL_NAME_print(bp, tree->base);
203e1051a39Sopenharmony_ci    }
204e1051a39Sopenharmony_ci    return 1;
205e1051a39Sopenharmony_ci}
206e1051a39Sopenharmony_ci
207e1051a39Sopenharmony_cistatic int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
208e1051a39Sopenharmony_ci{
209e1051a39Sopenharmony_ci    /* ip->length should be 8 or 32 and len1 == len2 == 4 or len1 == len2 == 16 */
210e1051a39Sopenharmony_ci    int len1 = ip->length >= 16 ? 16 : ip->length >= 4 ? 4 : ip->length;
211e1051a39Sopenharmony_ci    int len2 = ip->length - len1;
212e1051a39Sopenharmony_ci    char *ip1 = ossl_ipaddr_to_asc(ip->data, len1);
213e1051a39Sopenharmony_ci    char *ip2 = ossl_ipaddr_to_asc(ip->data + len1, len2);
214e1051a39Sopenharmony_ci    int ret = ip1 != NULL && ip2 != NULL
215e1051a39Sopenharmony_ci        && BIO_printf(bp, "IP:%s/%s", ip1, ip2) > 0;
216e1051a39Sopenharmony_ci
217e1051a39Sopenharmony_ci    OPENSSL_free(ip1);
218e1051a39Sopenharmony_ci    OPENSSL_free(ip2);
219e1051a39Sopenharmony_ci    return ret;
220e1051a39Sopenharmony_ci}
221e1051a39Sopenharmony_ci
222e1051a39Sopenharmony_ci#define NAME_CHECK_MAX (1 << 20)
223e1051a39Sopenharmony_ci
224e1051a39Sopenharmony_cistatic int add_lengths(int *out, int a, int b)
225e1051a39Sopenharmony_ci{
226e1051a39Sopenharmony_ci    /* sk_FOO_num(NULL) returns -1 but is effectively 0 when iterating. */
227e1051a39Sopenharmony_ci    if (a < 0)
228e1051a39Sopenharmony_ci        a = 0;
229e1051a39Sopenharmony_ci    if (b < 0)
230e1051a39Sopenharmony_ci        b = 0;
231e1051a39Sopenharmony_ci
232e1051a39Sopenharmony_ci    if (a > INT_MAX - b)
233e1051a39Sopenharmony_ci        return 0;
234e1051a39Sopenharmony_ci    *out = a + b;
235e1051a39Sopenharmony_ci    return 1;
236e1051a39Sopenharmony_ci}
237e1051a39Sopenharmony_ci
238e1051a39Sopenharmony_ci/*-
239e1051a39Sopenharmony_ci * Check a certificate conforms to a specified set of constraints.
240e1051a39Sopenharmony_ci * Return values:
241e1051a39Sopenharmony_ci *  X509_V_OK: All constraints obeyed.
242e1051a39Sopenharmony_ci *  X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
243e1051a39Sopenharmony_ci *  X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
244e1051a39Sopenharmony_ci *  X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
245e1051a39Sopenharmony_ci *  X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE:  Unsupported constraint type.
246e1051a39Sopenharmony_ci *  X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
247e1051a39Sopenharmony_ci *  X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
248e1051a39Sopenharmony_ci */
249e1051a39Sopenharmony_ci
250e1051a39Sopenharmony_ciint NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
251e1051a39Sopenharmony_ci{
252e1051a39Sopenharmony_ci    int r, i, name_count, constraint_count;
253e1051a39Sopenharmony_ci    X509_NAME *nm;
254e1051a39Sopenharmony_ci
255e1051a39Sopenharmony_ci    nm = X509_get_subject_name(x);
256e1051a39Sopenharmony_ci
257e1051a39Sopenharmony_ci    /*
258e1051a39Sopenharmony_ci     * Guard against certificates with an excessive number of names or
259e1051a39Sopenharmony_ci     * constraints causing a computationally expensive name constraints check.
260e1051a39Sopenharmony_ci     */
261e1051a39Sopenharmony_ci    if (!add_lengths(&name_count, X509_NAME_entry_count(nm),
262e1051a39Sopenharmony_ci                     sk_GENERAL_NAME_num(x->altname))
263e1051a39Sopenharmony_ci        || !add_lengths(&constraint_count,
264e1051a39Sopenharmony_ci                        sk_GENERAL_SUBTREE_num(nc->permittedSubtrees),
265e1051a39Sopenharmony_ci                        sk_GENERAL_SUBTREE_num(nc->excludedSubtrees))
266e1051a39Sopenharmony_ci        || (name_count > 0 && constraint_count > NAME_CHECK_MAX / name_count))
267e1051a39Sopenharmony_ci        return X509_V_ERR_UNSPECIFIED;
268e1051a39Sopenharmony_ci
269e1051a39Sopenharmony_ci    if (X509_NAME_entry_count(nm) > 0) {
270e1051a39Sopenharmony_ci        GENERAL_NAME gntmp;
271e1051a39Sopenharmony_ci        gntmp.type = GEN_DIRNAME;
272e1051a39Sopenharmony_ci        gntmp.d.directoryName = nm;
273e1051a39Sopenharmony_ci
274e1051a39Sopenharmony_ci        r = nc_match(&gntmp, nc);
275e1051a39Sopenharmony_ci
276e1051a39Sopenharmony_ci        if (r != X509_V_OK)
277e1051a39Sopenharmony_ci            return r;
278e1051a39Sopenharmony_ci
279e1051a39Sopenharmony_ci        gntmp.type = GEN_EMAIL;
280e1051a39Sopenharmony_ci
281e1051a39Sopenharmony_ci        /* Process any email address attributes in subject name */
282e1051a39Sopenharmony_ci
283e1051a39Sopenharmony_ci        for (i = -1;;) {
284e1051a39Sopenharmony_ci            const X509_NAME_ENTRY *ne;
285e1051a39Sopenharmony_ci
286e1051a39Sopenharmony_ci            i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i);
287e1051a39Sopenharmony_ci            if (i == -1)
288e1051a39Sopenharmony_ci                break;
289e1051a39Sopenharmony_ci            ne = X509_NAME_get_entry(nm, i);
290e1051a39Sopenharmony_ci            gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
291e1051a39Sopenharmony_ci            if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
292e1051a39Sopenharmony_ci                return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
293e1051a39Sopenharmony_ci
294e1051a39Sopenharmony_ci            r = nc_match(&gntmp, nc);
295e1051a39Sopenharmony_ci
296e1051a39Sopenharmony_ci            if (r != X509_V_OK)
297e1051a39Sopenharmony_ci                return r;
298e1051a39Sopenharmony_ci        }
299e1051a39Sopenharmony_ci
300e1051a39Sopenharmony_ci    }
301e1051a39Sopenharmony_ci
302e1051a39Sopenharmony_ci    for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) {
303e1051a39Sopenharmony_ci        GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i);
304e1051a39Sopenharmony_ci        r = nc_match(gen, nc);
305e1051a39Sopenharmony_ci        if (r != X509_V_OK)
306e1051a39Sopenharmony_ci            return r;
307e1051a39Sopenharmony_ci    }
308e1051a39Sopenharmony_ci
309e1051a39Sopenharmony_ci    return X509_V_OK;
310e1051a39Sopenharmony_ci
311e1051a39Sopenharmony_ci}
312e1051a39Sopenharmony_ci
313e1051a39Sopenharmony_cistatic int cn2dnsid(ASN1_STRING *cn, unsigned char **dnsid, size_t *idlen)
314e1051a39Sopenharmony_ci{
315e1051a39Sopenharmony_ci    int utf8_length;
316e1051a39Sopenharmony_ci    unsigned char *utf8_value;
317e1051a39Sopenharmony_ci    int i;
318e1051a39Sopenharmony_ci    int isdnsname = 0;
319e1051a39Sopenharmony_ci
320e1051a39Sopenharmony_ci    /* Don't leave outputs uninitialized */
321e1051a39Sopenharmony_ci    *dnsid = NULL;
322e1051a39Sopenharmony_ci    *idlen = 0;
323e1051a39Sopenharmony_ci
324e1051a39Sopenharmony_ci    /*-
325e1051a39Sopenharmony_ci     * Per RFC 6125, DNS-IDs representing internationalized domain names appear
326e1051a39Sopenharmony_ci     * in certificates in A-label encoded form:
327e1051a39Sopenharmony_ci     *
328e1051a39Sopenharmony_ci     *   https://tools.ietf.org/html/rfc6125#section-6.4.2
329e1051a39Sopenharmony_ci     *
330e1051a39Sopenharmony_ci     * The same applies to CNs which are intended to represent DNS names.
331e1051a39Sopenharmony_ci     * However, while in the SAN DNS-IDs are IA5Strings, as CNs they may be
332e1051a39Sopenharmony_ci     * needlessly encoded in 16-bit Unicode.  We perform a conversion to UTF-8
333e1051a39Sopenharmony_ci     * to ensure that we get an ASCII representation of any CNs that are
334e1051a39Sopenharmony_ci     * representable as ASCII, but just not encoded as ASCII.  The UTF-8 form
335e1051a39Sopenharmony_ci     * may contain some non-ASCII octets, and that's fine, such CNs are not
336e1051a39Sopenharmony_ci     * valid legacy DNS names.
337e1051a39Sopenharmony_ci     *
338e1051a39Sopenharmony_ci     * Note, 'int' is the return type of ASN1_STRING_to_UTF8() so that's what
339e1051a39Sopenharmony_ci     * we must use for 'utf8_length'.
340e1051a39Sopenharmony_ci     */
341e1051a39Sopenharmony_ci    if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, cn)) < 0)
342e1051a39Sopenharmony_ci        return X509_V_ERR_OUT_OF_MEM;
343e1051a39Sopenharmony_ci
344e1051a39Sopenharmony_ci    /*
345e1051a39Sopenharmony_ci     * Some certificates have had names that include a *trailing* NUL byte.
346e1051a39Sopenharmony_ci     * Remove these harmless NUL characters. They would otherwise yield false
347e1051a39Sopenharmony_ci     * alarms with the following embedded NUL check.
348e1051a39Sopenharmony_ci     */
349e1051a39Sopenharmony_ci    while (utf8_length > 0 && utf8_value[utf8_length - 1] == '\0')
350e1051a39Sopenharmony_ci        --utf8_length;
351e1051a39Sopenharmony_ci
352e1051a39Sopenharmony_ci    /* Reject *embedded* NULs */
353e1051a39Sopenharmony_ci    if (memchr(utf8_value, 0, utf8_length) != NULL) {
354e1051a39Sopenharmony_ci        OPENSSL_free(utf8_value);
355e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
356e1051a39Sopenharmony_ci    }
357e1051a39Sopenharmony_ci
358e1051a39Sopenharmony_ci    /*
359e1051a39Sopenharmony_ci     * XXX: Deviation from strict DNS name syntax, also check names with '_'
360e1051a39Sopenharmony_ci     * Check DNS name syntax, any '-' or '.' must be internal,
361e1051a39Sopenharmony_ci     * and on either side of each '.' we can't have a '-' or '.'.
362e1051a39Sopenharmony_ci     *
363e1051a39Sopenharmony_ci     * If the name has just one label, we don't consider it a DNS name.  This
364e1051a39Sopenharmony_ci     * means that "CN=sometld" cannot be precluded by DNS name constraints, but
365e1051a39Sopenharmony_ci     * that is not a problem.
366e1051a39Sopenharmony_ci     */
367e1051a39Sopenharmony_ci    for (i = 0; i < utf8_length; ++i) {
368e1051a39Sopenharmony_ci        unsigned char c = utf8_value[i];
369e1051a39Sopenharmony_ci
370e1051a39Sopenharmony_ci        if ((c >= 'a' && c <= 'z')
371e1051a39Sopenharmony_ci            || (c >= 'A' && c <= 'Z')
372e1051a39Sopenharmony_ci            || (c >= '0' && c <= '9')
373e1051a39Sopenharmony_ci            || c == '_')
374e1051a39Sopenharmony_ci            continue;
375e1051a39Sopenharmony_ci
376e1051a39Sopenharmony_ci        /* Dot and hyphen cannot be first or last. */
377e1051a39Sopenharmony_ci        if (i > 0 && i < utf8_length - 1) {
378e1051a39Sopenharmony_ci            if (c == '-')
379e1051a39Sopenharmony_ci                continue;
380e1051a39Sopenharmony_ci            /*
381e1051a39Sopenharmony_ci             * Next to a dot the preceding and following characters must not be
382e1051a39Sopenharmony_ci             * another dot or a hyphen.  Otherwise, record that the name is
383e1051a39Sopenharmony_ci             * plausible, since it has two or more labels.
384e1051a39Sopenharmony_ci             */
385e1051a39Sopenharmony_ci            if (c == '.'
386e1051a39Sopenharmony_ci                && utf8_value[i + 1] != '.'
387e1051a39Sopenharmony_ci                && utf8_value[i - 1] != '-'
388e1051a39Sopenharmony_ci                && utf8_value[i + 1] != '-') {
389e1051a39Sopenharmony_ci                isdnsname = 1;
390e1051a39Sopenharmony_ci                continue;
391e1051a39Sopenharmony_ci            }
392e1051a39Sopenharmony_ci        }
393e1051a39Sopenharmony_ci        isdnsname = 0;
394e1051a39Sopenharmony_ci        break;
395e1051a39Sopenharmony_ci    }
396e1051a39Sopenharmony_ci
397e1051a39Sopenharmony_ci    if (isdnsname) {
398e1051a39Sopenharmony_ci        *dnsid = utf8_value;
399e1051a39Sopenharmony_ci        *idlen = (size_t)utf8_length;
400e1051a39Sopenharmony_ci        return X509_V_OK;
401e1051a39Sopenharmony_ci    }
402e1051a39Sopenharmony_ci    OPENSSL_free(utf8_value);
403e1051a39Sopenharmony_ci    return X509_V_OK;
404e1051a39Sopenharmony_ci}
405e1051a39Sopenharmony_ci
406e1051a39Sopenharmony_ci/*
407e1051a39Sopenharmony_ci * Check CN against DNS-ID name constraints.
408e1051a39Sopenharmony_ci */
409e1051a39Sopenharmony_ciint NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc)
410e1051a39Sopenharmony_ci{
411e1051a39Sopenharmony_ci    int r, i;
412e1051a39Sopenharmony_ci    const X509_NAME *nm = X509_get_subject_name(x);
413e1051a39Sopenharmony_ci    ASN1_STRING stmp;
414e1051a39Sopenharmony_ci    GENERAL_NAME gntmp;
415e1051a39Sopenharmony_ci
416e1051a39Sopenharmony_ci    stmp.flags = 0;
417e1051a39Sopenharmony_ci    stmp.type = V_ASN1_IA5STRING;
418e1051a39Sopenharmony_ci    gntmp.type = GEN_DNS;
419e1051a39Sopenharmony_ci    gntmp.d.dNSName = &stmp;
420e1051a39Sopenharmony_ci
421e1051a39Sopenharmony_ci    /* Process any commonName attributes in subject name */
422e1051a39Sopenharmony_ci
423e1051a39Sopenharmony_ci    for (i = -1;;) {
424e1051a39Sopenharmony_ci        X509_NAME_ENTRY *ne;
425e1051a39Sopenharmony_ci        ASN1_STRING *cn;
426e1051a39Sopenharmony_ci        unsigned char *idval;
427e1051a39Sopenharmony_ci        size_t idlen;
428e1051a39Sopenharmony_ci
429e1051a39Sopenharmony_ci        i = X509_NAME_get_index_by_NID(nm, NID_commonName, i);
430e1051a39Sopenharmony_ci        if (i == -1)
431e1051a39Sopenharmony_ci            break;
432e1051a39Sopenharmony_ci        ne = X509_NAME_get_entry(nm, i);
433e1051a39Sopenharmony_ci        cn = X509_NAME_ENTRY_get_data(ne);
434e1051a39Sopenharmony_ci
435e1051a39Sopenharmony_ci        /* Only process attributes that look like host names */
436e1051a39Sopenharmony_ci        if ((r = cn2dnsid(cn, &idval, &idlen)) != X509_V_OK)
437e1051a39Sopenharmony_ci            return r;
438e1051a39Sopenharmony_ci        if (idlen == 0)
439e1051a39Sopenharmony_ci            continue;
440e1051a39Sopenharmony_ci
441e1051a39Sopenharmony_ci        stmp.length = idlen;
442e1051a39Sopenharmony_ci        stmp.data = idval;
443e1051a39Sopenharmony_ci        r = nc_match(&gntmp, nc);
444e1051a39Sopenharmony_ci        OPENSSL_free(idval);
445e1051a39Sopenharmony_ci        if (r != X509_V_OK)
446e1051a39Sopenharmony_ci            return r;
447e1051a39Sopenharmony_ci    }
448e1051a39Sopenharmony_ci    return X509_V_OK;
449e1051a39Sopenharmony_ci}
450e1051a39Sopenharmony_ci
451e1051a39Sopenharmony_ci/*
452e1051a39Sopenharmony_ci * Return nonzero if the GeneralSubtree has valid 'minimum' field
453e1051a39Sopenharmony_ci * (must be absent or 0) and valid 'maximum' field (must be absent).
454e1051a39Sopenharmony_ci */
455e1051a39Sopenharmony_cistatic int nc_minmax_valid(GENERAL_SUBTREE *sub) {
456e1051a39Sopenharmony_ci    BIGNUM *bn = NULL;
457e1051a39Sopenharmony_ci    int ok = 1;
458e1051a39Sopenharmony_ci
459e1051a39Sopenharmony_ci    if (sub->maximum)
460e1051a39Sopenharmony_ci        ok = 0;
461e1051a39Sopenharmony_ci
462e1051a39Sopenharmony_ci    if (sub->minimum) {
463e1051a39Sopenharmony_ci        bn = ASN1_INTEGER_to_BN(sub->minimum, NULL);
464e1051a39Sopenharmony_ci        if (bn == NULL || !BN_is_zero(bn))
465e1051a39Sopenharmony_ci            ok = 0;
466e1051a39Sopenharmony_ci        BN_free(bn);
467e1051a39Sopenharmony_ci    }
468e1051a39Sopenharmony_ci
469e1051a39Sopenharmony_ci    return ok;
470e1051a39Sopenharmony_ci}
471e1051a39Sopenharmony_ci
472e1051a39Sopenharmony_cistatic int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
473e1051a39Sopenharmony_ci{
474e1051a39Sopenharmony_ci    GENERAL_SUBTREE *sub;
475e1051a39Sopenharmony_ci    int i, r, match = 0;
476e1051a39Sopenharmony_ci    int effective_type = gen->type;
477e1051a39Sopenharmony_ci
478e1051a39Sopenharmony_ci    /*
479e1051a39Sopenharmony_ci     * We need to compare not gen->type field but an "effective" type because
480e1051a39Sopenharmony_ci     * the otherName field may contain EAI email address treated specially
481e1051a39Sopenharmony_ci     * according to RFC 8398, section 6
482e1051a39Sopenharmony_ci     */
483e1051a39Sopenharmony_ci    if (effective_type == GEN_OTHERNAME &&
484e1051a39Sopenharmony_ci        (OBJ_obj2nid(gen->d.otherName->type_id) == NID_id_on_SmtpUTF8Mailbox)) {
485e1051a39Sopenharmony_ci        effective_type = GEN_EMAIL;
486e1051a39Sopenharmony_ci    }
487e1051a39Sopenharmony_ci
488e1051a39Sopenharmony_ci    /*
489e1051a39Sopenharmony_ci     * Permitted subtrees: if any subtrees exist of matching the type at
490e1051a39Sopenharmony_ci     * least one subtree must match.
491e1051a39Sopenharmony_ci     */
492e1051a39Sopenharmony_ci
493e1051a39Sopenharmony_ci    for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
494e1051a39Sopenharmony_ci        sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
495e1051a39Sopenharmony_ci        if (effective_type != sub->base->type
496e1051a39Sopenharmony_ci            || (effective_type == GEN_OTHERNAME &&
497e1051a39Sopenharmony_ci                OBJ_cmp(gen->d.otherName->type_id,
498e1051a39Sopenharmony_ci                        sub->base->d.otherName->type_id) != 0))
499e1051a39Sopenharmony_ci            continue;
500e1051a39Sopenharmony_ci        if (!nc_minmax_valid(sub))
501e1051a39Sopenharmony_ci            return X509_V_ERR_SUBTREE_MINMAX;
502e1051a39Sopenharmony_ci        /* If we already have a match don't bother trying any more */
503e1051a39Sopenharmony_ci        if (match == 2)
504e1051a39Sopenharmony_ci            continue;
505e1051a39Sopenharmony_ci        if (match == 0)
506e1051a39Sopenharmony_ci            match = 1;
507e1051a39Sopenharmony_ci        r = nc_match_single(effective_type, gen, sub->base);
508e1051a39Sopenharmony_ci        if (r == X509_V_OK)
509e1051a39Sopenharmony_ci            match = 2;
510e1051a39Sopenharmony_ci        else if (r != X509_V_ERR_PERMITTED_VIOLATION)
511e1051a39Sopenharmony_ci            return r;
512e1051a39Sopenharmony_ci    }
513e1051a39Sopenharmony_ci
514e1051a39Sopenharmony_ci    if (match == 1)
515e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
516e1051a39Sopenharmony_ci
517e1051a39Sopenharmony_ci    /* Excluded subtrees: must not match any of these */
518e1051a39Sopenharmony_ci
519e1051a39Sopenharmony_ci    for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
520e1051a39Sopenharmony_ci        sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
521e1051a39Sopenharmony_ci        if (effective_type != sub->base->type
522e1051a39Sopenharmony_ci            || (effective_type == GEN_OTHERNAME &&
523e1051a39Sopenharmony_ci                OBJ_cmp(gen->d.otherName->type_id,
524e1051a39Sopenharmony_ci                        sub->base->d.otherName->type_id) != 0))
525e1051a39Sopenharmony_ci            continue;
526e1051a39Sopenharmony_ci        if (!nc_minmax_valid(sub))
527e1051a39Sopenharmony_ci            return X509_V_ERR_SUBTREE_MINMAX;
528e1051a39Sopenharmony_ci
529e1051a39Sopenharmony_ci        r = nc_match_single(effective_type, gen, sub->base);
530e1051a39Sopenharmony_ci        if (r == X509_V_OK)
531e1051a39Sopenharmony_ci            return X509_V_ERR_EXCLUDED_VIOLATION;
532e1051a39Sopenharmony_ci        else if (r != X509_V_ERR_PERMITTED_VIOLATION)
533e1051a39Sopenharmony_ci            return r;
534e1051a39Sopenharmony_ci
535e1051a39Sopenharmony_ci    }
536e1051a39Sopenharmony_ci
537e1051a39Sopenharmony_ci    return X509_V_OK;
538e1051a39Sopenharmony_ci
539e1051a39Sopenharmony_ci}
540e1051a39Sopenharmony_ci
541e1051a39Sopenharmony_cistatic int nc_match_single(int effective_type, GENERAL_NAME *gen,
542e1051a39Sopenharmony_ci                           GENERAL_NAME *base)
543e1051a39Sopenharmony_ci{
544e1051a39Sopenharmony_ci    switch (gen->type) {
545e1051a39Sopenharmony_ci    case GEN_OTHERNAME:
546e1051a39Sopenharmony_ci        switch (effective_type) {
547e1051a39Sopenharmony_ci        case GEN_EMAIL:
548e1051a39Sopenharmony_ci            /*
549e1051a39Sopenharmony_ci             * We are here only when we have SmtpUTF8 name,
550e1051a39Sopenharmony_ci             * so we match the value of othername with base->d.rfc822Name
551e1051a39Sopenharmony_ci             */
552e1051a39Sopenharmony_ci            return nc_email_eai(gen->d.otherName->value, base->d.rfc822Name);
553e1051a39Sopenharmony_ci
554e1051a39Sopenharmony_ci        default:
555e1051a39Sopenharmony_ci            return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
556e1051a39Sopenharmony_ci        }
557e1051a39Sopenharmony_ci
558e1051a39Sopenharmony_ci    case GEN_DIRNAME:
559e1051a39Sopenharmony_ci        return nc_dn(gen->d.directoryName, base->d.directoryName);
560e1051a39Sopenharmony_ci
561e1051a39Sopenharmony_ci    case GEN_DNS:
562e1051a39Sopenharmony_ci        return nc_dns(gen->d.dNSName, base->d.dNSName);
563e1051a39Sopenharmony_ci
564e1051a39Sopenharmony_ci    case GEN_EMAIL:
565e1051a39Sopenharmony_ci        return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
566e1051a39Sopenharmony_ci
567e1051a39Sopenharmony_ci    case GEN_URI:
568e1051a39Sopenharmony_ci        return nc_uri(gen->d.uniformResourceIdentifier,
569e1051a39Sopenharmony_ci                      base->d.uniformResourceIdentifier);
570e1051a39Sopenharmony_ci
571e1051a39Sopenharmony_ci    case GEN_IPADD:
572e1051a39Sopenharmony_ci        return nc_ip(gen->d.iPAddress, base->d.iPAddress);
573e1051a39Sopenharmony_ci
574e1051a39Sopenharmony_ci    default:
575e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
576e1051a39Sopenharmony_ci    }
577e1051a39Sopenharmony_ci
578e1051a39Sopenharmony_ci}
579e1051a39Sopenharmony_ci
580e1051a39Sopenharmony_ci/*
581e1051a39Sopenharmony_ci * directoryName name constraint matching. The canonical encoding of
582e1051a39Sopenharmony_ci * X509_NAME makes this comparison easy. It is matched if the subtree is a
583e1051a39Sopenharmony_ci * subset of the name.
584e1051a39Sopenharmony_ci */
585e1051a39Sopenharmony_ci
586e1051a39Sopenharmony_cistatic int nc_dn(const X509_NAME *nm, const X509_NAME *base)
587e1051a39Sopenharmony_ci{
588e1051a39Sopenharmony_ci    /* Ensure canonical encodings are up to date.  */
589e1051a39Sopenharmony_ci    if (nm->modified && i2d_X509_NAME(nm, NULL) < 0)
590e1051a39Sopenharmony_ci        return X509_V_ERR_OUT_OF_MEM;
591e1051a39Sopenharmony_ci    if (base->modified && i2d_X509_NAME(base, NULL) < 0)
592e1051a39Sopenharmony_ci        return X509_V_ERR_OUT_OF_MEM;
593e1051a39Sopenharmony_ci    if (base->canon_enclen > nm->canon_enclen)
594e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
595e1051a39Sopenharmony_ci    if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
596e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
597e1051a39Sopenharmony_ci    return X509_V_OK;
598e1051a39Sopenharmony_ci}
599e1051a39Sopenharmony_ci
600e1051a39Sopenharmony_cistatic int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
601e1051a39Sopenharmony_ci{
602e1051a39Sopenharmony_ci    char *baseptr = (char *)base->data;
603e1051a39Sopenharmony_ci    char *dnsptr = (char *)dns->data;
604e1051a39Sopenharmony_ci
605e1051a39Sopenharmony_ci    /* Empty matches everything */
606e1051a39Sopenharmony_ci    if (base->length == 0)
607e1051a39Sopenharmony_ci        return X509_V_OK;
608e1051a39Sopenharmony_ci
609e1051a39Sopenharmony_ci    if (dns->length < base->length)
610e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
611e1051a39Sopenharmony_ci
612e1051a39Sopenharmony_ci    /*
613e1051a39Sopenharmony_ci     * Otherwise can add zero or more components on the left so compare RHS
614e1051a39Sopenharmony_ci     * and if dns is longer and expect '.' as preceding character.
615e1051a39Sopenharmony_ci     */
616e1051a39Sopenharmony_ci    if (dns->length > base->length) {
617e1051a39Sopenharmony_ci        dnsptr += dns->length - base->length;
618e1051a39Sopenharmony_ci        if (*baseptr != '.' && dnsptr[-1] != '.')
619e1051a39Sopenharmony_ci            return X509_V_ERR_PERMITTED_VIOLATION;
620e1051a39Sopenharmony_ci    }
621e1051a39Sopenharmony_ci
622e1051a39Sopenharmony_ci    if (ia5ncasecmp(baseptr, dnsptr, base->length))
623e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
624e1051a39Sopenharmony_ci
625e1051a39Sopenharmony_ci    return X509_V_OK;
626e1051a39Sopenharmony_ci
627e1051a39Sopenharmony_ci}
628e1051a39Sopenharmony_ci
629e1051a39Sopenharmony_ci/*
630e1051a39Sopenharmony_ci * This function implements comparison between ASCII/U-label in emltype
631e1051a39Sopenharmony_ci * and A-label in base according to RFC 8398, section 6.
632e1051a39Sopenharmony_ci * Convert base to U-label and ASCII-parts of domain names, for base
633e1051a39Sopenharmony_ci * Octet-to-octet comparison of `emltype` and `base` hostname parts
634e1051a39Sopenharmony_ci * (ASCII-parts should be compared in case-insensitive manner)
635e1051a39Sopenharmony_ci */
636e1051a39Sopenharmony_cistatic int nc_email_eai(ASN1_TYPE *emltype, ASN1_IA5STRING *base)
637e1051a39Sopenharmony_ci{
638e1051a39Sopenharmony_ci    ASN1_UTF8STRING *eml;
639e1051a39Sopenharmony_ci    char *baseptr = NULL;
640e1051a39Sopenharmony_ci    const char *emlptr;
641e1051a39Sopenharmony_ci    const char *emlat;
642e1051a39Sopenharmony_ci    char ulabel[256];
643e1051a39Sopenharmony_ci    size_t size = sizeof(ulabel) - 1;
644e1051a39Sopenharmony_ci    int ret = X509_V_OK;
645e1051a39Sopenharmony_ci    size_t emlhostlen;
646e1051a39Sopenharmony_ci
647e1051a39Sopenharmony_ci    /* We do not accept embedded NUL characters */
648e1051a39Sopenharmony_ci    if (base->length > 0 && memchr(base->data, 0, base->length) != NULL)
649e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
650e1051a39Sopenharmony_ci
651e1051a39Sopenharmony_ci    /* 'base' may not be NUL terminated. Create a copy that is */
652e1051a39Sopenharmony_ci    baseptr = OPENSSL_strndup((char *)base->data, base->length);
653e1051a39Sopenharmony_ci    if (baseptr == NULL)
654e1051a39Sopenharmony_ci        return X509_V_ERR_OUT_OF_MEM;
655e1051a39Sopenharmony_ci
656e1051a39Sopenharmony_ci    if (emltype->type != V_ASN1_UTF8STRING) {
657e1051a39Sopenharmony_ci        ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
658e1051a39Sopenharmony_ci        goto end;
659e1051a39Sopenharmony_ci    }
660e1051a39Sopenharmony_ci
661e1051a39Sopenharmony_ci    eml = emltype->value.utf8string;
662e1051a39Sopenharmony_ci    emlptr = (char *)eml->data;
663e1051a39Sopenharmony_ci    emlat = ia5memrchr(eml, '@');
664e1051a39Sopenharmony_ci
665e1051a39Sopenharmony_ci    if (emlat == NULL) {
666e1051a39Sopenharmony_ci        ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
667e1051a39Sopenharmony_ci        goto end;
668e1051a39Sopenharmony_ci    }
669e1051a39Sopenharmony_ci
670e1051a39Sopenharmony_ci    memset(ulabel, 0, sizeof(ulabel));
671e1051a39Sopenharmony_ci    /* Special case: initial '.' is RHS match */
672e1051a39Sopenharmony_ci    if (*baseptr == '.') {
673e1051a39Sopenharmony_ci        ulabel[0] = '.';
674e1051a39Sopenharmony_ci        size -= 1;
675e1051a39Sopenharmony_ci        if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0) {
676e1051a39Sopenharmony_ci            ret = X509_V_ERR_UNSPECIFIED;
677e1051a39Sopenharmony_ci            goto end;
678e1051a39Sopenharmony_ci        }
679e1051a39Sopenharmony_ci
680e1051a39Sopenharmony_ci        if ((size_t)eml->length > strlen(ulabel)) {
681e1051a39Sopenharmony_ci            emlptr += eml->length - (strlen(ulabel));
682e1051a39Sopenharmony_ci            /* X509_V_OK */
683e1051a39Sopenharmony_ci            if (ia5ncasecmp(ulabel, emlptr, strlen(ulabel)) == 0)
684e1051a39Sopenharmony_ci                goto end;
685e1051a39Sopenharmony_ci        }
686e1051a39Sopenharmony_ci        ret = X509_V_ERR_PERMITTED_VIOLATION;
687e1051a39Sopenharmony_ci        goto end;
688e1051a39Sopenharmony_ci    }
689e1051a39Sopenharmony_ci
690e1051a39Sopenharmony_ci    if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0) {
691e1051a39Sopenharmony_ci        ret = X509_V_ERR_UNSPECIFIED;
692e1051a39Sopenharmony_ci        goto end;
693e1051a39Sopenharmony_ci    }
694e1051a39Sopenharmony_ci    /* Just have hostname left to match: case insensitive */
695e1051a39Sopenharmony_ci    emlptr = emlat + 1;
696e1051a39Sopenharmony_ci    emlhostlen = IA5_OFFSET_LEN(eml, emlptr);
697e1051a39Sopenharmony_ci    if (emlhostlen != strlen(ulabel)
698e1051a39Sopenharmony_ci            || ia5ncasecmp(ulabel, emlptr, emlhostlen) != 0) {
699e1051a39Sopenharmony_ci        ret = X509_V_ERR_PERMITTED_VIOLATION;
700e1051a39Sopenharmony_ci        goto end;
701e1051a39Sopenharmony_ci    }
702e1051a39Sopenharmony_ci
703e1051a39Sopenharmony_ci end:
704e1051a39Sopenharmony_ci    OPENSSL_free(baseptr);
705e1051a39Sopenharmony_ci    return ret;
706e1051a39Sopenharmony_ci}
707e1051a39Sopenharmony_ci
708e1051a39Sopenharmony_cistatic int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
709e1051a39Sopenharmony_ci{
710e1051a39Sopenharmony_ci    const char *baseptr = (char *)base->data;
711e1051a39Sopenharmony_ci    const char *emlptr = (char *)eml->data;
712e1051a39Sopenharmony_ci    const char *baseat = ia5memrchr(base, '@');
713e1051a39Sopenharmony_ci    const char *emlat = ia5memrchr(eml, '@');
714e1051a39Sopenharmony_ci    size_t basehostlen, emlhostlen;
715e1051a39Sopenharmony_ci
716e1051a39Sopenharmony_ci    if (!emlat)
717e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
718e1051a39Sopenharmony_ci    /* Special case: initial '.' is RHS match */
719e1051a39Sopenharmony_ci    if (!baseat && base->length > 0 && (*baseptr == '.')) {
720e1051a39Sopenharmony_ci        if (eml->length > base->length) {
721e1051a39Sopenharmony_ci            emlptr += eml->length - base->length;
722e1051a39Sopenharmony_ci            if (ia5ncasecmp(baseptr, emlptr, base->length) == 0)
723e1051a39Sopenharmony_ci                return X509_V_OK;
724e1051a39Sopenharmony_ci        }
725e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
726e1051a39Sopenharmony_ci    }
727e1051a39Sopenharmony_ci
728e1051a39Sopenharmony_ci    /* If we have anything before '@' match local part */
729e1051a39Sopenharmony_ci
730e1051a39Sopenharmony_ci    if (baseat) {
731e1051a39Sopenharmony_ci        if (baseat != baseptr) {
732e1051a39Sopenharmony_ci            if ((baseat - baseptr) != (emlat - emlptr))
733e1051a39Sopenharmony_ci                return X509_V_ERR_PERMITTED_VIOLATION;
734e1051a39Sopenharmony_ci            if (memchr(baseptr, 0, baseat - baseptr) ||
735e1051a39Sopenharmony_ci                memchr(emlptr, 0, emlat - emlptr))
736e1051a39Sopenharmony_ci                return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
737e1051a39Sopenharmony_ci            /* Case sensitive match of local part */
738e1051a39Sopenharmony_ci            if (strncmp(baseptr, emlptr, emlat - emlptr))
739e1051a39Sopenharmony_ci                return X509_V_ERR_PERMITTED_VIOLATION;
740e1051a39Sopenharmony_ci        }
741e1051a39Sopenharmony_ci        /* Position base after '@' */
742e1051a39Sopenharmony_ci        baseptr = baseat + 1;
743e1051a39Sopenharmony_ci    }
744e1051a39Sopenharmony_ci    emlptr = emlat + 1;
745e1051a39Sopenharmony_ci    basehostlen = IA5_OFFSET_LEN(base, baseptr);
746e1051a39Sopenharmony_ci    emlhostlen = IA5_OFFSET_LEN(eml, emlptr);
747e1051a39Sopenharmony_ci    /* Just have hostname left to match: case insensitive */
748e1051a39Sopenharmony_ci    if (basehostlen != emlhostlen || ia5ncasecmp(baseptr, emlptr, emlhostlen))
749e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
750e1051a39Sopenharmony_ci
751e1051a39Sopenharmony_ci    return X509_V_OK;
752e1051a39Sopenharmony_ci
753e1051a39Sopenharmony_ci}
754e1051a39Sopenharmony_ci
755e1051a39Sopenharmony_cistatic int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
756e1051a39Sopenharmony_ci{
757e1051a39Sopenharmony_ci    const char *baseptr = (char *)base->data;
758e1051a39Sopenharmony_ci    const char *hostptr = (char *)uri->data;
759e1051a39Sopenharmony_ci    const char *p = ia5memchr(uri, (char *)uri->data, ':');
760e1051a39Sopenharmony_ci    int hostlen;
761e1051a39Sopenharmony_ci
762e1051a39Sopenharmony_ci    /* Check for foo:// and skip past it */
763e1051a39Sopenharmony_ci    if (p == NULL
764e1051a39Sopenharmony_ci            || IA5_OFFSET_LEN(uri, p) < 3
765e1051a39Sopenharmony_ci            || p[1] != '/'
766e1051a39Sopenharmony_ci            || p[2] != '/')
767e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
768e1051a39Sopenharmony_ci    hostptr = p + 3;
769e1051a39Sopenharmony_ci
770e1051a39Sopenharmony_ci    /* Determine length of hostname part of URI */
771e1051a39Sopenharmony_ci
772e1051a39Sopenharmony_ci    /* Look for a port indicator as end of hostname first */
773e1051a39Sopenharmony_ci
774e1051a39Sopenharmony_ci    p = ia5memchr(uri, hostptr, ':');
775e1051a39Sopenharmony_ci    /* Otherwise look for trailing slash */
776e1051a39Sopenharmony_ci    if (p == NULL)
777e1051a39Sopenharmony_ci        p = ia5memchr(uri, hostptr, '/');
778e1051a39Sopenharmony_ci
779e1051a39Sopenharmony_ci    if (p == NULL)
780e1051a39Sopenharmony_ci        hostlen = IA5_OFFSET_LEN(uri, hostptr);
781e1051a39Sopenharmony_ci    else
782e1051a39Sopenharmony_ci        hostlen = p - hostptr;
783e1051a39Sopenharmony_ci
784e1051a39Sopenharmony_ci    if (hostlen == 0)
785e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
786e1051a39Sopenharmony_ci
787e1051a39Sopenharmony_ci    /* Special case: initial '.' is RHS match */
788e1051a39Sopenharmony_ci    if (base->length > 0 && *baseptr == '.') {
789e1051a39Sopenharmony_ci        if (hostlen > base->length) {
790e1051a39Sopenharmony_ci            p = hostptr + hostlen - base->length;
791e1051a39Sopenharmony_ci            if (ia5ncasecmp(p, baseptr, base->length) == 0)
792e1051a39Sopenharmony_ci                return X509_V_OK;
793e1051a39Sopenharmony_ci        }
794e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
795e1051a39Sopenharmony_ci    }
796e1051a39Sopenharmony_ci
797e1051a39Sopenharmony_ci    if ((base->length != (int)hostlen)
798e1051a39Sopenharmony_ci        || ia5ncasecmp(hostptr, baseptr, hostlen))
799e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
800e1051a39Sopenharmony_ci
801e1051a39Sopenharmony_ci    return X509_V_OK;
802e1051a39Sopenharmony_ci
803e1051a39Sopenharmony_ci}
804e1051a39Sopenharmony_ci
805e1051a39Sopenharmony_cistatic int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base)
806e1051a39Sopenharmony_ci{
807e1051a39Sopenharmony_ci    int hostlen, baselen, i;
808e1051a39Sopenharmony_ci    unsigned char *hostptr, *baseptr, *maskptr;
809e1051a39Sopenharmony_ci    hostptr = ip->data;
810e1051a39Sopenharmony_ci    hostlen = ip->length;
811e1051a39Sopenharmony_ci    baseptr = base->data;
812e1051a39Sopenharmony_ci    baselen = base->length;
813e1051a39Sopenharmony_ci
814e1051a39Sopenharmony_ci    /* Invalid if not IPv4 or IPv6 */
815e1051a39Sopenharmony_ci    if (!((hostlen == 4) || (hostlen == 16)))
816e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
817e1051a39Sopenharmony_ci    if (!((baselen == 8) || (baselen == 32)))
818e1051a39Sopenharmony_ci        return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
819e1051a39Sopenharmony_ci
820e1051a39Sopenharmony_ci    /* Do not match IPv4 with IPv6 */
821e1051a39Sopenharmony_ci    if (hostlen * 2 != baselen)
822e1051a39Sopenharmony_ci        return X509_V_ERR_PERMITTED_VIOLATION;
823e1051a39Sopenharmony_ci
824e1051a39Sopenharmony_ci    maskptr = base->data + hostlen;
825e1051a39Sopenharmony_ci
826e1051a39Sopenharmony_ci    /* Considering possible not aligned base ipAddress */
827e1051a39Sopenharmony_ci    /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */
828e1051a39Sopenharmony_ci    for (i = 0; i < hostlen; i++)
829e1051a39Sopenharmony_ci        if ((hostptr[i] & maskptr[i]) != (baseptr[i] & maskptr[i]))
830e1051a39Sopenharmony_ci            return X509_V_ERR_PERMITTED_VIOLATION;
831e1051a39Sopenharmony_ci
832e1051a39Sopenharmony_ci    return X509_V_OK;
833e1051a39Sopenharmony_ci
834e1051a39Sopenharmony_ci}
835