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