1 /* v3_ncons.c */ 2 /* 3 * Written by Dr Stephen N Henson (steve (at) 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 (at) 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 (at) cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh (at) 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 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 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 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. Return 219 * values: X509_V_OK: All constraints obeyed. 220 * X509_V_ERR_PERMITTED_VIOLATION: Permitted subtree violation. 221 * X509_V_ERR_EXCLUDED_VIOLATION: Excluded subtree violation. 222 * X509_V_ERR_SUBTREE_MINMAX: Min or max values present and matching type. 223 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: Unsupported constraint type. 224 * X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: bad unsupported constraint 225 * syntax. X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: bad or unsupported syntax of 226 * name 227 * 228 */ 229 230 int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc) 231 { 232 int r, i; 233 size_t j; 234 X509_NAME *nm; 235 236 nm = X509_get_subject_name(x); 237 238 if (X509_NAME_entry_count(nm) > 0) { 239 GENERAL_NAME gntmp; 240 gntmp.type = GEN_DIRNAME; 241 gntmp.d.directoryName = nm; 242 243 r = nc_match(&gntmp, nc); 244 245 if (r != X509_V_OK) 246 return r; 247 248 gntmp.type = GEN_EMAIL; 249 250 /* Process any email address attributes in subject name */ 251 252 for (i = -1;;) { 253 X509_NAME_ENTRY *ne; 254 i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i); 255 if (i == -1) 256 break; 257 ne = X509_NAME_get_entry(nm, i); 258 gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne); 259 if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) 260 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 261 262 r = nc_match(&gntmp, nc); 263 264 if (r != X509_V_OK) 265 return r; 266 } 267 268 } 269 270 for (j = 0; j < sk_GENERAL_NAME_num(x->altname); j++) { 271 GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, j); 272 r = nc_match(gen, nc); 273 if (r != X509_V_OK) 274 return r; 275 } 276 277 return X509_V_OK; 278 279 } 280 281 static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) 282 { 283 GENERAL_SUBTREE *sub; 284 int r, match = 0; 285 size_t i; 286 287 /* 288 * Permitted subtrees: if any subtrees exist of matching the type at 289 * least one subtree must match. 290 */ 291 292 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { 293 sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); 294 if (gen->type != sub->base->type) 295 continue; 296 if (sub->minimum || sub->maximum) 297 return X509_V_ERR_SUBTREE_MINMAX; 298 /* If we already have a match don't bother trying any more */ 299 if (match == 2) 300 continue; 301 if (match == 0) 302 match = 1; 303 r = nc_match_single(gen, sub->base); 304 if (r == X509_V_OK) 305 match = 2; 306 else if (r != X509_V_ERR_PERMITTED_VIOLATION) 307 return r; 308 } 309 310 if (match == 1) 311 return X509_V_ERR_PERMITTED_VIOLATION; 312 313 /* Excluded subtrees: must not match any of these */ 314 315 for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { 316 sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); 317 if (gen->type != sub->base->type) 318 continue; 319 if (sub->minimum || sub->maximum) 320 return X509_V_ERR_SUBTREE_MINMAX; 321 322 r = nc_match_single(gen, sub->base); 323 if (r == X509_V_OK) 324 return X509_V_ERR_EXCLUDED_VIOLATION; 325 else if (r != X509_V_ERR_PERMITTED_VIOLATION) 326 return r; 327 328 } 329 330 return X509_V_OK; 331 332 } 333 334 static int nc_match_single(GENERAL_NAME *gen, GENERAL_NAME *base) 335 { 336 switch (base->type) { 337 case GEN_DIRNAME: 338 return nc_dn(gen->d.directoryName, base->d.directoryName); 339 340 case GEN_DNS: 341 return nc_dns(gen->d.dNSName, base->d.dNSName); 342 343 case GEN_EMAIL: 344 return nc_email(gen->d.rfc822Name, base->d.rfc822Name); 345 346 case GEN_URI: 347 return nc_uri(gen->d.uniformResourceIdentifier, 348 base->d.uniformResourceIdentifier); 349 350 default: 351 return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; 352 } 353 354 } 355 356 /* 357 * directoryName name constraint matching. The canonical encoding of 358 * X509_NAME makes this comparison easy. It is matched if the subtree is a 359 * subset of the name. 360 */ 361 362 static int nc_dn(X509_NAME *nm, X509_NAME *base) 363 { 364 /* Ensure canonical encodings are up to date. */ 365 if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) 366 return X509_V_ERR_OUT_OF_MEM; 367 if (base->modified && i2d_X509_NAME(base, NULL) < 0) 368 return X509_V_ERR_OUT_OF_MEM; 369 if (base->canon_enclen > nm->canon_enclen) 370 return X509_V_ERR_PERMITTED_VIOLATION; 371 if (OPENSSL_memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) 372 return X509_V_ERR_PERMITTED_VIOLATION; 373 return X509_V_OK; 374 } 375 376 static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) 377 { 378 char *baseptr = (char *)base->data; 379 char *dnsptr = (char *)dns->data; 380 /* Empty matches everything */ 381 if (!*baseptr) 382 return X509_V_OK; 383 /* 384 * Otherwise can add zero or more components on the left so compare RHS 385 * and if dns is longer and expect '.' as preceding character. 386 */ 387 if (dns->length > base->length) { 388 dnsptr += dns->length - base->length; 389 if (*baseptr != '.' && dnsptr[-1] != '.') 390 return X509_V_ERR_PERMITTED_VIOLATION; 391 } 392 393 if (OPENSSL_strcasecmp(baseptr, dnsptr)) 394 return X509_V_ERR_PERMITTED_VIOLATION; 395 396 return X509_V_OK; 397 398 } 399 400 static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) 401 { 402 const char *baseptr = (char *)base->data; 403 const char *emlptr = (char *)eml->data; 404 405 const char *baseat = strchr(baseptr, '@'); 406 const char *emlat = strchr(emlptr, '@'); 407 if (!emlat) 408 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 409 /* Special case: inital '.' is RHS match */ 410 if (!baseat && (*baseptr == '.')) { 411 if (eml->length > base->length) { 412 emlptr += eml->length - base->length; 413 if (!OPENSSL_strcasecmp(baseptr, emlptr)) 414 return X509_V_OK; 415 } 416 return X509_V_ERR_PERMITTED_VIOLATION; 417 } 418 419 /* If we have anything before '@' match local part */ 420 421 if (baseat) { 422 if (baseat != baseptr) { 423 if ((baseat - baseptr) != (emlat - emlptr)) 424 return X509_V_ERR_PERMITTED_VIOLATION; 425 /* Case sensitive match of local part */ 426 if (strncmp(baseptr, emlptr, emlat - emlptr)) 427 return X509_V_ERR_PERMITTED_VIOLATION; 428 } 429 /* Position base after '@' */ 430 baseptr = baseat + 1; 431 } 432 emlptr = emlat + 1; 433 /* Just have hostname left to match: case insensitive */ 434 if (OPENSSL_strcasecmp(baseptr, emlptr)) 435 return X509_V_ERR_PERMITTED_VIOLATION; 436 437 return X509_V_OK; 438 439 } 440 441 static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) 442 { 443 const char *baseptr = (char *)base->data; 444 const char *hostptr = (char *)uri->data; 445 const char *p = strchr(hostptr, ':'); 446 int hostlen; 447 /* Check for foo:// and skip past it */ 448 if (!p || (p[1] != '/') || (p[2] != '/')) 449 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 450 hostptr = p + 3; 451 452 /* Determine length of hostname part of URI */ 453 454 /* Look for a port indicator as end of hostname first */ 455 456 p = strchr(hostptr, ':'); 457 /* Otherwise look for trailing slash */ 458 if (!p) 459 p = strchr(hostptr, '/'); 460 461 if (!p) 462 hostlen = strlen(hostptr); 463 else 464 hostlen = p - hostptr; 465 466 if (hostlen == 0) 467 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 468 469 /* Special case: inital '.' is RHS match */ 470 if (*baseptr == '.') { 471 if (hostlen > base->length) { 472 p = hostptr + hostlen - base->length; 473 if (!OPENSSL_strncasecmp(p, baseptr, base->length)) 474 return X509_V_OK; 475 } 476 return X509_V_ERR_PERMITTED_VIOLATION; 477 } 478 479 if ((base->length != (int)hostlen) 480 || OPENSSL_strncasecmp(hostptr, baseptr, hostlen)) 481 return X509_V_ERR_PERMITTED_VIOLATION; 482 483 return X509_V_OK; 484 485 } 486