• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* v3_ncons.c */
2 /*
3  * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
4  * project.
5  */
6 /* ====================================================================
7  * Copyright (c) 2003 The OpenSSL Project.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. All advertising materials mentioning features or use of this
22  *    software must display the following acknowledgment:
23  *    "This product includes software developed by the OpenSSL Project
24  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25  *
26  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27  *    endorse or promote products derived from this software without
28  *    prior written permission. For written permission, please contact
29  *    licensing@OpenSSL.org.
30  *
31  * 5. Products derived from this software may not be called "OpenSSL"
32  *    nor may "OpenSSL" appear in their names without prior written
33  *    permission of the OpenSSL Project.
34  *
35  * 6. Redistributions of any form whatsoever must retain the following
36  *    acknowledgment:
37  *    "This product includes software developed by the OpenSSL Project
38  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51  * OF THE POSSIBILITY OF SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This product includes cryptographic software written by Eric Young
55  * (eay@cryptsoft.com).  This product includes software written by Tim
56  * Hudson (tjh@cryptsoft.com). */
57 
58 #include <stdio.h>
59 #include <string.h>
60 
61 #include <openssl/asn1t.h>
62 #include <openssl/conf.h>
63 #include <openssl/err.h>
64 #include <openssl/mem.h>
65 #include <openssl/obj.h>
66 #include <openssl/x509v3.h>
67 
68 #include "../internal.h"
69 
70 
71 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
72                                   X509V3_CTX *ctx,
73                                   STACK_OF(CONF_VALUE) *nval);
74 static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
75                                 BIO *bp, int ind);
76 static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
77                                    STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp,
78                                    int ind, const char *name);
79 static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
80 
81 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
82 static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
83 static int nc_dn(X509_NAME *sub, X509_NAME *nm);
84 static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
85 static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
86 static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
87 
88 const X509V3_EXT_METHOD v3_name_constraints = {
89     NID_name_constraints, 0,
90     ASN1_ITEM_ref(NAME_CONSTRAINTS),
91     0, 0, 0, 0,
92     0, 0,
93     0, v2i_NAME_CONSTRAINTS,
94     i2r_NAME_CONSTRAINTS, 0,
95     NULL
96 };
97 
98 ASN1_SEQUENCE(GENERAL_SUBTREE) = {
99         ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME),
100         ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0),
101         ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1)
102 } ASN1_SEQUENCE_END(GENERAL_SUBTREE)
103 
104 ASN1_SEQUENCE(NAME_CONSTRAINTS) = {
105         ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees,
106                                                         GENERAL_SUBTREE, 0),
107         ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees,
108                                                         GENERAL_SUBTREE, 1),
109 } ASN1_SEQUENCE_END(NAME_CONSTRAINTS)
110 
111 
112 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
113 IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
114 
115 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
116                                   X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
117 {
118     size_t i;
119     CONF_VALUE tval, *val;
120     STACK_OF(GENERAL_SUBTREE) **ptree = NULL;
121     NAME_CONSTRAINTS *ncons = NULL;
122     GENERAL_SUBTREE *sub = NULL;
123     ncons = NAME_CONSTRAINTS_new();
124     if (!ncons)
125         goto memerr;
126     for (i = 0; i < sk_CONF_VALUE_num(nval); i++) {
127         val = sk_CONF_VALUE_value(nval, i);
128         if (!strncmp(val->name, "permitted", 9) && val->name[9]) {
129             ptree = &ncons->permittedSubtrees;
130             tval.name = val->name + 10;
131         } else if (!strncmp(val->name, "excluded", 8) && val->name[8]) {
132             ptree = &ncons->excludedSubtrees;
133             tval.name = val->name + 9;
134         } else {
135             OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_SYNTAX);
136             goto err;
137         }
138         tval.value = val->value;
139         sub = GENERAL_SUBTREE_new();
140         if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1))
141             goto err;
142         if (!*ptree)
143             *ptree = sk_GENERAL_SUBTREE_new_null();
144         if (!*ptree || !sk_GENERAL_SUBTREE_push(*ptree, sub))
145             goto memerr;
146         sub = NULL;
147     }
148 
149     return ncons;
150 
151  memerr:
152     OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
153  err:
154     if (ncons)
155         NAME_CONSTRAINTS_free(ncons);
156     if (sub)
157         GENERAL_SUBTREE_free(sub);
158 
159     return NULL;
160 }
161 
i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD * method,void * a,BIO * bp,int ind)162 static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
163                                 BIO *bp, int ind)
164 {
165     NAME_CONSTRAINTS *ncons = a;
166     do_i2r_name_constraints(method, ncons->permittedSubtrees,
167                             bp, ind, "Permitted");
168     do_i2r_name_constraints(method, ncons->excludedSubtrees,
169                             bp, ind, "Excluded");
170     return 1;
171 }
172 
do_i2r_name_constraints(const X509V3_EXT_METHOD * method,STACK_OF (GENERAL_SUBTREE)* trees,BIO * bp,int ind,const char * name)173 static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
174                                    STACK_OF(GENERAL_SUBTREE) *trees,
175                                    BIO *bp, int ind, const char *name)
176 {
177     GENERAL_SUBTREE *tree;
178     size_t i;
179     if (sk_GENERAL_SUBTREE_num(trees) > 0)
180         BIO_printf(bp, "%*s%s:\n", ind, "", name);
181     for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) {
182         tree = sk_GENERAL_SUBTREE_value(trees, i);
183         BIO_printf(bp, "%*s", ind + 2, "");
184         if (tree->base->type == GEN_IPADD)
185             print_nc_ipadd(bp, tree->base->d.ip);
186         else
187             GENERAL_NAME_print(bp, tree->base);
188         BIO_puts(bp, "\n");
189     }
190     return 1;
191 }
192 
print_nc_ipadd(BIO * bp,ASN1_OCTET_STRING * ip)193 static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
194 {
195     int i, len;
196     unsigned char *p;
197     p = ip->data;
198     len = ip->length;
199     BIO_puts(bp, "IP:");
200     if (len == 8) {
201         BIO_printf(bp, "%d.%d.%d.%d/%d.%d.%d.%d",
202                    p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
203     } else if (len == 32) {
204         for (i = 0; i < 16; i++) {
205             BIO_printf(bp, "%X", p[0] << 8 | p[1]);
206             p += 2;
207             if (i == 7)
208                 BIO_puts(bp, "/");
209             else if (i != 15)
210                 BIO_puts(bp, ":");
211         }
212     } else
213         BIO_printf(bp, "IP Address:<invalid>");
214     return 1;
215 }
216 
217 /*-
218  * Check a certificate conforms to a specified set of constraints.
219  * Return values:
220  *   X509_V_OK: All constraints obeyed.
221  *   X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
222  *   X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
223  *   X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
224  *   X509_V_ERR_UNSPECIFIED: Unspecified error.
225  *   X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
226  *   X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: Bad or unsupported constraint
227  *     syntax.
228  *   X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: Bad or unsupported syntax of name.
229  */
230 
NAME_CONSTRAINTS_check(X509 * x,NAME_CONSTRAINTS * nc)231 int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
232 {
233     int r, i;
234     size_t j;
235     X509_NAME *nm;
236 
237     nm = X509_get_subject_name(x);
238 
239     /* Guard against certificates with an excessive number of names or
240      * constraints causing a computationally expensive name constraints
241      * check. */
242     size_t name_count =
243         X509_NAME_entry_count(nm) + sk_GENERAL_NAME_num(x->altname);
244     size_t constraint_count = sk_GENERAL_SUBTREE_num(nc->permittedSubtrees) +
245                               sk_GENERAL_SUBTREE_num(nc->excludedSubtrees);
246     size_t check_count = constraint_count * name_count;
247     if (name_count < (size_t)X509_NAME_entry_count(nm) ||
248         constraint_count < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees) ||
249         (constraint_count && check_count / constraint_count != name_count) ||
250         check_count > 1 << 20) {
251       return X509_V_ERR_UNSPECIFIED;
252     }
253 
254     if (X509_NAME_entry_count(nm) > 0) {
255         GENERAL_NAME gntmp;
256         gntmp.type = GEN_DIRNAME;
257         gntmp.d.directoryName = nm;
258 
259         r = nc_match(&gntmp, nc);
260 
261         if (r != X509_V_OK)
262             return r;
263 
264         gntmp.type = GEN_EMAIL;
265 
266         /* Process any email address attributes in subject name */
267 
268         for (i = -1;;) {
269             X509_NAME_ENTRY *ne;
270             i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i);
271             if (i == -1)
272                 break;
273             ne = X509_NAME_get_entry(nm, i);
274             gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
275             if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
276                 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
277 
278             r = nc_match(&gntmp, nc);
279 
280             if (r != X509_V_OK)
281                 return r;
282         }
283 
284     }
285 
286     for (j = 0; j < sk_GENERAL_NAME_num(x->altname); j++) {
287         GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, j);
288         r = nc_match(gen, nc);
289         if (r != X509_V_OK)
290             return r;
291     }
292 
293     return X509_V_OK;
294 
295 }
296 
nc_match(GENERAL_NAME * gen,NAME_CONSTRAINTS * nc)297 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
298 {
299     GENERAL_SUBTREE *sub;
300     int r, match = 0;
301     size_t i;
302 
303     /*
304      * Permitted subtrees: if any subtrees exist of matching the type at
305      * least one subtree must match.
306      */
307 
308     for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
309         sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
310         if (gen->type != sub->base->type)
311             continue;
312         if (sub->minimum || sub->maximum)
313             return X509_V_ERR_SUBTREE_MINMAX;
314         /* If we already have a match don't bother trying any more */
315         if (match == 2)
316             continue;
317         if (match == 0)
318             match = 1;
319         r = nc_match_single(gen, sub->base);
320         if (r == X509_V_OK)
321             match = 2;
322         else if (r != X509_V_ERR_PERMITTED_VIOLATION)
323             return r;
324     }
325 
326     if (match == 1)
327         return X509_V_ERR_PERMITTED_VIOLATION;
328 
329     /* Excluded subtrees: must not match any of these */
330 
331     for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
332         sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
333         if (gen->type != sub->base->type)
334             continue;
335         if (sub->minimum || sub->maximum)
336             return X509_V_ERR_SUBTREE_MINMAX;
337 
338         r = nc_match_single(gen, sub->base);
339         if (r == X509_V_OK)
340             return X509_V_ERR_EXCLUDED_VIOLATION;
341         else if (r != X509_V_ERR_PERMITTED_VIOLATION)
342             return r;
343 
344     }
345 
346     return X509_V_OK;
347 
348 }
349 
nc_match_single(GENERAL_NAME * gen,GENERAL_NAME * base)350 static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
351 {
352     switch (base->type) {
353     case GEN_DIRNAME:
354         return nc_dn(gen->d.directoryName, base->d.directoryName);
355 
356     case GEN_DNS:
357         return nc_dns(gen->d.dNSName, base->d.dNSName);
358 
359     case GEN_EMAIL:
360         return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
361 
362     case GEN_URI:
363         return nc_uri(gen->d.uniformResourceIdentifier,
364                       base->d.uniformResourceIdentifier);
365 
366     default:
367         return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
368     }
369 
370 }
371 
372 /*
373  * directoryName name constraint matching. The canonical encoding of
374  * X509_NAME makes this comparison easy. It is matched if the subtree is a
375  * subset of the name.
376  */
377 
nc_dn(X509_NAME * nm,X509_NAME * base)378 static int nc_dn(X509_NAME *nm, X509_NAME *base)
379 {
380     /* Ensure canonical encodings are up to date.  */
381     if (nm->modified && i2d_X509_NAME(nm, NULL) < 0)
382         return X509_V_ERR_OUT_OF_MEM;
383     if (base->modified && i2d_X509_NAME(base, NULL) < 0)
384         return X509_V_ERR_OUT_OF_MEM;
385     if (base->canon_enclen > nm->canon_enclen)
386         return X509_V_ERR_PERMITTED_VIOLATION;
387     if (OPENSSL_memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
388         return X509_V_ERR_PERMITTED_VIOLATION;
389     return X509_V_OK;
390 }
391 
nc_dns(ASN1_IA5STRING * dns,ASN1_IA5STRING * base)392 static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
393 {
394     char *baseptr = (char *)base->data;
395     char *dnsptr = (char *)dns->data;
396     /* Empty matches everything */
397     if (!*baseptr)
398         return X509_V_OK;
399     /*
400      * Otherwise can add zero or more components on the left so compare RHS
401      * and if dns is longer and expect '.' as preceding character.
402      */
403     if (dns->length > base->length) {
404         dnsptr += dns->length - base->length;
405         if (*baseptr != '.' && dnsptr[-1] != '.')
406             return X509_V_ERR_PERMITTED_VIOLATION;
407     }
408 
409     if (OPENSSL_strcasecmp(baseptr, dnsptr))
410         return X509_V_ERR_PERMITTED_VIOLATION;
411 
412     return X509_V_OK;
413 
414 }
415 
nc_email(ASN1_IA5STRING * eml,ASN1_IA5STRING * base)416 static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
417 {
418     const char *baseptr = (char *)base->data;
419     const char *emlptr = (char *)eml->data;
420 
421     const char *baseat = strchr(baseptr, '@');
422     const char *emlat = strchr(emlptr, '@');
423     if (!emlat)
424         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
425     /* Special case: inital '.' is RHS match */
426     if (!baseat && (*baseptr == '.')) {
427         if (eml->length > base->length) {
428             emlptr += eml->length - base->length;
429             if (!OPENSSL_strcasecmp(baseptr, emlptr))
430                 return X509_V_OK;
431         }
432         return X509_V_ERR_PERMITTED_VIOLATION;
433     }
434 
435     /* If we have anything before '@' match local part */
436 
437     if (baseat) {
438         if (baseat != baseptr) {
439             if ((baseat - baseptr) != (emlat - emlptr))
440                 return X509_V_ERR_PERMITTED_VIOLATION;
441             /* Case sensitive match of local part */
442             if (strncmp(baseptr, emlptr, emlat - emlptr))
443                 return X509_V_ERR_PERMITTED_VIOLATION;
444         }
445         /* Position base after '@' */
446         baseptr = baseat + 1;
447     }
448     emlptr = emlat + 1;
449     /* Just have hostname left to match: case insensitive */
450     if (OPENSSL_strcasecmp(baseptr, emlptr))
451         return X509_V_ERR_PERMITTED_VIOLATION;
452 
453     return X509_V_OK;
454 
455 }
456 
nc_uri(ASN1_IA5STRING * uri,ASN1_IA5STRING * base)457 static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
458 {
459     const char *baseptr = (char *)base->data;
460     const char *hostptr = (char *)uri->data;
461     const char *p = strchr(hostptr, ':');
462     int hostlen;
463     /* Check for foo:// and skip past it */
464     if (!p || (p[1] != '/') || (p[2] != '/'))
465         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
466     hostptr = p + 3;
467 
468     /* Determine length of hostname part of URI */
469 
470     /* Look for a port indicator as end of hostname first */
471 
472     p = strchr(hostptr, ':');
473     /* Otherwise look for trailing slash */
474     if (!p)
475         p = strchr(hostptr, '/');
476 
477     if (!p)
478         hostlen = strlen(hostptr);
479     else
480         hostlen = p - hostptr;
481 
482     if (hostlen == 0)
483         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
484 
485     /* Special case: inital '.' is RHS match */
486     if (*baseptr == '.') {
487         if (hostlen > base->length) {
488             p = hostptr + hostlen - base->length;
489             if (!OPENSSL_strncasecmp(p, baseptr, base->length))
490                 return X509_V_OK;
491         }
492         return X509_V_ERR_PERMITTED_VIOLATION;
493     }
494 
495     if ((base->length != (int)hostlen)
496         || OPENSSL_strncasecmp(hostptr, baseptr, hostlen))
497         return X509_V_ERR_PERMITTED_VIOLATION;
498 
499     return X509_V_OK;
500 
501 }
502