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