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