/src/openssl30/crypto/x509/v3_ncons.c
| Line | Count | Source (jump to first uncovered line) | 
| 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 | 0 |     ((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 | 0 | # 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 | 0 | { | 
| 82 | 0 |     int i; | 
| 83 |  | 
 | 
| 84 | 0 |     for (i = str->length; i > 0 && str->data[i - 1] != c; i--); | 
| 85 |  | 
 | 
| 86 | 0 |     if (i == 0) | 
| 87 | 0 |         return NULL; | 
| 88 |  |  | 
| 89 | 0 |     return (char *)&str->data[i - 1]; | 
| 90 | 0 | } | 
| 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 | 0 | { | 
| 101 | 0 |     for (; n > 0; n--, s1++, s2++) { | 
| 102 | 0 |         if (*s1 != *s2) { | 
| 103 | 0 |             unsigned char c1 = (unsigned char)*s1, c2 = (unsigned char)*s2; | 
| 104 |  |  | 
| 105 |  |             /* Convert to lower case */ | 
| 106 | 0 |             if (c1 >= 0x41 /* A */ && c1 <= 0x5A /* Z */) | 
| 107 | 0 |                 c1 += 0x20; | 
| 108 | 0 |             if (c2 >= 0x41 /* A */ && c2 <= 0x5A /* Z */) | 
| 109 | 0 |                 c2 += 0x20; | 
| 110 |  | 
 | 
| 111 | 0 |             if (c1 == c2) | 
| 112 | 0 |                 continue; | 
| 113 |  |  | 
| 114 | 0 |             if (c1 < c2) | 
| 115 | 0 |                 return -1; | 
| 116 |  |  | 
| 117 |  |             /* c1 > c2 */ | 
| 118 | 0 |             return 1; | 
| 119 | 0 |         } | 
| 120 | 0 |     } | 
| 121 |  |  | 
| 122 | 0 |     return 0; | 
| 123 | 0 | } | 
| 124 |  |  | 
| 125 |  | static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, | 
| 126 |  |                                   X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval) | 
| 127 | 0 | { | 
| 128 | 0 |     int i; | 
| 129 | 0 |     CONF_VALUE tval, *val; | 
| 130 | 0 |     STACK_OF(GENERAL_SUBTREE) **ptree = NULL; | 
| 131 | 0 |     NAME_CONSTRAINTS *ncons = NULL; | 
| 132 | 0 |     GENERAL_SUBTREE *sub = NULL; | 
| 133 |  | 
 | 
| 134 | 0 |     ncons = NAME_CONSTRAINTS_new(); | 
| 135 | 0 |     if (ncons == NULL) | 
| 136 | 0 |         goto memerr; | 
| 137 | 0 |     for (i = 0; i < sk_CONF_VALUE_num(nval); i++) { | 
| 138 | 0 |         val = sk_CONF_VALUE_value(nval, i); | 
| 139 | 0 |         if (strncmp(val->name, "permitted", 9) == 0 && val->name[9]) { | 
| 140 | 0 |             ptree = &ncons->permittedSubtrees; | 
| 141 | 0 |             tval.name = val->name + 10; | 
| 142 | 0 |         } else if (strncmp(val->name, "excluded", 8) == 0 && val->name[8]) { | 
| 143 | 0 |             ptree = &ncons->excludedSubtrees; | 
| 144 | 0 |             tval.name = val->name + 9; | 
| 145 | 0 |         } else { | 
| 146 | 0 |             ERR_raise(ERR_LIB_X509V3, X509V3_R_INVALID_SYNTAX); | 
| 147 | 0 |             goto err; | 
| 148 | 0 |         } | 
| 149 | 0 |         tval.value = val->value; | 
| 150 | 0 |         sub = GENERAL_SUBTREE_new(); | 
| 151 | 0 |         if (sub == NULL) | 
| 152 | 0 |             goto memerr; | 
| 153 | 0 |         if (!v2i_GENERAL_NAME_ex(sub->base, method, ctx, &tval, 1)) | 
| 154 | 0 |             goto err; | 
| 155 | 0 |         if (*ptree == NULL) | 
| 156 | 0 |             *ptree = sk_GENERAL_SUBTREE_new_null(); | 
| 157 | 0 |         if (*ptree == NULL || !sk_GENERAL_SUBTREE_push(*ptree, sub)) | 
| 158 | 0 |             goto memerr; | 
| 159 | 0 |         sub = NULL; | 
| 160 | 0 |     } | 
| 161 |  |  | 
| 162 | 0 |     return ncons; | 
| 163 |  |  | 
| 164 | 0 |  memerr: | 
| 165 | 0 |     ERR_raise(ERR_LIB_X509V3, ERR_R_MALLOC_FAILURE); | 
| 166 | 0 |  err: | 
| 167 | 0 |     NAME_CONSTRAINTS_free(ncons); | 
| 168 | 0 |     GENERAL_SUBTREE_free(sub); | 
| 169 |  | 
 | 
| 170 | 0 |     return NULL; | 
| 171 | 0 | } | 
| 172 |  |  | 
| 173 |  | static int i2r_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method, void *a, | 
| 174 |  |                                 BIO *bp, int ind) | 
| 175 | 7.34k | { | 
| 176 | 7.34k |     NAME_CONSTRAINTS *ncons = a; | 
| 177 | 7.34k |     do_i2r_name_constraints(method, ncons->permittedSubtrees, | 
| 178 | 7.34k |                             bp, ind, "Permitted"); | 
| 179 | 7.34k |     if (ncons->permittedSubtrees && ncons->excludedSubtrees) | 
| 180 | 477 |         BIO_puts(bp, "\n"); | 
| 181 | 7.34k |     do_i2r_name_constraints(method, ncons->excludedSubtrees, | 
| 182 | 7.34k |                             bp, ind, "Excluded"); | 
| 183 | 7.34k |     return 1; | 
| 184 | 7.34k | } | 
| 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 | 14.6k | { | 
| 190 | 14.6k |     GENERAL_SUBTREE *tree; | 
| 191 | 14.6k |     int i; | 
| 192 | 14.6k |     if (sk_GENERAL_SUBTREE_num(trees) > 0) | 
| 193 | 4.48k |         BIO_printf(bp, "%*s%s:\n", ind, "", name); | 
| 194 | 22.0k |     for (i = 0; i < sk_GENERAL_SUBTREE_num(trees); i++) { | 
| 195 | 7.40k |         if (i > 0) | 
| 196 | 2.91k |             BIO_puts(bp, "\n"); | 
| 197 | 7.40k |         tree = sk_GENERAL_SUBTREE_value(trees, i); | 
| 198 | 7.40k |         BIO_printf(bp, "%*s", ind + 2, ""); | 
| 199 | 7.40k |         if (tree->base->type == GEN_IPADD) | 
| 200 | 2.25k |             print_nc_ipadd(bp, tree->base->d.ip); | 
| 201 | 5.14k |         else | 
| 202 | 5.14k |             GENERAL_NAME_print(bp, tree->base); | 
| 203 | 7.40k |     } | 
| 204 | 14.6k |     return 1; | 
| 205 | 14.6k | } | 
| 206 |  |  | 
| 207 |  | static int print_nc_ipadd(BIO *bp, ASN1_OCTET_STRING *ip) | 
| 208 | 2.25k | { | 
| 209 |  |     /* ip->length should be 8 or 32 and len1 == len2 == 4 or len1 == len2 == 16 */ | 
| 210 | 2.25k |     int len1 = ip->length >= 16 ? 16 : ip->length >= 4 ? 4 : ip->length; | 
| 211 | 2.25k |     int len2 = ip->length - len1; | 
| 212 | 2.25k |     char *ip1 = ossl_ipaddr_to_asc(ip->data, len1); | 
| 213 | 2.25k |     char *ip2 = ossl_ipaddr_to_asc(ip->data + len1, len2); | 
| 214 | 2.25k |     int ret = ip1 != NULL && ip2 != NULL | 
| 215 | 2.25k |         && BIO_printf(bp, "IP:%s/%s", ip1, ip2) > 0; | 
| 216 |  |  | 
| 217 | 2.25k |     OPENSSL_free(ip1); | 
| 218 | 2.25k |     OPENSSL_free(ip2); | 
| 219 | 2.25k |     return ret; | 
| 220 | 2.25k | } | 
| 221 |  |  | 
| 222 | 28 | #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 | 28 | { | 
| 252 | 28 |     int r, i, name_count, constraint_count; | 
| 253 | 28 |     X509_NAME *nm; | 
| 254 |  |  | 
| 255 | 28 |     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 | 28 |     if (!add_lengths(&name_count, X509_NAME_entry_count(nm), | 
| 262 | 28 |                      sk_GENERAL_NAME_num(x->altname)) | 
| 263 | 28 |         || !add_lengths(&constraint_count, | 
| 264 | 28 |                         sk_GENERAL_SUBTREE_num(nc->permittedSubtrees), | 
| 265 | 28 |                         sk_GENERAL_SUBTREE_num(nc->excludedSubtrees)) | 
| 266 | 28 |         || (name_count > 0 && constraint_count > NAME_CHECK_MAX / name_count)) | 
| 267 | 0 |         return X509_V_ERR_UNSPECIFIED; | 
| 268 |  |  | 
| 269 | 28 |     if (X509_NAME_entry_count(nm) > 0) { | 
| 270 | 28 |         GENERAL_NAME gntmp; | 
| 271 | 28 |         gntmp.type = GEN_DIRNAME; | 
| 272 | 28 |         gntmp.d.directoryName = nm; | 
| 273 |  |  | 
| 274 | 28 |         r = nc_match(&gntmp, nc); | 
| 275 |  |  | 
| 276 | 28 |         if (r != X509_V_OK) | 
| 277 | 0 |             return r; | 
| 278 |  |  | 
| 279 | 28 |         gntmp.type = GEN_EMAIL; | 
| 280 |  |  | 
| 281 |  |         /* Process any email address attributes in subject name */ | 
| 282 |  |  | 
| 283 | 28 |         for (i = -1;;) { | 
| 284 | 28 |             const X509_NAME_ENTRY *ne; | 
| 285 |  |  | 
| 286 | 28 |             i = X509_NAME_get_index_by_NID(nm, NID_pkcs9_emailAddress, i); | 
| 287 | 28 |             if (i == -1) | 
| 288 | 28 |                 break; | 
| 289 | 0 |             ne = X509_NAME_get_entry(nm, i); | 
| 290 | 0 |             gntmp.d.rfc822Name = X509_NAME_ENTRY_get_data(ne); | 
| 291 | 0 |             if (gntmp.d.rfc822Name->type != V_ASN1_IA5STRING) | 
| 292 | 0 |                 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 293 |  |  | 
| 294 | 0 |             r = nc_match(&gntmp, nc); | 
| 295 |  | 
 | 
| 296 | 0 |             if (r != X509_V_OK) | 
| 297 | 0 |                 return r; | 
| 298 | 0 |         } | 
| 299 |  |  | 
| 300 | 28 |     } | 
| 301 |  |  | 
| 302 | 28 |     for (i = 0; i < sk_GENERAL_NAME_num(x->altname); i++) { | 
| 303 | 0 |         GENERAL_NAME *gen = sk_GENERAL_NAME_value(x->altname, i); | 
| 304 | 0 |         r = nc_match(gen, nc); | 
| 305 | 0 |         if (r != X509_V_OK) | 
| 306 | 0 |             return r; | 
| 307 | 0 |     } | 
| 308 |  |  | 
| 309 | 28 |     return X509_V_OK; | 
| 310 |  |  | 
| 311 | 28 | } | 
| 312 |  |  | 
| 313 |  | static int cn2dnsid(ASN1_STRING *cn, unsigned char **dnsid, size_t *idlen) | 
| 314 | 27 | { | 
| 315 | 27 |     int utf8_length; | 
| 316 | 27 |     unsigned char *utf8_value; | 
| 317 | 27 |     int i; | 
| 318 | 27 |     int isdnsname = 0; | 
| 319 |  |  | 
| 320 |  |     /* Don't leave outputs uninitialized */ | 
| 321 | 27 |     *dnsid = NULL; | 
| 322 | 27 |     *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 | 27 |     if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, cn)) < 0) | 
| 342 | 1 |         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 | 87 |     while (utf8_length > 0 && utf8_value[utf8_length - 1] == '\0') | 
| 350 | 61 |         --utf8_length; | 
| 351 |  |  | 
| 352 |  |     /* Reject *embedded* NULs */ | 
| 353 | 26 |     if (memchr(utf8_value, 0, utf8_length) != NULL) { | 
| 354 | 2 |         OPENSSL_free(utf8_value); | 
| 355 | 2 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 356 | 2 |     } | 
| 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 | 120 |     for (i = 0; i < utf8_length; ++i) { | 
| 368 | 115 |         unsigned char c = utf8_value[i]; | 
| 369 |  |  | 
| 370 | 115 |         if ((c >= 'a' && c <= 'z') | 
| 371 | 115 |             || (c >= 'A' && c <= 'Z') | 
| 372 | 115 |             || (c >= '0' && c <= '9') | 
| 373 | 115 |             || c == '_') | 
| 374 | 89 |             continue; | 
| 375 |  |  | 
| 376 |  |         /* Dot and hyphen cannot be first or last. */ | 
| 377 | 26 |         if (i > 0 && i < utf8_length - 1) { | 
| 378 | 22 |             if (c == '-') | 
| 379 | 1 |                 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 | 21 |             if (c == '.' | 
| 386 | 21 |                 && utf8_value[i + 1] != '.' | 
| 387 | 21 |                 && utf8_value[i - 1] != '-' | 
| 388 | 21 |                 && utf8_value[i + 1] != '-') { | 
| 389 | 6 |                 isdnsname = 1; | 
| 390 | 6 |                 continue; | 
| 391 | 6 |             } | 
| 392 | 21 |         } | 
| 393 | 19 |         isdnsname = 0; | 
| 394 | 19 |         break; | 
| 395 | 26 |     } | 
| 396 |  |  | 
| 397 | 24 |     if (isdnsname) { | 
| 398 | 1 |         *dnsid = utf8_value; | 
| 399 | 1 |         *idlen = (size_t)utf8_length; | 
| 400 | 1 |         return X509_V_OK; | 
| 401 | 1 |     } | 
| 402 | 23 |     OPENSSL_free(utf8_value); | 
| 403 | 23 |     return X509_V_OK; | 
| 404 | 24 | } | 
| 405 |  |  | 
| 406 |  | /* | 
| 407 |  |  * Check CN against DNS-ID name constraints. | 
| 408 |  |  */ | 
| 409 |  | int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc) | 
| 410 | 28 | { | 
| 411 | 28 |     int r, i; | 
| 412 | 28 |     const X509_NAME *nm = X509_get_subject_name(x); | 
| 413 | 28 |     ASN1_STRING stmp; | 
| 414 | 28 |     GENERAL_NAME gntmp; | 
| 415 |  |  | 
| 416 | 28 |     stmp.flags = 0; | 
| 417 | 28 |     stmp.type = V_ASN1_IA5STRING; | 
| 418 | 28 |     gntmp.type = GEN_DNS; | 
| 419 | 28 |     gntmp.d.dNSName = &stmp; | 
| 420 |  |  | 
| 421 |  |     /* Process any commonName attributes in subject name */ | 
| 422 |  |  | 
| 423 | 52 |     for (i = -1;;) { | 
| 424 | 52 |         X509_NAME_ENTRY *ne; | 
| 425 | 52 |         ASN1_STRING *cn; | 
| 426 | 52 |         unsigned char *idval; | 
| 427 | 52 |         size_t idlen; | 
| 428 |  |  | 
| 429 | 52 |         i = X509_NAME_get_index_by_NID(nm, NID_commonName, i); | 
| 430 | 52 |         if (i == -1) | 
| 431 | 25 |             break; | 
| 432 | 27 |         ne = X509_NAME_get_entry(nm, i); | 
| 433 | 27 |         cn = X509_NAME_ENTRY_get_data(ne); | 
| 434 |  |  | 
| 435 |  |         /* Only process attributes that look like host names */ | 
| 436 | 27 |         if ((r = cn2dnsid(cn, &idval, &idlen)) != X509_V_OK) | 
| 437 | 3 |             return r; | 
| 438 | 24 |         if (idlen == 0) | 
| 439 | 23 |             continue; | 
| 440 |  |  | 
| 441 | 1 |         stmp.length = idlen; | 
| 442 | 1 |         stmp.data = idval; | 
| 443 | 1 |         r = nc_match(&gntmp, nc); | 
| 444 | 1 |         OPENSSL_free(idval); | 
| 445 | 1 |         if (r != X509_V_OK) | 
| 446 | 0 |             return r; | 
| 447 | 1 |     } | 
| 448 | 25 |     return X509_V_OK; | 
| 449 | 28 | } | 
| 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 | 0 | static int nc_minmax_valid(GENERAL_SUBTREE *sub) { | 
| 456 | 0 |     BIGNUM *bn = NULL; | 
| 457 | 0 |     int ok = 1; | 
| 458 |  | 
 | 
| 459 | 0 |     if (sub->maximum) | 
| 460 | 0 |         ok = 0; | 
| 461 |  | 
 | 
| 462 | 0 |     if (sub->minimum) { | 
| 463 | 0 |         bn = ASN1_INTEGER_to_BN(sub->minimum, NULL); | 
| 464 | 0 |         if (bn == NULL || !BN_is_zero(bn)) | 
| 465 | 0 |             ok = 0; | 
| 466 | 0 |         BN_free(bn); | 
| 467 | 0 |     } | 
| 468 |  | 
 | 
| 469 | 0 |     return ok; | 
| 470 | 0 | } | 
| 471 |  |  | 
| 472 |  | static int nc_match(GENERAL_NAME *gen, NAME_CONSTRAINTS *nc) | 
| 473 | 29 | { | 
| 474 | 29 |     GENERAL_SUBTREE *sub; | 
| 475 | 29 |     int i, r, match = 0; | 
| 476 | 29 |     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 | 29 |     if (effective_type == GEN_OTHERNAME && | 
| 484 | 29 |         (OBJ_obj2nid(gen->d.otherName->type_id) == NID_id_on_SmtpUTF8Mailbox)) { | 
| 485 | 0 |         effective_type = GEN_EMAIL; | 
| 486 | 0 |     } | 
| 487 |  |  | 
| 488 |  |     /* | 
| 489 |  |      * Permitted subtrees: if any subtrees exist of matching the type at | 
| 490 |  |      * least one subtree must match. | 
| 491 |  |      */ | 
| 492 |  |  | 
| 493 | 29 |     for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { | 
| 494 | 0 |         sub = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); | 
| 495 | 0 |         if (effective_type != sub->base->type | 
| 496 | 0 |             || (effective_type == GEN_OTHERNAME && | 
| 497 | 0 |                 OBJ_cmp(gen->d.otherName->type_id, | 
| 498 | 0 |                         sub->base->d.otherName->type_id) != 0)) | 
| 499 | 0 |             continue; | 
| 500 | 0 |         if (!nc_minmax_valid(sub)) | 
| 501 | 0 |             return X509_V_ERR_SUBTREE_MINMAX; | 
| 502 |  |         /* If we already have a match don't bother trying any more */ | 
| 503 | 0 |         if (match == 2) | 
| 504 | 0 |             continue; | 
| 505 | 0 |         if (match == 0) | 
| 506 | 0 |             match = 1; | 
| 507 | 0 |         r = nc_match_single(effective_type, gen, sub->base); | 
| 508 | 0 |         if (r == X509_V_OK) | 
| 509 | 0 |             match = 2; | 
| 510 | 0 |         else if (r != X509_V_ERR_PERMITTED_VIOLATION) | 
| 511 | 0 |             return r; | 
| 512 | 0 |     } | 
| 513 |  |  | 
| 514 | 29 |     if (match == 1) | 
| 515 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 516 |  |  | 
| 517 |  |     /* Excluded subtrees: must not match any of these */ | 
| 518 |  |  | 
| 519 | 29 |     for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { | 
| 520 | 0 |         sub = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); | 
| 521 | 0 |         if (effective_type != sub->base->type | 
| 522 | 0 |             || (effective_type == GEN_OTHERNAME && | 
| 523 | 0 |                 OBJ_cmp(gen->d.otherName->type_id, | 
| 524 | 0 |                         sub->base->d.otherName->type_id) != 0)) | 
| 525 | 0 |             continue; | 
| 526 | 0 |         if (!nc_minmax_valid(sub)) | 
| 527 | 0 |             return X509_V_ERR_SUBTREE_MINMAX; | 
| 528 |  |  | 
| 529 | 0 |         r = nc_match_single(effective_type, gen, sub->base); | 
| 530 | 0 |         if (r == X509_V_OK) | 
| 531 | 0 |             return X509_V_ERR_EXCLUDED_VIOLATION; | 
| 532 | 0 |         else if (r != X509_V_ERR_PERMITTED_VIOLATION) | 
| 533 | 0 |             return r; | 
| 534 |  | 
 | 
| 535 | 0 |     } | 
| 536 |  |  | 
| 537 | 29 |     return X509_V_OK; | 
| 538 |  |  | 
| 539 | 29 | } | 
| 540 |  |  | 
| 541 |  | static int nc_match_single(int effective_type, GENERAL_NAME *gen, | 
| 542 |  |                            GENERAL_NAME *base) | 
| 543 | 0 | { | 
| 544 | 0 |     switch (gen->type) { | 
| 545 | 0 |     case GEN_OTHERNAME: | 
| 546 | 0 |         switch (effective_type) { | 
| 547 | 0 |         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 | 0 |             return nc_email_eai(gen->d.otherName->value, base->d.rfc822Name); | 
| 553 |  |  | 
| 554 | 0 |         default: | 
| 555 | 0 |             return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; | 
| 556 | 0 |         } | 
| 557 |  |  | 
| 558 | 0 |     case GEN_DIRNAME: | 
| 559 | 0 |         return nc_dn(gen->d.directoryName, base->d.directoryName); | 
| 560 |  |  | 
| 561 | 0 |     case GEN_DNS: | 
| 562 | 0 |         return nc_dns(gen->d.dNSName, base->d.dNSName); | 
| 563 |  |  | 
| 564 | 0 |     case GEN_EMAIL: | 
| 565 | 0 |         return nc_email(gen->d.rfc822Name, base->d.rfc822Name); | 
| 566 |  |  | 
| 567 | 0 |     case GEN_URI: | 
| 568 | 0 |         return nc_uri(gen->d.uniformResourceIdentifier, | 
| 569 | 0 |                       base->d.uniformResourceIdentifier); | 
| 570 |  |  | 
| 571 | 0 |     case GEN_IPADD: | 
| 572 | 0 |         return nc_ip(gen->d.iPAddress, base->d.iPAddress); | 
| 573 |  |  | 
| 574 | 0 |     default: | 
| 575 | 0 |         return X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE; | 
| 576 | 0 |     } | 
| 577 |  | 
 | 
| 578 | 0 | } | 
| 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 | 0 | { | 
| 588 |  |     /* Ensure canonical encodings are up to date.  */ | 
| 589 | 0 |     if (nm->modified && i2d_X509_NAME(nm, NULL) < 0) | 
| 590 | 0 |         return X509_V_ERR_OUT_OF_MEM; | 
| 591 | 0 |     if (base->modified && i2d_X509_NAME(base, NULL) < 0) | 
| 592 | 0 |         return X509_V_ERR_OUT_OF_MEM; | 
| 593 | 0 |     if (base->canon_enclen > nm->canon_enclen) | 
| 594 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 595 | 0 |     if (memcmp(base->canon_enc, nm->canon_enc, base->canon_enclen)) | 
| 596 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 597 | 0 |     return X509_V_OK; | 
| 598 | 0 | } | 
| 599 |  |  | 
| 600 |  | static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) | 
| 601 | 0 | { | 
| 602 | 0 |     char *baseptr = (char *)base->data; | 
| 603 | 0 |     char *dnsptr = (char *)dns->data; | 
| 604 |  |  | 
| 605 |  |     /* Empty matches everything */ | 
| 606 | 0 |     if (base->length == 0) | 
| 607 | 0 |         return X509_V_OK; | 
| 608 |  |  | 
| 609 | 0 |     if (dns->length < base->length) | 
| 610 | 0 |         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 | 0 |     if (dns->length > base->length) { | 
| 617 | 0 |         dnsptr += dns->length - base->length; | 
| 618 | 0 |         if (*baseptr != '.' && dnsptr[-1] != '.') | 
| 619 | 0 |             return X509_V_ERR_PERMITTED_VIOLATION; | 
| 620 | 0 |     } | 
| 621 |  |  | 
| 622 | 0 |     if (ia5ncasecmp(baseptr, dnsptr, base->length)) | 
| 623 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 624 |  |  | 
| 625 | 0 |     return X509_V_OK; | 
| 626 |  | 
 | 
| 627 | 0 | } | 
| 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 | 0 | { | 
| 638 | 0 |     ASN1_UTF8STRING *eml; | 
| 639 | 0 |     char *baseptr = NULL; | 
| 640 | 0 |     const char *emlptr; | 
| 641 | 0 |     const char *emlat; | 
| 642 | 0 |     char ulabel[256]; | 
| 643 | 0 |     size_t size = sizeof(ulabel) - 1; | 
| 644 | 0 |     int ret = X509_V_OK; | 
| 645 | 0 |     size_t emlhostlen; | 
| 646 |  |  | 
| 647 |  |     /* We do not accept embedded NUL characters */ | 
| 648 | 0 |     if (base->length > 0 && memchr(base->data, 0, base->length) != NULL) | 
| 649 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 650 |  |  | 
| 651 |  |     /* 'base' may not be NUL terminated. Create a copy that is */ | 
| 652 | 0 |     baseptr = OPENSSL_strndup((char *)base->data, base->length); | 
| 653 | 0 |     if (baseptr == NULL) | 
| 654 | 0 |         return X509_V_ERR_OUT_OF_MEM; | 
| 655 |  |  | 
| 656 | 0 |     if (emltype->type != V_ASN1_UTF8STRING) { | 
| 657 | 0 |         ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 658 | 0 |         goto end; | 
| 659 | 0 |     } | 
| 660 |  |  | 
| 661 | 0 |     eml = emltype->value.utf8string; | 
| 662 | 0 |     emlptr = (char *)eml->data; | 
| 663 | 0 |     emlat = ia5memrchr(eml, '@'); | 
| 664 |  | 
 | 
| 665 | 0 |     if (emlat == NULL) { | 
| 666 | 0 |         ret = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 667 | 0 |         goto end; | 
| 668 | 0 |     } | 
| 669 |  |  | 
| 670 | 0 |     memset(ulabel, 0, sizeof(ulabel)); | 
| 671 |  |     /* Special case: initial '.' is RHS match */ | 
| 672 | 0 |     if (*baseptr == '.') { | 
| 673 | 0 |         ulabel[0] = '.'; | 
| 674 | 0 |         size -= 1; | 
| 675 | 0 |         if (ossl_a2ulabel(baseptr, ulabel + 1, &size) <= 0) { | 
| 676 | 0 |             ret = X509_V_ERR_UNSPECIFIED; | 
| 677 | 0 |             goto end; | 
| 678 | 0 |         } | 
| 679 |  |  | 
| 680 | 0 |         if ((size_t)eml->length > strlen(ulabel)) { | 
| 681 | 0 |             emlptr += eml->length - (strlen(ulabel)); | 
| 682 |  |             /* X509_V_OK */ | 
| 683 | 0 |             if (ia5ncasecmp(ulabel, emlptr, strlen(ulabel)) == 0) | 
| 684 | 0 |                 goto end; | 
| 685 | 0 |         } | 
| 686 | 0 |         ret = X509_V_ERR_PERMITTED_VIOLATION; | 
| 687 | 0 |         goto end; | 
| 688 | 0 |     } | 
| 689 |  |  | 
| 690 | 0 |     if (ossl_a2ulabel(baseptr, ulabel, &size) <= 0) { | 
| 691 | 0 |         ret = X509_V_ERR_UNSPECIFIED; | 
| 692 | 0 |         goto end; | 
| 693 | 0 |     } | 
| 694 |  |     /* Just have hostname left to match: case insensitive */ | 
| 695 | 0 |     emlptr = emlat + 1; | 
| 696 | 0 |     emlhostlen = IA5_OFFSET_LEN(eml, emlptr); | 
| 697 | 0 |     if (emlhostlen != strlen(ulabel) | 
| 698 | 0 |             || ia5ncasecmp(ulabel, emlptr, emlhostlen) != 0) { | 
| 699 | 0 |         ret = X509_V_ERR_PERMITTED_VIOLATION; | 
| 700 | 0 |         goto end; | 
| 701 | 0 |     } | 
| 702 |  |  | 
| 703 | 0 |  end: | 
| 704 | 0 |     OPENSSL_free(baseptr); | 
| 705 | 0 |     return ret; | 
| 706 | 0 | } | 
| 707 |  |  | 
| 708 |  | static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) | 
| 709 | 0 | { | 
| 710 | 0 |     const char *baseptr = (char *)base->data; | 
| 711 | 0 |     const char *emlptr = (char *)eml->data; | 
| 712 | 0 |     const char *baseat = ia5memrchr(base, '@'); | 
| 713 | 0 |     const char *emlat = ia5memrchr(eml, '@'); | 
| 714 | 0 |     size_t basehostlen, emlhostlen; | 
| 715 |  | 
 | 
| 716 | 0 |     if (!emlat) | 
| 717 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 718 |  |     /* Special case: initial '.' is RHS match */ | 
| 719 | 0 |     if (!baseat && base->length > 0 && (*baseptr == '.')) { | 
| 720 | 0 |         if (eml->length > base->length) { | 
| 721 | 0 |             emlptr += eml->length - base->length; | 
| 722 | 0 |             if (ia5ncasecmp(baseptr, emlptr, base->length) == 0) | 
| 723 | 0 |                 return X509_V_OK; | 
| 724 | 0 |         } | 
| 725 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 726 | 0 |     } | 
| 727 |  |  | 
| 728 |  |     /* If we have anything before '@' match local part */ | 
| 729 |  |  | 
| 730 | 0 |     if (baseat) { | 
| 731 | 0 |         if (baseat != baseptr) { | 
| 732 | 0 |             if ((baseat - baseptr) != (emlat - emlptr)) | 
| 733 | 0 |                 return X509_V_ERR_PERMITTED_VIOLATION; | 
| 734 | 0 |             if (memchr(baseptr, 0, baseat - baseptr) || | 
| 735 | 0 |                 memchr(emlptr, 0, emlat - emlptr)) | 
| 736 | 0 |                 return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 737 |  |             /* Case sensitive match of local part */ | 
| 738 | 0 |             if (strncmp(baseptr, emlptr, emlat - emlptr)) | 
| 739 | 0 |                 return X509_V_ERR_PERMITTED_VIOLATION; | 
| 740 | 0 |         } | 
| 741 |  |         /* Position base after '@' */ | 
| 742 | 0 |         baseptr = baseat + 1; | 
| 743 | 0 |     } | 
| 744 | 0 |     emlptr = emlat + 1; | 
| 745 | 0 |     basehostlen = IA5_OFFSET_LEN(base, baseptr); | 
| 746 | 0 |     emlhostlen = IA5_OFFSET_LEN(eml, emlptr); | 
| 747 |  |     /* Just have hostname left to match: case insensitive */ | 
| 748 | 0 |     if (basehostlen != emlhostlen || ia5ncasecmp(baseptr, emlptr, emlhostlen)) | 
| 749 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 750 |  |  | 
| 751 | 0 |     return X509_V_OK; | 
| 752 |  | 
 | 
| 753 | 0 | } | 
| 754 |  |  | 
| 755 |  | static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base) | 
| 756 | 0 | { | 
| 757 | 0 |     const char *baseptr = (char *)base->data; | 
| 758 | 0 |     const char *hostptr = (char *)uri->data; | 
| 759 | 0 |     const char *p = ia5memchr(uri, (char *)uri->data, ':'); | 
| 760 | 0 |     int hostlen; | 
| 761 |  |  | 
| 762 |  |     /* Check for foo:// and skip past it */ | 
| 763 | 0 |     if (p == NULL | 
| 764 | 0 |             || IA5_OFFSET_LEN(uri, p) < 3 | 
| 765 | 0 |             || p[1] != '/' | 
| 766 | 0 |             || p[2] != '/') | 
| 767 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 768 | 0 |     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 | 0 |     p = ia5memchr(uri, hostptr, ':'); | 
| 775 |  |     /* Otherwise look for trailing slash */ | 
| 776 | 0 |     if (p == NULL) | 
| 777 | 0 |         p = ia5memchr(uri, hostptr, '/'); | 
| 778 |  | 
 | 
| 779 | 0 |     if (p == NULL) | 
| 780 | 0 |         hostlen = IA5_OFFSET_LEN(uri, hostptr); | 
| 781 | 0 |     else | 
| 782 | 0 |         hostlen = p - hostptr; | 
| 783 |  | 
 | 
| 784 | 0 |     if (hostlen == 0) | 
| 785 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 786 |  |  | 
| 787 |  |     /* Special case: initial '.' is RHS match */ | 
| 788 | 0 |     if (base->length > 0 && *baseptr == '.') { | 
| 789 | 0 |         if (hostlen > base->length) { | 
| 790 | 0 |             p = hostptr + hostlen - base->length; | 
| 791 | 0 |             if (ia5ncasecmp(p, baseptr, base->length) == 0) | 
| 792 | 0 |                 return X509_V_OK; | 
| 793 | 0 |         } | 
| 794 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 795 | 0 |     } | 
| 796 |  |  | 
| 797 | 0 |     if ((base->length != (int)hostlen) | 
| 798 | 0 |         || ia5ncasecmp(hostptr, baseptr, hostlen)) | 
| 799 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 800 |  |  | 
| 801 | 0 |     return X509_V_OK; | 
| 802 |  | 
 | 
| 803 | 0 | } | 
| 804 |  |  | 
| 805 |  | static int nc_ip(ASN1_OCTET_STRING *ip, ASN1_OCTET_STRING *base) | 
| 806 | 0 | { | 
| 807 | 0 |     int hostlen, baselen, i; | 
| 808 | 0 |     unsigned char *hostptr, *baseptr, *maskptr; | 
| 809 | 0 |     hostptr = ip->data; | 
| 810 | 0 |     hostlen = ip->length; | 
| 811 | 0 |     baseptr = base->data; | 
| 812 | 0 |     baselen = base->length; | 
| 813 |  |  | 
| 814 |  |     /* Invalid if not IPv4 or IPv6 */ | 
| 815 | 0 |     if (!((hostlen == 4) || (hostlen == 16))) | 
| 816 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 817 | 0 |     if (!((baselen == 8) || (baselen == 32))) | 
| 818 | 0 |         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; | 
| 819 |  |  | 
| 820 |  |     /* Do not match IPv4 with IPv6 */ | 
| 821 | 0 |     if (hostlen * 2 != baselen) | 
| 822 | 0 |         return X509_V_ERR_PERMITTED_VIOLATION; | 
| 823 |  |  | 
| 824 | 0 |     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 | 0 |     for (i = 0; i < hostlen; i++) | 
| 829 | 0 |         if ((hostptr[i] & maskptr[i]) != (baseptr[i] & maskptr[i])) | 
| 830 | 0 |             return X509_V_ERR_PERMITTED_VIOLATION; | 
| 831 |  |  | 
| 832 | 0 |     return X509_V_OK; | 
| 833 |  | 
 | 
| 834 | 0 | } |