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