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