]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/x509v3/v3_ncons.c
Copyright consolidation 07/10
[thirdparty/openssl.git] / crypto / x509v3 / v3_ncons.c
CommitLineData
0f113f3e 1/*
d2e9e320 2 * Copyright 2003-2016 The OpenSSL Project Authors. All Rights Reserved.
520b76ff 3 *
d2e9e320
RS
4 * Licensed under the OpenSSL license (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
520b76ff
DSH
8 */
9
520b76ff 10#include <stdio.h>
b39fc560 11#include "internal/cryptlib.h"
520b76ff
DSH
12#include <openssl/asn1t.h>
13#include <openssl/conf.h>
14#include <openssl/x509v3.h>
15
2743e38c 16#include "internal/x509_int.h"
df2ee0e2 17#include "ext_dat.h"
2743e38c 18
babb3798 19static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
0f113f3e
MC
20 X509V3_CTX *ctx,
21 STACK_OF(CONF_VALUE) *nval);
22static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
23 BIO *bp, int ind);
babb3798 24static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
0f113f3e
MC
25 STACK_OF(GENERAL_SUBTREE) *trees, BIO *bp,
26 int ind, char *name);
520b76ff
DSH
27static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip);
28
e9746e03
DSH
29static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc);
30static int nc_match_single(GENERAL_NAME *sub, GENERAL_NAME *gen);
31static int nc_dn(X509_NAME *sub, X509_NAME *nm);
32static int nc_dns(ASN1_IA5STRING *sub, ASN1_IA5STRING *dns);
33static int nc_email(ASN1_IA5STRING *sub, ASN1_IA5STRING *eml);
34static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base);
dd36fce0 35static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base);
e9746e03 36
560b79cb 37const X509V3_EXT_METHOD v3_name_constraints = {
0f113f3e
MC
38 NID_name_constraints, 0,
39 ASN1_ITEM_ref(NAME_CONSTRAINTS),
40 0, 0, 0, 0,
41 0, 0,
42 0, v2i_NAME_CONSTRAINTS,
43 i2r_NAME_CONSTRAINTS, 0,
44 NULL
520b76ff
DSH
45};
46
47ASN1_SEQUENCE(GENERAL_SUBTREE) = {
0f113f3e
MC
48 ASN1_SIMPLE(GENERAL_SUBTREE, base, GENERAL_NAME),
49 ASN1_IMP_OPT(GENERAL_SUBTREE, minimum, ASN1_INTEGER, 0),
50 ASN1_IMP_OPT(GENERAL_SUBTREE, maximum, ASN1_INTEGER, 1)
520b76ff
DSH
51} ASN1_SEQUENCE_END(GENERAL_SUBTREE)
52
53ASN1_SEQUENCE(NAME_CONSTRAINTS) = {
0f113f3e
MC
54 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, permittedSubtrees,
55 GENERAL_SUBTREE, 0),
56 ASN1_IMP_SEQUENCE_OF_OPT(NAME_CONSTRAINTS, excludedSubtrees,
57 GENERAL_SUBTREE, 1),
520b76ff 58} ASN1_SEQUENCE_END(NAME_CONSTRAINTS)
0f113f3e 59
520b76ff
DSH
60
61IMPLEMENT_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE)
62IMPLEMENT_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS)
63
babb3798 64static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
0f113f3e
MC
65 X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval)
66{
67 int i;
68 CONF_VALUE tval, *val;
69 STACK_OF(GENERAL_SUBTREE) **ptree = NULL;
70 NAME_CONSTRAINTS *ncons = NULL;
71 GENERAL_SUBTREE *sub = NULL;
86885c28 72
0f113f3e 73 ncons = NAME_CONSTRAINTS_new();
90945fa3 74 if (ncons == NULL)
0f113f3e
MC
75 goto memerr;
76 for (i = 0; i < sk_CONF_VALUE_num(nval); i++) {
77 val = sk_CONF_VALUE_value(nval, i);
86885c28 78 if (strncmp(val->name, "permitted", 9) == 0 && val->name[9]) {
0f113f3e
MC
79 ptree = &ncons->permittedSubtrees;
80 tval.name = val->name + 10;
86885c28 81 } else if (strncmp(val->name, "excluded", 8) == 0 && val->name[8]) {
0f113f3e
MC
82 ptree = &ncons->excludedSubtrees;
83 tval.name = val->name + 9;
84 } else {
85 X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, X509V3_R_INVALID_SYNTAX);
86 goto err;
87 }
88 tval.value = val->value;
89 sub = GENERAL_SUBTREE_new();
90945fa3
MC
90 if (sub == NULL)
91 goto memerr;
0f113f3e
MC
92 if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1))
93 goto err;
90945fa3 94 if (*ptree == NULL)
0f113f3e 95 *ptree = sk_GENERAL_SUBTREE_new_null();
90945fa3 96 if (*ptree == NULL || !sk_GENERAL_SUBTREE_push(*ptree, sub))
0f113f3e
MC
97 goto memerr;
98 sub = NULL;
99 }
100
101 return ncons;
102
103 memerr:
104 X509V3err(X509V3_F_V2I_NAME_CONSTRAINTS, ERR_R_MALLOC_FAILURE);
105 err:
895cba19
RS
106 NAME_CONSTRAINTS_free(ncons);
107 GENERAL_SUBTREE_free(sub);
0f113f3e
MC
108
109 return NULL;
110}
520b76ff 111
babb3798 112static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a,
0f113f3e
MC
113 BIO *bp, int ind)
114{
115 NAME_CONSTRAINTS *ncons = a;
116 do_i2r_name_constraints(method, ncons->permittedSubtrees,
117 bp, ind, "Permitted");
118 do_i2r_name_constraints(method, ncons->excludedSubtrees,
119 bp, ind, "Excluded");
120 return 1;
121}
520b76ff 122
babb3798 123static int do_i2r_name_constraints(const X509V3_EXT_METHOD *method,
0f113f3e
MC
124 STACK_OF(GENERAL_SUBTREE) *trees,
125 BIO *bp, int ind, char *name)
126{
127 GENERAL_SUBTREE *tree;
128 int i;
129 if (sk_GENERAL_SUBTREE_num(trees) > 0)
130 BIO_printf(bp, "%*s%s:\n", ind, "", name);
131 for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) {
132 tree = sk_GENERAL_SUBTREE_value(trees, i);
133 BIO_printf(bp, "%*s", ind + 2, "");
134 if (tree->base->type == GEN_IPADD)
135 print_nc_ipadd(bp, tree->base->d.ip);
136 else
137 GENERAL_NAME_print(bp, tree->base);
138 BIO_puts(bp, "\n");
139 }
140 return 1;
141}
520b76ff
DSH
142
143static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip)
0f113f3e
MC
144{
145 int i, len;
146 unsigned char *p;
147 p = ip->data;
148 len = ip->length;
149 BIO_puts(bp, "IP:");
150 if (len == 8) {
151 BIO_printf(bp, "%d.%d.%d.%d/%d.%d.%d.%d",
152 p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
153 } else if (len == 32) {
154 for (i = 0; i < 16; i++) {
155 BIO_printf(bp, "%X", p[0] << 8 | p[1]);
156 p += 2;
157 if (i == 7)
158 BIO_puts(bp, "/");
159 else if (i != 15)
160 BIO_puts(bp, ":");
161 }
162 } else
163 BIO_printf(bp, "IP Address:<invalid>");
164 return 1;
165}
520b76ff 166
1d97c843
TH
167/*-
168 * Check a certificate conforms to a specified set of constraints.
e9746e03
DSH
169 * Return values:
170 * X509_V_OK: All constraints obeyed.
171 * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation.
172 * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation.
173 * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type.
174 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type.
175 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint syntax.
176 * X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of name
e9746e03
DSH
177 */
178
179int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
0f113f3e
MC
180{
181 int r, i;
182 X509_NAME *nm;
e9746e03 183
0f113f3e 184 nm = X509_get_subject_name(x);
e9746e03 185
0f113f3e
MC
186 if (X509_NAME_entry_count(nm) > 0) {
187 GENERAL_NAME gntmp;
188 gntmp.type = GEN_DIRNAME;
189 gntmp.d.directoryName = nm;
e9746e03 190
0f113f3e 191 r = nc_match(&gntmp, nc);
e9746e03 192
0f113f3e
MC
193 if (r != X509_V_OK)
194 return r;
e9746e03 195
0f113f3e 196 gntmp.type = GEN_EMAIL;
e9746e03 197
0f113f3e 198 /* Process any email address attributes in subject name */
e9746e03 199
0f113f3e
MC
200 for (i = -1;;) {
201 X509_NAME_ENTRY *ne;
202 i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i);
203 if (i == -1)
204 break;
205 ne = X509_NAME_get_entry(nm, i);
206 gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne);
207 if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING)
208 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
e9746e03 209
0f113f3e 210 r = nc_match(&gntmp, nc);
e9746e03 211
0f113f3e
MC
212 if (r != X509_V_OK)
213 return r;
214 }
e9746e03 215
0f113f3e 216 }
e9746e03 217
0f113f3e
MC
218 for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) {
219 GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i);
220 r = nc_match(gen, nc);
221 if (r != X509_V_OK)
222 return r;
223 }
e9746e03 224
0f113f3e 225 return X509_V_OK;
e9746e03 226
0f113f3e 227}
e9746e03
DSH
228
229static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc)
0f113f3e
MC
230{
231 GENERAL_SUBTREE *sub;
232 int i, r, match = 0;
233
234 /*
235 * Permitted subtrees: if any subtrees exist of matching the type at
236 * least one subtree must match.
237 */
238
239 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) {
240 sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i);
241 if (gen->type != sub->base->type)
242 continue;
243 if (sub->minimum || sub->maximum)
244 return X509_V_ERR_SUBTREE_MINMAX;
245 /* If we already have a match don't bother trying any more */
246 if (match == 2)
247 continue;
248 if (match == 0)
249 match = 1;
250 r = nc_match_single(gen, sub->base);
251 if (r == X509_V_OK)
252 match = 2;
253 else if (r != X509_V_ERR_PERMITTED_VIOLATION)
254 return r;
255 }
256
257 if (match == 1)
258 return X509_V_ERR_PERMITTED_VIOLATION;
259
260 /* Excluded subtrees: must not match any of these */
261
262 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) {
263 sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i);
264 if (gen->type != sub->base->type)
265 continue;
266 if (sub->minimum || sub->maximum)
267 return X509_V_ERR_SUBTREE_MINMAX;
268
269 r = nc_match_single(gen, sub->base);
270 if (r == X509_V_OK)
271 return X509_V_ERR_EXCLUDED_VIOLATION;
272 else if (r != X509_V_ERR_PERMITTED_VIOLATION)
273 return r;
274
275 }
276
277 return X509_V_OK;
278
279}
e9746e03
DSH
280
281static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base)
0f113f3e
MC
282{
283 switch (base->type) {
284 case GEN_DIRNAME:
285 return nc_dn(gen->d.directoryName, base->d.directoryName);
e9746e03 286
0f113f3e
MC
287 case GEN_DNS:
288 return nc_dns(gen->d.dNSName, base->d.dNSName);
e9746e03 289
0f113f3e
MC
290 case GEN_EMAIL:
291 return nc_email(gen->d.rfc822Name, base->d.rfc822Name);
e9746e03 292
0f113f3e
MC
293 case GEN_URI:
294 return nc_uri(gen->d.uniformResourceIdentifier,
295 base->d.uniformResourceIdentifier);
e9746e03 296
0f113f3e
MC
297 case GEN_IPADD:
298 return nc_ip(gen->d.iPAddress, base->d.iPAddress);
dd36fce0 299
0f113f3e
MC
300 default:
301 return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE;
302 }
e9746e03 303
0f113f3e 304}
e9746e03 305
0f113f3e
MC
306/*
307 * directoryName name constraint matching. The canonical encoding of
308 * X509_NAME makes this comparison easy. It is matched if the subtree is a
309 * subset of the name.
e9746e03
DSH
310 */
311
312static int nc_dn(X509_NAME *nm, X509_NAME *base)
0f113f3e
MC
313{
314 /* Ensure canonical encodings are up to date. */
315 if (nm->modified && i2d_X509_NAME(nm, NULL) < 0)
316 return X509_V_ERR_OUT_OF_MEM;
317 if (base->modified && i2d_X509_NAME(base, NULL) < 0)
318 return X509_V_ERR_OUT_OF_MEM;
319 if (base->canon_enclen > nm->canon_enclen)
320 return X509_V_ERR_PERMITTED_VIOLATION;
321 if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen))
322 return X509_V_ERR_PERMITTED_VIOLATION;
323 return X509_V_OK;
324}
e9746e03
DSH
325
326static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
0f113f3e
MC
327{
328 char *baseptr = (char *)base->data;
329 char *dnsptr = (char *)dns->data;
330 /* Empty matches everything */
331 if (!*baseptr)
332 return X509_V_OK;
333 /*
334 * Otherwise can add zero or more components on the left so compare RHS
335 * and if dns is longer and expect '.' as preceding character.
336 */
337 if (dns->length > base->length) {
338 dnsptr += dns->length - base->length;
339 if (*baseptr != '.' && dnsptr[-1] != '.')
340 return X509_V_ERR_PERMITTED_VIOLATION;
341 }
342
343 if (strcasecmp(baseptr, dnsptr))
344 return X509_V_ERR_PERMITTED_VIOLATION;
345
346 return X509_V_OK;
347
348}
e9746e03
DSH
349
350static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
0f113f3e
MC
351{
352 const char *baseptr = (char *)base->data;
353 const char *emlptr = (char *)eml->data;
354
355 const char *baseat = strchr(baseptr, '@');
356 const char *emlat = strchr(emlptr, '@');
357 if (!emlat)
358 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
0d4fb843 359 /* Special case: initial '.' is RHS match */
0f113f3e
MC
360 if (!baseat && (*baseptr == '.')) {
361 if (eml->length > base->length) {
362 emlptr += eml->length - base->length;
86885c28 363 if (strcasecmp(baseptr, emlptr) == 0)
0f113f3e
MC
364 return X509_V_OK;
365 }
366 return X509_V_ERR_PERMITTED_VIOLATION;
367 }
368
369 /* If we have anything before '@' match local part */
370
371 if (baseat) {
372 if (baseat != baseptr) {
373 if ((baseat - baseptr) != (emlat - emlptr))
374 return X509_V_ERR_PERMITTED_VIOLATION;
375 /* Case sensitive match of local part */
376 if (strncmp(baseptr, emlptr, emlat - emlptr))
377 return X509_V_ERR_PERMITTED_VIOLATION;
378 }
379 /* Position base after '@' */
380 baseptr = baseat + 1;
381 }
382 emlptr = emlat + 1;
383 /* Just have hostname left to match: case insensitive */
384 if (strcasecmp(baseptr, emlptr))
385 return X509_V_ERR_PERMITTED_VIOLATION;
386
387 return X509_V_OK;
388
389}
e9746e03
DSH
390
391static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
0f113f3e
MC
392{
393 const char *baseptr = (char *)base->data;
394 const char *hostptr = (char *)uri->data;
395 const char *p = strchr(hostptr, ':');
396 int hostlen;
397 /* Check for foo:// and skip past it */
398 if (!p || (p[1] != '/') || (p[2] != '/'))
399 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
400 hostptr = p + 3;
401
402 /* Determine length of hostname part of URI */
403
404 /* Look for a port indicator as end of hostname first */
405
406 p = strchr(hostptr, ':');
407 /* Otherwise look for trailing slash */
408 if (!p)
409 p = strchr(hostptr, '/');
410
411 if (!p)
412 hostlen = strlen(hostptr);
413 else
414 hostlen = p - hostptr;
415
416 if (hostlen == 0)
417 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
418
0d4fb843 419 /* Special case: initial '.' is RHS match */
0f113f3e
MC
420 if (*baseptr == '.') {
421 if (hostlen > base->length) {
422 p = hostptr + hostlen - base->length;
86885c28 423 if (strncasecmp(p, baseptr, base->length) == 0)
0f113f3e
MC
424 return X509_V_OK;
425 }
426 return X509_V_ERR_PERMITTED_VIOLATION;
427 }
428
429 if ((base->length != (int)hostlen)
430 || strncasecmp(hostptr, baseptr, hostlen))
431 return X509_V_ERR_PERMITTED_VIOLATION;
432
433 return X509_V_OK;
434
435}
dd36fce0
LADL
436
437static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base)
0f113f3e
MC
438{
439 int hostlen, baselen, i;
440 unsigned char *hostptr, *baseptr, *maskptr;
441 hostptr = ip->data;
442 hostlen = ip->length;
443 baseptr = base->data;
444 baselen = base->length;
445
446 /* Invalid if not IPv4 or IPv6 */
447 if (!((hostlen == 4) || (hostlen == 16)))
448 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
449 if (!((baselen == 8) || (baselen == 32)))
450 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
451
452 /* Do not match IPv4 with IPv6 */
453 if (hostlen * 2 != baselen)
454 return X509_V_ERR_PERMITTED_VIOLATION;
455
456 maskptr = base->data + hostlen;
457
458 /* Considering possible not aligned base ipAddress */
459 /* Not checking for wrong mask definition: i.e.: 255.0.255.0 */
460 for (i = 0; i < hostlen; i++)
461 if ((hostptr[i] & maskptr[i]) != (baseptr[i] & maskptr[i]))
462 return X509_V_ERR_PERMITTED_VIOLATION;
463
464 return X509_V_OK;
465
466}