1*73cc06fdStb /* $OpenBSD: x509_constraints.c,v 1.8 2020/09/20 19:13:06 tb Exp $ */ 2d27446edSbeck /* 3d27446edSbeck * Copyright (c) 2020 Bob Beck <beck@openbsd.org> 4d27446edSbeck * 5d27446edSbeck * Permission to use, copy, modify, and distribute this software for any 6d27446edSbeck * purpose with or without fee is hereby granted, provided that the above 7d27446edSbeck * copyright notice and this permission notice appear in all copies. 8d27446edSbeck * 9d27446edSbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10d27446edSbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11d27446edSbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12d27446edSbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13d27446edSbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14d27446edSbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15d27446edSbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16d27446edSbeck */ 17d27446edSbeck 18d27446edSbeck #include <ctype.h> 19d27446edSbeck #include <errno.h> 20d27446edSbeck #include <stdio.h> 21d27446edSbeck #include <string.h> 22d27446edSbeck #include <time.h> 23d27446edSbeck #include <unistd.h> 24d27446edSbeck 25d27446edSbeck #include <sys/socket.h> 26d27446edSbeck #include <arpa/inet.h> 27d27446edSbeck 28d27446edSbeck #include <openssl/safestack.h> 29d27446edSbeck #include <openssl/x509.h> 30d27446edSbeck #include <openssl/x509v3.h> 31d27446edSbeck 32d27446edSbeck #include "x509_internal.h" 33d27446edSbeck 34d27446edSbeck /* RFC 2821 section 4.5.3.1 */ 35d27446edSbeck #define LOCAL_PART_MAX_LEN 64 36d27446edSbeck #define DOMAIN_PART_MAX_LEN 255 37d27446edSbeck 38d27446edSbeck struct x509_constraints_name * 39d27446edSbeck x509_constraints_name_new() 40d27446edSbeck { 41d27446edSbeck return (calloc(1, sizeof(struct x509_constraints_name))); 42d27446edSbeck } 43d27446edSbeck 44d27446edSbeck void 45d27446edSbeck x509_constraints_name_clear(struct x509_constraints_name *name) 46d27446edSbeck { 47d27446edSbeck free(name->name); 48d27446edSbeck free(name->local); 49d27446edSbeck free(name->der); 50d27446edSbeck memset(name, 0, sizeof(*name)); 51d27446edSbeck } 52d27446edSbeck 53d27446edSbeck void 54d27446edSbeck x509_constraints_name_free(struct x509_constraints_name *name) 55d27446edSbeck { 56d27446edSbeck if (name == NULL) 57d27446edSbeck return; 58d27446edSbeck x509_constraints_name_clear(name); 59d27446edSbeck free(name); 60d27446edSbeck } 61d27446edSbeck 62d27446edSbeck struct x509_constraints_name * 63d27446edSbeck x509_constraints_name_dup(struct x509_constraints_name *name) 64d27446edSbeck { 65d27446edSbeck struct x509_constraints_name *new; 66d27446edSbeck 67d27446edSbeck if ((new = x509_constraints_name_new()) == NULL) 68d27446edSbeck goto err; 69d27446edSbeck new->type = name->type; 70d27446edSbeck new->af = name->af; 71d27446edSbeck new->der_len = name->der_len; 72d27446edSbeck if (name->der_len > 0 && (new->der = malloc(name->der_len)) == NULL) 73d27446edSbeck goto err; 74d27446edSbeck memcpy(new->der, name->der, name->der_len); 75d27446edSbeck if (name->name != NULL && (new->name = strdup(name->name)) == NULL) 76d27446edSbeck goto err; 77d27446edSbeck if (name->local != NULL && (new->local = strdup(name->local)) == NULL) 78d27446edSbeck goto err; 79d27446edSbeck memcpy(new->address, name->address, sizeof(name->address)); 80d27446edSbeck return new; 81d27446edSbeck err: 82d27446edSbeck x509_constraints_name_free(new); 83d27446edSbeck return NULL; 84d27446edSbeck } 85d27446edSbeck 86d27446edSbeck struct x509_constraints_names * 87d27446edSbeck x509_constraints_names_new() 88d27446edSbeck { 89d27446edSbeck return (calloc(1, sizeof(struct x509_constraints_names))); 90d27446edSbeck } 91d27446edSbeck 92d27446edSbeck void 93d27446edSbeck x509_constraints_names_clear(struct x509_constraints_names *names) 94d27446edSbeck { 95d27446edSbeck size_t i; 96d27446edSbeck 97d27446edSbeck for (i = 0; i < names->names_count; i++) 98d27446edSbeck x509_constraints_name_free(names->names[i]); 99d27446edSbeck free(names->names); 100d27446edSbeck memset(names, 0, sizeof(*names)); 101d27446edSbeck } 102d27446edSbeck 103d27446edSbeck void 104d27446edSbeck x509_constraints_names_free(struct x509_constraints_names *names) 105d27446edSbeck { 106d27446edSbeck if (names == NULL) 107d27446edSbeck return; 108d27446edSbeck 109d27446edSbeck x509_constraints_names_clear(names); 110d27446edSbeck free(names); 111d27446edSbeck } 112d27446edSbeck 113d27446edSbeck int 114d27446edSbeck x509_constraints_names_add(struct x509_constraints_names *names, 115d27446edSbeck struct x509_constraints_name *name) 116d27446edSbeck { 117d27446edSbeck size_t i = names->names_count; 118d27446edSbeck 119d27446edSbeck if (names->names_count == names->names_len) { 120d27446edSbeck struct x509_constraints_name **tmp; 121d27446edSbeck if ((tmp = recallocarray(names->names, names->names_len, 122d27446edSbeck names->names_len + 32, sizeof(*tmp))) == NULL) 123d27446edSbeck return 0; 124d27446edSbeck names->names_len += 32; 125d27446edSbeck names->names = tmp; 126d27446edSbeck } 127d27446edSbeck names->names[i] = name; 128d27446edSbeck names->names_count++; 129d27446edSbeck return 1; 130d27446edSbeck } 131d27446edSbeck 132d27446edSbeck struct x509_constraints_names * 133d27446edSbeck x509_constraints_names_dup(struct x509_constraints_names *names) 134d27446edSbeck { 135d27446edSbeck struct x509_constraints_names *new = NULL; 136d27446edSbeck struct x509_constraints_name *name = NULL; 137d27446edSbeck size_t i; 138d27446edSbeck 139d27446edSbeck if (names == NULL) 140d27446edSbeck return NULL; 141d27446edSbeck 142d27446edSbeck if ((new = x509_constraints_names_new()) == NULL) 143d27446edSbeck goto err; 144d27446edSbeck for (i = 0; i < names->names_count; i++) { 145d27446edSbeck if ((name = x509_constraints_name_dup(names->names[i])) == NULL) 146d27446edSbeck goto err; 147d27446edSbeck if (!x509_constraints_names_add(new, name)) 148d27446edSbeck goto err; 149d27446edSbeck } 150d27446edSbeck return new; 151d27446edSbeck err: 152d27446edSbeck x509_constraints_names_free(new); 153d27446edSbeck x509_constraints_name_free(name); 154d27446edSbeck return NULL; 155d27446edSbeck } 156d27446edSbeck 157d27446edSbeck 158d27446edSbeck /* 159d27446edSbeck * Validate that the name contains only a hostname consisting of RFC 160d27446edSbeck * 5890 compliant A-labels (see RFC 6066 section 3). This is more 161d27446edSbeck * permissive to allow for a leading '*' for a SAN DNSname wildcard, 162d27446edSbeck * or a leading '.' for a subdomain based constraint, as well as 163d27446edSbeck * allowing for '_' which is commonly accepted by nonconformant 164d27446edSbeck * DNS implementaitons. 165d27446edSbeck */ 166d27446edSbeck static int 167d27446edSbeck x509_constraints_valid_domain_internal(uint8_t *name, size_t len) 168d27446edSbeck { 169d27446edSbeck uint8_t prev, c = 0; 170d27446edSbeck int component = 0; 171d27446edSbeck int first; 172d27446edSbeck size_t i; 173d27446edSbeck 174d27446edSbeck if (len > DOMAIN_PART_MAX_LEN) 175d27446edSbeck return 0; 176d27446edSbeck 177d27446edSbeck for (i = 0; i < len; i++) { 178d27446edSbeck prev = c; 179d27446edSbeck c = name[i]; 180d27446edSbeck 181d27446edSbeck first = (i == 0); 182d27446edSbeck 183d27446edSbeck /* Everything has to be ASCII, with no NUL byte */ 184d27446edSbeck if (!isascii(c) || c == '\0') 185d27446edSbeck return 0; 186d27446edSbeck /* It must be alphanumeric, a '-', '.', '_' or '*' */ 187d27446edSbeck if (!isalnum(c) && c != '-' && c != '.' && c != '_' && 188d27446edSbeck c != '*') 189d27446edSbeck return 0; 190d27446edSbeck 191d27446edSbeck /* '*' can only be the first thing. */ 192d27446edSbeck if (c == '*' && !first) 193d27446edSbeck return 0; 194d27446edSbeck 195d27446edSbeck /* '-' must not start a component or be at the end. */ 196d27446edSbeck if (c == '-' && (component == 0 || i == len - 1)) 197d27446edSbeck return 0; 198d27446edSbeck 199d27446edSbeck /* 200d27446edSbeck * '.' must not be at the end. It may be first overall 201d27446edSbeck * but must not otherwise start a component. 202d27446edSbeck */ 203d27446edSbeck if (c == '.' && ((component == 0 && !first) || i == len - 1)) 204d27446edSbeck return 0; 205d27446edSbeck 206d27446edSbeck if (c == '.') { 207d27446edSbeck /* Components can not end with a dash. */ 208d27446edSbeck if (prev == '-') 209d27446edSbeck return 0; 210d27446edSbeck /* Start new component */ 211d27446edSbeck component = 0; 212d27446edSbeck continue; 213d27446edSbeck } 214d27446edSbeck /* Components must be 63 chars or less. */ 215d27446edSbeck if (++component > 63) 216d27446edSbeck return 0; 217d27446edSbeck } 218d27446edSbeck return 1; 219d27446edSbeck } 220d27446edSbeck 221d27446edSbeck int 222d27446edSbeck x509_constraints_valid_domain(uint8_t *name, size_t len) 223d27446edSbeck { 224d27446edSbeck if (len == 0) 225d27446edSbeck return 0; 226d27446edSbeck if (name[0] == '*') /* wildcard not allowed in a domain name */ 227d27446edSbeck return 0; 228d27446edSbeck /* 229d27446edSbeck * A domain may not be less than two characters, so you can't 230d27446edSbeck * have a require subdomain name with less than that. 231d27446edSbeck */ 232d27446edSbeck if (len < 3 && name[0] == '.') 233d27446edSbeck return 0; 234d27446edSbeck return x509_constraints_valid_domain_internal(name, len); 235d27446edSbeck } 236d27446edSbeck 237d27446edSbeck int 238d27446edSbeck x509_constraints_valid_host(uint8_t *name, size_t len) 239d27446edSbeck { 240d27446edSbeck struct sockaddr_in sin4; 241d27446edSbeck struct sockaddr_in6 sin6; 242d27446edSbeck 243d27446edSbeck if (len == 0) 244d27446edSbeck return 0; 245d27446edSbeck if (name[0] == '*') /* wildcard not allowed in a host name */ 246d27446edSbeck return 0; 247d27446edSbeck if (name[0] == '.') /* leading . not allowed in a host name*/ 248d27446edSbeck return 0; 249d27446edSbeck if (inet_pton(AF_INET, name, &sin4) == 1) 250d27446edSbeck return 0; 251d27446edSbeck if (inet_pton(AF_INET6, name, &sin6) == 1) 252d27446edSbeck return 0; 253d27446edSbeck return x509_constraints_valid_domain_internal(name, len); 254d27446edSbeck } 255d27446edSbeck 256d27446edSbeck int 257d27446edSbeck x509_constraints_valid_sandns(uint8_t *name, size_t len) 258d27446edSbeck { 259d27446edSbeck if (len == 0) 260d27446edSbeck return 0; 261d27446edSbeck 262d27446edSbeck if (name[0] == '.') /* leading . not allowed in a SAN DNS name */ 263d27446edSbeck return 0; 264d27446edSbeck /* 265d27446edSbeck * A domain may not be less than two characters, so you 266d27446edSbeck * can't wildcard a single domain of less than that 267d27446edSbeck */ 268d27446edSbeck if (len < 4 && name[0] == '*') 269d27446edSbeck return 0; 270d27446edSbeck /* 271d27446edSbeck * A wildcard may only be followed by a '.' 272d27446edSbeck */ 273d27446edSbeck if (len >= 4 && name[0] == '*' && name[1] != '.') 274d27446edSbeck return 0; 275d27446edSbeck 276d27446edSbeck return x509_constraints_valid_domain_internal(name, len); 277d27446edSbeck } 278d27446edSbeck 279d27446edSbeck static inline int 280d27446edSbeck local_part_ok(char c) 281d27446edSbeck { 282d27446edSbeck return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || 283d27446edSbeck ('A' <= c && c <= 'Z') || c == '!' || c == '#' || c == '$' || 284d27446edSbeck c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' || 285d27446edSbeck c == '-' || c == '/' || c == '=' || c == '?' || c == '^' || 286d27446edSbeck c == '_' || c == '`' || c == '{' || c == '|' || c == '}' || 287d27446edSbeck c == '~' || c == '.'); 288d27446edSbeck } 289d27446edSbeck 290d27446edSbeck /* 291d27446edSbeck * Parse "candidate" as an RFC 2821 mailbox. 292d27446edSbeck * Returns 0 if candidate is not a valid mailbox or if an error occurs. 293d27446edSbeck * Returns 1 if candidate is a mailbox and adds newly allocated 294d27446edSbeck * local and domain parts of the mailbox to "name->local" and name->name" 295d27446edSbeck */ 296d27446edSbeck int 297d27446edSbeck x509_constraints_parse_mailbox(uint8_t *candidate, size_t len, 298d27446edSbeck struct x509_constraints_name *name) 299d27446edSbeck { 300d27446edSbeck char working[DOMAIN_PART_MAX_LEN + 1] = { 0 }; 301d27446edSbeck char *candidate_local = NULL; 302d27446edSbeck char *candidate_domain = NULL; 303d27446edSbeck size_t i, wi = 0; 304d27446edSbeck int accept = 0; 305d27446edSbeck int quoted = 0; 306d27446edSbeck 307d27446edSbeck if (candidate == NULL) 308d27446edSbeck return 0; 309d27446edSbeck 310d27446edSbeck /* It can't be bigger than the local part, domain part and the '@' */ 311d27446edSbeck if (len > LOCAL_PART_MAX_LEN + DOMAIN_PART_MAX_LEN + 1) 312d27446edSbeck return 0; 313d27446edSbeck 314d27446edSbeck for (i = 0; i < len; i++) { 315d27446edSbeck char c = candidate[i]; 316d27446edSbeck /* non ascii, cr, lf, or nul is never allowed */ 317d27446edSbeck if (!isascii(c) || c == '\r' || c == '\n' || c == '\0') 318d27446edSbeck goto bad; 319d27446edSbeck if (i == 0) { 320d27446edSbeck /* local part is quoted part */ 321d27446edSbeck if (c == '"') 322d27446edSbeck quoted = 1; 323d27446edSbeck /* can not start with a . */ 324d27446edSbeck if (c == '.') 325d27446edSbeck goto bad; 326d27446edSbeck } 327d27446edSbeck if (wi > DOMAIN_PART_MAX_LEN) 328d27446edSbeck goto bad; 329d27446edSbeck if (accept) { 330d27446edSbeck working[wi++] = c; 331d27446edSbeck accept = 0; 332d27446edSbeck continue; 333d27446edSbeck } 334d27446edSbeck if (candidate_local != NULL) { 335d27446edSbeck /* We are looking for the domain part */ 336d27446edSbeck if (wi > DOMAIN_PART_MAX_LEN) 337d27446edSbeck goto bad; 338d27446edSbeck working[wi++] = c; 339d27446edSbeck if (i == len - 1) { 340d27446edSbeck if (wi == 0) 341d27446edSbeck goto bad; 342d27446edSbeck if (candidate_domain != NULL) 343d27446edSbeck goto bad; 344d27446edSbeck candidate_domain = strdup(working); 345d27446edSbeck if (candidate_domain == NULL) 346d27446edSbeck goto bad; 347d27446edSbeck } 348d27446edSbeck continue; 349d27446edSbeck } 350d27446edSbeck /* We are looking for the local part */ 351d27446edSbeck if (wi > LOCAL_PART_MAX_LEN) 352d27446edSbeck break; 353d27446edSbeck 354d27446edSbeck if (quoted) { 355d27446edSbeck if (c == '\\') { 356d27446edSbeck accept = 1; 357d27446edSbeck continue; 358d27446edSbeck } 359d27446edSbeck if (c == '"' && i != 0) { 360d27446edSbeck /* end the quoted part. @ must be next */ 361d27446edSbeck if (i + 1 == len || candidate[i + 1] != '@') 362d27446edSbeck goto bad; 363d27446edSbeck quoted = 0; 364d27446edSbeck } 365d27446edSbeck /* 366d27446edSbeck * XXX Go strangely permits sp but forbids ht 367d27446edSbeck * mimic that for now 368d27446edSbeck */ 369d27446edSbeck if (c == 9) 370d27446edSbeck goto bad; 371d27446edSbeck working[wi++] = c; 372d27446edSbeck continue; /* all's good inside our quoted string */ 373d27446edSbeck } 374d27446edSbeck if (c == '@') { 375d27446edSbeck if (wi == 0) 376d27446edSbeck goto bad;; 377d27446edSbeck if (candidate_local != NULL) 378d27446edSbeck goto bad; 379d27446edSbeck candidate_local = strdup(working); 380d27446edSbeck if (candidate_local == NULL) 381d27446edSbeck goto bad; 382d27446edSbeck memset(working, 0, sizeof(working)); 383d27446edSbeck wi = 0; 384d27446edSbeck continue; 385d27446edSbeck } 386d27446edSbeck if (c == '\\') { 387d27446edSbeck /* 388d27446edSbeck * RFC 3936 hints these can happen outside of 389d27446edSbeck * quotend string. don't include the \ but 390d27446edSbeck * next character must be ok. 391d27446edSbeck */ 392d27446edSbeck if (i + 1 == len) 393d27446edSbeck goto bad; 394d27446edSbeck if (!local_part_ok(candidate[i + 1])) 395d27446edSbeck goto bad; 396d27446edSbeck accept = 1; 397d27446edSbeck } 398d27446edSbeck if (!local_part_ok(c)) 399d27446edSbeck goto bad; 400d27446edSbeck working[wi++] = c; 401d27446edSbeck } 402d27446edSbeck if (candidate_local == NULL || candidate_domain == NULL) 403d27446edSbeck goto bad; 404d27446edSbeck if (!x509_constraints_valid_host(candidate_domain, 405d27446edSbeck strlen(candidate_domain))) 406d27446edSbeck goto bad; 407d27446edSbeck 408d27446edSbeck name->local = candidate_local; 409d27446edSbeck name->name = candidate_domain; 410d27446edSbeck name->type = GEN_EMAIL; 411d27446edSbeck return 1; 412d27446edSbeck bad: 413d27446edSbeck free(candidate_local); 414d27446edSbeck free(candidate_domain); 415d27446edSbeck return 0; 416d27446edSbeck } 417d27446edSbeck 418d27446edSbeck int 419d27446edSbeck x509_constraints_valid_domain_constraint(uint8_t *constraint, size_t len) 420d27446edSbeck { 421d27446edSbeck if (len == 0) 422d27446edSbeck return 1; /* empty constraints match */ 423d27446edSbeck 424d27446edSbeck if (constraint[0] == '*') /* wildcard not allowed in a constraint */ 425d27446edSbeck return 0; 426d27446edSbeck 427d27446edSbeck /* 428d27446edSbeck * A domain may not be less than two characters, so you 429d27446edSbeck * can't match a single domain of less than that 430d27446edSbeck */ 431d27446edSbeck if (len < 3 && constraint[0] == '.') 432d27446edSbeck return 0; 433d27446edSbeck return x509_constraints_valid_domain_internal(constraint, len); 434d27446edSbeck } 435d27446edSbeck 436d27446edSbeck /* 437d27446edSbeck * Extract the host part of a URI, returns the host part as a c string 438d27446edSbeck * the caller must free, or or NULL if it could not be found or is 439d27446edSbeck * invalid. 440d27446edSbeck * 441092bbf76Sbeck * RFC 3986: 442d27446edSbeck * the authority part of a uri starts with // and is terminated with 443d27446edSbeck * the next '/', '?', '#' or end of the URI. 444d27446edSbeck * 445d27446edSbeck * The authority itself contains [userinfo '@'] host [: port] 446d27446edSbeck * 447d27446edSbeck * so the host starts at the start or after the '@', and ends 448d27446edSbeck * with end of URI, '/', '?', "#', or ':'. 449d27446edSbeck */ 450d27446edSbeck int 451d27446edSbeck x509_constraints_uri_host(uint8_t *uri, size_t len, char **hostpart) 452d27446edSbeck { 453d27446edSbeck size_t i, hostlen = 0; 454d27446edSbeck uint8_t *authority = NULL; 455d27446edSbeck char *host = NULL; 456d27446edSbeck 457092bbf76Sbeck /* 458092bbf76Sbeck * Find first '//'. there must be at least a '//' and 459092bbf76Sbeck * something else. 460092bbf76Sbeck */ 461092bbf76Sbeck if (len < 3) 462092bbf76Sbeck return 0; 463d27446edSbeck for (i = 0; i < len - 1; i++) { 464d27446edSbeck if (!isascii(uri[i])) 465d27446edSbeck return 0; 466d27446edSbeck if (uri[i] == '/' && uri[i + 1] == '/') { 467d27446edSbeck authority = uri + i + 2; 468d27446edSbeck break; 469d27446edSbeck } 470d27446edSbeck } 471d27446edSbeck if (authority == NULL) 472d27446edSbeck return 0; 473d27446edSbeck for (i = authority - uri; i < len; i++) { 474d27446edSbeck if (!isascii(uri[i])) 475d27446edSbeck return 0; 476d27446edSbeck /* it has a userinfo part */ 477d27446edSbeck if (uri[i] == '@') { 478d27446edSbeck hostlen = 0; 479d27446edSbeck /* it can only have one */ 480d27446edSbeck if (host != NULL) 481d27446edSbeck break; 482d27446edSbeck /* start after the userinfo part */ 483d27446edSbeck host = uri + i + 1; 484d27446edSbeck continue; 485d27446edSbeck } 486d27446edSbeck /* did we find the end? */ 487d27446edSbeck if (uri[i] == ':' || uri[i] == '/' || uri[i] == '?' || 488d27446edSbeck uri[i] == '#') 489d27446edSbeck break; 490d27446edSbeck hostlen++; 491d27446edSbeck } 492d27446edSbeck if (hostlen == 0) 493d27446edSbeck return 0; 494d27446edSbeck if (host == NULL) 495d27446edSbeck host = authority; 496d27446edSbeck if (!x509_constraints_valid_host(host, hostlen)) 497d27446edSbeck return 0; 498d27446edSbeck *hostpart = strndup(host, hostlen); 499d27446edSbeck return 1; 500d27446edSbeck } 501d27446edSbeck 502d27446edSbeck int 503d27446edSbeck x509_constraints_sandns(char *sandns, size_t dlen, char *constraint, 504d27446edSbeck size_t len) 505d27446edSbeck { 506d27446edSbeck char *suffix; 507d27446edSbeck 508d27446edSbeck if (len == 0) 509d27446edSbeck return 1; /* an empty constraint matches everything */ 510d27446edSbeck 511d27446edSbeck /* match the end of the domain */ 512d27446edSbeck if (dlen < len) 513d27446edSbeck return 0; 514d27446edSbeck suffix = sandns + (dlen - len); 515d27446edSbeck return (strncasecmp(suffix, constraint, len) == 0); 516d27446edSbeck } 517d27446edSbeck 518d27446edSbeck /* 519d27446edSbeck * Validate a pre-validated domain of length dlen against a pre-validated 520d27446edSbeck * constraint of length len. 521d27446edSbeck * 522d27446edSbeck * returns 1 if the domain and constraint match. 523d27446edSbeck * returns 0 otherwise. 524d27446edSbeck * 525d27446edSbeck * an empty constraint matches everyting. 526d27446edSbeck * constraint will be matched against the domain as a suffix if it 527d27446edSbeck * starts with a '.'. 528d27446edSbeck * domain will be matched against the constraint as a suffix if it 529d27446edSbeck * starts with a '.'. 530d27446edSbeck */ 531d27446edSbeck int 532d27446edSbeck x509_constraints_domain(char *domain, size_t dlen, char *constraint, 533d27446edSbeck size_t len) 534d27446edSbeck { 535d27446edSbeck if (len == 0) 536d27446edSbeck return 1; /* an empty constraint matches everything */ 537d27446edSbeck 538d27446edSbeck if (constraint[0] == '.') { 539d27446edSbeck /* match the end of the domain */ 540d27446edSbeck char *suffix; 541d27446edSbeck if (dlen < len) 542d27446edSbeck return 0; 543d27446edSbeck suffix = domain + (dlen - len); 544d27446edSbeck return (strncasecmp(suffix, constraint, len) == 0); 545d27446edSbeck } 546d27446edSbeck if (domain[0] == '.') { 547d27446edSbeck /* match the end of the constraint */ 548d27446edSbeck char *suffix; 549d27446edSbeck if (len < dlen) 550d27446edSbeck return 0; 551d27446edSbeck suffix = constraint + (len - dlen); 552d27446edSbeck return (strncasecmp(suffix, domain, dlen) == 0); 553d27446edSbeck } 554d27446edSbeck /* otherwise we must exactly match the constraint */ 555d27446edSbeck if (dlen != len) 556d27446edSbeck return 0; 557d27446edSbeck return (strncasecmp(domain, constraint, len) == 0); 558d27446edSbeck } 559d27446edSbeck 560d27446edSbeck int 561d27446edSbeck x509_constraints_uri(uint8_t *uri, size_t ulen, uint8_t *constraint, 562d27446edSbeck size_t len, int *error) 563d27446edSbeck { 564d27446edSbeck int ret = 0; 565092bbf76Sbeck char *hostpart = NULL; 566d27446edSbeck 567d27446edSbeck if (!x509_constraints_uri_host(uri, ulen, &hostpart)) { 568d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 569d27446edSbeck goto err; 570d27446edSbeck } 571d27446edSbeck if (hostpart == NULL) { 572d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 573d27446edSbeck goto err; 574d27446edSbeck } 575d27446edSbeck if (!x509_constraints_valid_domain_constraint(constraint, len)) { 576d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX; 577d27446edSbeck goto err; 578d27446edSbeck } 579d27446edSbeck ret = x509_constraints_domain(hostpart, strlen(hostpart), 580d27446edSbeck constraint, len); 581d27446edSbeck err: 582d27446edSbeck free(hostpart); 583d27446edSbeck return ret; 584d27446edSbeck } 585d27446edSbeck 586d27446edSbeck /* 587d27446edSbeck * Verify a validated address of size alen with a validated contraint 588d27446edSbeck * of size constraint_len. returns 1 if matching, 0 if not. 589d27446edSbeck * Addresses are assumed to be pre-validated for a length of 4 and 8 590d27446edSbeck * respectively for ipv4 addreses and constraints, and a length of 591d27446edSbeck * 16 and 32 respectively for ipv6 address constraints by the caller. 592d27446edSbeck */ 593d27446edSbeck int 594d27446edSbeck x509_constraints_ipaddr(uint8_t *address, size_t alen, 595d27446edSbeck uint8_t *constraint, size_t len) 596d27446edSbeck { 597d27446edSbeck uint8_t *mask; 598d27446edSbeck size_t i; 599d27446edSbeck 600d27446edSbeck if (alen * 2 != len) 601d27446edSbeck return 0; 602d27446edSbeck 603d27446edSbeck mask = constraint + alen; 604d27446edSbeck for (i = 0; i < alen; i++) { 605d27446edSbeck if ((address[i] & mask[i]) != (constraint[i] & mask[i])) 606d27446edSbeck return 0; 607d27446edSbeck } 608d27446edSbeck return 1; 609d27446edSbeck } 610d27446edSbeck 611d27446edSbeck /* 612d27446edSbeck * Verify a canonicalized der encoded constraint dirname 613d27446edSbeck * a canonicalized der encoded constraint. 614d27446edSbeck */ 615d27446edSbeck int 616d27446edSbeck x509_constraints_dirname(uint8_t *dirname, size_t dlen, 617d27446edSbeck uint8_t *constraint, size_t len) 618d27446edSbeck { 619d27446edSbeck if (len != dlen) 620d27446edSbeck return 0; 621d27446edSbeck return (memcmp(constraint, dirname, len) == 0); 622d27446edSbeck } 623d27446edSbeck 624d27446edSbeck /* 625d27446edSbeck * De-obfuscate a GENERAL_NAME into useful bytes for a name or constraint. 626d27446edSbeck */ 627d27446edSbeck int 628d27446edSbeck x509_constraints_general_to_bytes(GENERAL_NAME *name, uint8_t **bytes, 629d27446edSbeck size_t *len) 630d27446edSbeck { 631d27446edSbeck *bytes = NULL; 632d27446edSbeck *len = 0; 633d27446edSbeck 634d27446edSbeck if (name->type == GEN_DNS) { 635d27446edSbeck ASN1_IA5STRING *aname = name->d.dNSName; 636d27446edSbeck *bytes = aname->data; 637d27446edSbeck *len = strlen(aname->data); 638d27446edSbeck return name->type; 639d27446edSbeck } 640d27446edSbeck if (name->type == GEN_EMAIL) { 641d27446edSbeck ASN1_IA5STRING *aname = name->d.rfc822Name; 642d27446edSbeck *bytes = aname->data; 643d27446edSbeck *len = strlen(aname->data); 644d27446edSbeck return name->type; 645d27446edSbeck } 646d27446edSbeck if (name->type == GEN_URI) { 647d27446edSbeck ASN1_IA5STRING *aname = name->d.uniformResourceIdentifier; 648d27446edSbeck *bytes = aname->data; 649d27446edSbeck *len = strlen(aname->data); 650d27446edSbeck return name->type; 651d27446edSbeck } 652d27446edSbeck if (name->type == GEN_DIRNAME) { 653d27446edSbeck X509_NAME *dname = name->d.directoryName; 654d27446edSbeck if (!dname->modified || i2d_X509_NAME(dname, NULL) >= 0) { 655d27446edSbeck *bytes = dname->canon_enc; 656d27446edSbeck *len = dname->canon_enclen; 657d27446edSbeck return name->type; 658d27446edSbeck } 659d27446edSbeck } 660d27446edSbeck if (name->type == GEN_IPADD) { 661d27446edSbeck *bytes = name->d.ip->data; 662d27446edSbeck *len = name->d.ip->length; 663d27446edSbeck return name->type; 664d27446edSbeck } 665d27446edSbeck return 0; 666d27446edSbeck } 667d27446edSbeck 668d27446edSbeck 669d27446edSbeck /* 670d27446edSbeck * Extract the relevant names for constraint checking from "cert", 671d27446edSbeck * validate them, and add them to the list of cert names for "chain". 672d27446edSbeck * returns 1 on success sets error and returns 0 on failure. 673d27446edSbeck */ 674d27446edSbeck int 675d27446edSbeck x509_constraints_extract_names(struct x509_constraints_names *names, 676d27446edSbeck X509 *cert, int is_leaf, int *error) 677d27446edSbeck { 678d27446edSbeck struct x509_constraints_name *vname = NULL; 679d27446edSbeck X509_NAME *subject_name; 680d27446edSbeck GENERAL_NAME *name; 681d27446edSbeck ssize_t i = 0; 6823b28baacStb int name_type, include_cn = is_leaf, include_email = is_leaf; 683d27446edSbeck 684d27446edSbeck /* first grab the altnames */ 685d27446edSbeck while ((name = sk_GENERAL_NAME_value(cert->altname, i++)) != NULL) { 686d27446edSbeck uint8_t *bytes = NULL; 687d27446edSbeck size_t len = 0; 688d27446edSbeck 689d27446edSbeck if ((vname = x509_constraints_name_new()) == NULL) { 690d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 691d27446edSbeck goto err; 692d27446edSbeck } 693d27446edSbeck 694d27446edSbeck name_type = x509_constraints_general_to_bytes(name, &bytes, 695d27446edSbeck &len); 696d27446edSbeck switch(name_type) { 697d27446edSbeck case GEN_DNS: 698d27446edSbeck if (!x509_constraints_valid_sandns(bytes, len)) { 699d27446edSbeck *error = 700d27446edSbeck X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 701d27446edSbeck goto err; 702d27446edSbeck } 703d27446edSbeck if ((vname->name = strdup(bytes)) == NULL) { 704d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 705d27446edSbeck goto err; 706d27446edSbeck } 707d27446edSbeck vname->type=GEN_DNS; 708d27446edSbeck include_cn = 0; /* don't use cn from subject */ 709d27446edSbeck break; 710d27446edSbeck case GEN_EMAIL: 711d27446edSbeck if (!x509_constraints_parse_mailbox(bytes, len, 712d27446edSbeck vname)) { 713d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 714d27446edSbeck goto err; 715d27446edSbeck } 716d27446edSbeck vname->type = GEN_EMAIL; 717d27446edSbeck include_email = 0; /* don't use email from subject */ 718d27446edSbeck break; 719d27446edSbeck case GEN_URI: 720d27446edSbeck if (!x509_constraints_uri_host(bytes, len, &vname->name)) { 721d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 722d27446edSbeck goto err; 723d27446edSbeck } 724d27446edSbeck if (vname->name == NULL) { 725d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 726d27446edSbeck goto err; 727d27446edSbeck } 728d27446edSbeck vname->type = GEN_URI; 729d27446edSbeck break; 730d27446edSbeck case GEN_DIRNAME: 731d27446edSbeck if (bytes == NULL || ((vname->der = malloc(len)) == 732d27446edSbeck NULL)) { 733d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 734d27446edSbeck goto err; 735d27446edSbeck } 736d27446edSbeck if (len == 0) { 737d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 738d27446edSbeck goto err; 739d27446edSbeck } 740d27446edSbeck memcpy(vname->der, bytes, len); 741d27446edSbeck vname->der_len = len; 742d27446edSbeck vname->type = GEN_DIRNAME; 743d27446edSbeck break; 744d27446edSbeck case GEN_IPADD: 745d27446edSbeck if (len == 4) 746d27446edSbeck vname->af = AF_INET; 747d27446edSbeck if (len == 16) 748d27446edSbeck vname->af = AF_INET6; 749d27446edSbeck if (vname->af != AF_INET && vname->af != 750d27446edSbeck AF_INET6) { 751d27446edSbeck *error = 752d27446edSbeck X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 753d27446edSbeck goto err; 754d27446edSbeck } 755d27446edSbeck memcpy(vname->address, bytes, len); 756d27446edSbeck vname->type = GEN_IPADD; 757d27446edSbeck break; 758d27446edSbeck default: 759d27446edSbeck /* Ignore this name */ 7603b28baacStb x509_constraints_name_free(vname); 7613b28baacStb vname = NULL; 7623b28baacStb continue; 763d27446edSbeck } 7643b28baacStb if (!x509_constraints_names_add(names, vname)) { 765d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 766d27446edSbeck goto err; 767d27446edSbeck } 768d27446edSbeck vname = NULL; 769d27446edSbeck } 770d27446edSbeck subject_name = X509_get_subject_name(cert); 771d27446edSbeck if (X509_NAME_entry_count(subject_name) > 0) { 772d27446edSbeck X509_NAME_ENTRY *email; 773d27446edSbeck X509_NAME_ENTRY *cn; 774*73cc06fdStb 775*73cc06fdStb x509_constraints_name_free(vname); 776*73cc06fdStb vname = NULL; 777*73cc06fdStb 778d27446edSbeck /* 779d27446edSbeck * This cert has a non-empty subject, so we must add 780d27446edSbeck * the subject as a dirname to be compared against 781d27446edSbeck * any dirname constraints 782d27446edSbeck */ 783d27446edSbeck if ((subject_name->modified && 784d27446edSbeck i2d_X509_NAME(subject_name, NULL) < 0) || 785d27446edSbeck (vname = x509_constraints_name_new()) == NULL || 786d27446edSbeck (vname->der = malloc(subject_name->canon_enclen)) == NULL) { 787d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 788d27446edSbeck goto err; 789d27446edSbeck } 790d27446edSbeck 791d27446edSbeck memcpy(vname->der, subject_name->canon_enc, 792d27446edSbeck subject_name->canon_enclen); 793d27446edSbeck vname->der_len = subject_name->canon_enclen; 794d27446edSbeck vname->type = GEN_DIRNAME; 795d27446edSbeck if (!x509_constraints_names_add(names, vname)) { 796d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 797d27446edSbeck goto err; 798d27446edSbeck } 799d27446edSbeck vname = NULL; 800d27446edSbeck /* 801d27446edSbeck * Get any email addresses from the subject line, and 802d27446edSbeck * add them as mbox names to be compared against any 803d27446edSbeck * email constraints 804d27446edSbeck */ 805d27446edSbeck while (include_email && 806d27446edSbeck (i = X509_NAME_get_index_by_NID(subject_name, 807d27446edSbeck NID_pkcs9_emailAddress, i)) >= 0) { 808d27446edSbeck ASN1_STRING *aname; 809d27446edSbeck if ((email = X509_NAME_get_entry(subject_name, i)) == NULL || 810d27446edSbeck (aname = X509_NAME_ENTRY_get_data(email)) == NULL) { 811d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 812d27446edSbeck goto err; 813d27446edSbeck } 814d27446edSbeck if ((vname = x509_constraints_name_new()) == NULL) { 815d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 816d27446edSbeck goto err; 817d27446edSbeck } 818d27446edSbeck if (!x509_constraints_parse_mailbox(aname->data, 81955bfdebcSbeck aname->length, vname)) { 820d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX; 821d27446edSbeck goto err; 822d27446edSbeck } 823d27446edSbeck vname->type = GEN_EMAIL; 824d27446edSbeck if (!x509_constraints_names_add(names, vname)) { 825d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 826d27446edSbeck goto err; 827d27446edSbeck } 828d27446edSbeck vname = NULL; 829d27446edSbeck } 830d27446edSbeck /* 831d27446edSbeck * Include the CN as a hostname to be checked againt 832d27446edSbeck * name constraints if it looks like a hostname. 833d27446edSbeck */ 834d27446edSbeck while (include_cn && 835d27446edSbeck (i = X509_NAME_get_index_by_NID(subject_name, 836d27446edSbeck NID_commonName, i)) >= 0) { 837d27446edSbeck ASN1_STRING *aname; 838d27446edSbeck if ((cn = X509_NAME_get_entry(subject_name, i)) == NULL || 839d27446edSbeck (aname = X509_NAME_ENTRY_get_data(cn)) == NULL) { 840d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 841d27446edSbeck goto err; 842d27446edSbeck } 843d27446edSbeck if (!x509_constraints_valid_host(aname->data, 84455bfdebcSbeck aname->length)) 845d27446edSbeck continue; /* ignore it if not a hostname */ 846d27446edSbeck if ((vname = x509_constraints_name_new()) == NULL) { 847d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 848d27446edSbeck goto err; 849d27446edSbeck } 85055bfdebcSbeck if ((vname->name = strndup(aname->data, 85155bfdebcSbeck aname->length)) == NULL) { 852d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 853d27446edSbeck goto err; 854d27446edSbeck } 855d27446edSbeck vname->type = GEN_DNS; 856d27446edSbeck if (!x509_constraints_names_add(names, vname)) { 857d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 858d27446edSbeck goto err; 859d27446edSbeck } 860d27446edSbeck vname = NULL; 861d27446edSbeck } 862d27446edSbeck } 863d27446edSbeck return 1; 864d27446edSbeck err: 865d27446edSbeck x509_constraints_name_free(vname); 866d27446edSbeck return 0; 867d27446edSbeck } 868d27446edSbeck 869d27446edSbeck /* 870d27446edSbeck * Validate a constraint in a general name, putting the relevant data 871d27446edSbeck * into "name" if valid. returns 0, and sets error if the constraint is 872d27446edSbeck * not valid. returns 1 if the constraint validated. name->type will be 873d27446edSbeck * set to a valid type if there is constraint data in name, or unmodified 874d27446edSbeck * if the GENERAL_NAME had a valid type but was ignored. 875d27446edSbeck */ 876d27446edSbeck int 877d27446edSbeck x509_constraints_validate(GENERAL_NAME *constraint, 878d27446edSbeck struct x509_constraints_name *name, int *error) 879d27446edSbeck { 880d27446edSbeck uint8_t *bytes = NULL; 881d27446edSbeck size_t len = 0; 882d27446edSbeck int name_type; 883d27446edSbeck 884d27446edSbeck name_type = x509_constraints_general_to_bytes(constraint, &bytes, &len); 885d27446edSbeck switch (name_type) { 886d27446edSbeck case GEN_DIRNAME: 887d27446edSbeck if (bytes == NULL || (name->der = malloc(len)) == NULL) { 888d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 889d27446edSbeck return 0; 890d27446edSbeck } 891d27446edSbeck if (len == 0) 892d27446edSbeck goto err; /* XXX The RFC's are delightfully vague */ 893d27446edSbeck memcpy(name->der, bytes, len); 894d27446edSbeck name->der_len = len; 895d27446edSbeck name->type = GEN_DIRNAME; 896d27446edSbeck break; 897d27446edSbeck case GEN_DNS: 898d27446edSbeck if (!x509_constraints_valid_domain_constraint(bytes, 899d27446edSbeck len)) 900d27446edSbeck goto err; 901d27446edSbeck if ((name->name = strdup(bytes)) == NULL) { 902d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 903d27446edSbeck return 0; 904d27446edSbeck } 905d27446edSbeck name->type = GEN_DNS; 906d27446edSbeck break; 907d27446edSbeck case GEN_EMAIL: 908d27446edSbeck if (memchr(bytes, '@', len) != NULL) { 909d27446edSbeck if (!x509_constraints_parse_mailbox(bytes, len, 910d27446edSbeck name)) 911d27446edSbeck goto err; 912d27446edSbeck } else { 913d27446edSbeck if (!x509_constraints_valid_domain_constraint( 914d27446edSbeck bytes, len)) 915d27446edSbeck goto err; 916d27446edSbeck if ((name->name = strdup(bytes)) == NULL) { 917d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 918d27446edSbeck return 0; 919d27446edSbeck } 920d27446edSbeck } 921d27446edSbeck name->type = GEN_EMAIL; 922d27446edSbeck break; 923d27446edSbeck case GEN_IPADD: 924d27446edSbeck /* Constraints are ip then mask */ 925d27446edSbeck if (len == 8) 926d27446edSbeck name->af = AF_INET; 927d27446edSbeck else if (len == 32) 928d27446edSbeck name->af = AF_INET6; 929d27446edSbeck else 930d27446edSbeck goto err; 931d27446edSbeck memcpy(&name->address[0], bytes, len); 932d27446edSbeck name->type = GEN_IPADD; 933d27446edSbeck break; 934d27446edSbeck case GEN_URI: 935d27446edSbeck if (!x509_constraints_valid_domain_constraint(bytes, 936d27446edSbeck len)) 937d27446edSbeck goto err; 938d27446edSbeck name->name = strdup(bytes); 939d27446edSbeck name->type = GEN_URI; 940d27446edSbeck break; 941d27446edSbeck default: 942d27446edSbeck break; 943d27446edSbeck } 944d27446edSbeck return 1; 945d27446edSbeck err: 946d27446edSbeck *error = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX; 947d27446edSbeck return 0; 948d27446edSbeck } 949d27446edSbeck 950d27446edSbeck int 951d27446edSbeck x509_constraints_extract_constraints(X509 *cert, 952d27446edSbeck struct x509_constraints_names *permitted, 953d27446edSbeck struct x509_constraints_names *excluded, 954d27446edSbeck int *error) 955d27446edSbeck { 956d27446edSbeck struct x509_constraints_name *vname; 957d27446edSbeck NAME_CONSTRAINTS *nc = cert->nc; 958d27446edSbeck GENERAL_SUBTREE *subtree; 959d27446edSbeck int i; 960d27446edSbeck 961d27446edSbeck if (nc == NULL) 962d27446edSbeck return 1; 963d27446edSbeck 964d27446edSbeck for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->permittedSubtrees); i++) { 965d27446edSbeck 966d27446edSbeck subtree = sk_GENERAL_SUBTREE_value(nc->permittedSubtrees, i); 967d27446edSbeck if (subtree->minimum || subtree->maximum) { 968d27446edSbeck *error = X509_V_ERR_SUBTREE_MINMAX; 969d27446edSbeck return 0; 970d27446edSbeck } 971d27446edSbeck if ((vname = x509_constraints_name_new()) == NULL) { 972d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 973d27446edSbeck return 0; 974d27446edSbeck } 97569d5250dSbeck if (x509_constraints_validate(subtree->base, vname, error) == 97669d5250dSbeck 0) { 977d27446edSbeck x509_constraints_name_free(vname); 978d27446edSbeck return 0; 979d27446edSbeck } 980d27446edSbeck if (vname->type == 0) { 981d27446edSbeck x509_constraints_name_free(vname); 982d27446edSbeck continue; 983d27446edSbeck } 984d27446edSbeck if (!x509_constraints_names_add(permitted, vname)) { 985d27446edSbeck x509_constraints_name_free(vname); 986d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 987d27446edSbeck return 0; 988d27446edSbeck } 989d27446edSbeck } 990d27446edSbeck 991d27446edSbeck for (i = 0; i < sk_GENERAL_SUBTREE_num(nc->excludedSubtrees); i++) { 992d27446edSbeck subtree = sk_GENERAL_SUBTREE_value(nc->excludedSubtrees, i); 993d27446edSbeck if (subtree->minimum || subtree->maximum) { 994d27446edSbeck *error = X509_V_ERR_SUBTREE_MINMAX; 995d27446edSbeck return 0; 996d27446edSbeck } 997d27446edSbeck if ((vname = x509_constraints_name_new()) == NULL) { 998d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 999d27446edSbeck return 0; 1000d27446edSbeck } 100169d5250dSbeck if (x509_constraints_validate(subtree->base, vname, error) == 100269d5250dSbeck 0) { 1003d27446edSbeck x509_constraints_name_free(vname); 1004d27446edSbeck return 0; 1005d27446edSbeck } 1006d27446edSbeck if (vname->type == 0) { 1007d27446edSbeck x509_constraints_name_free(vname); 1008d27446edSbeck continue; 1009d27446edSbeck } 1010d27446edSbeck if (!x509_constraints_names_add(excluded, vname)) { 1011d27446edSbeck x509_constraints_name_free(vname); 1012d27446edSbeck *error = X509_V_ERR_OUT_OF_MEM; 1013d27446edSbeck return 0; 1014d27446edSbeck } 1015d27446edSbeck } 1016d27446edSbeck 1017d27446edSbeck return 1; 1018d27446edSbeck } 1019d27446edSbeck 1020d27446edSbeck /* 1021d27446edSbeck * Match a validated name in "name" against a validated constraint in 1022d27446edSbeck * "constraint" return 1 if then name matches, 0 otherwise. 1023d27446edSbeck */ 1024d27446edSbeck int 1025d27446edSbeck x509_constraints_match(struct x509_constraints_name *name, 1026d27446edSbeck struct x509_constraints_name *constraint) 1027d27446edSbeck { 1028d27446edSbeck if (name->type != constraint->type) 1029d27446edSbeck return 0; 1030d27446edSbeck if (name->type == GEN_DNS) 1031d27446edSbeck return x509_constraints_sandns(name->name, 1032d27446edSbeck strlen(name->name), constraint->name, 1033d27446edSbeck strlen(constraint->name)); 1034d27446edSbeck if (name->type == GEN_URI) 1035d27446edSbeck return x509_constraints_domain(name->name, 1036d27446edSbeck strlen(name->name), constraint->name, 1037d27446edSbeck strlen(constraint->name)); 1038d27446edSbeck if (name->type == GEN_IPADD) { 1039d27446edSbeck size_t nlen = name->af == AF_INET ? 4 : 16; 1040d27446edSbeck size_t clen = name->af == AF_INET ? 8 : 32; 1041d27446edSbeck if (name->af != AF_INET && name->af != AF_INET6) 1042d27446edSbeck return 0; 1043d27446edSbeck if (constraint->af != AF_INET && constraint->af != AF_INET6) 1044d27446edSbeck return 0; 1045d27446edSbeck if (name->af != constraint->af) 1046d27446edSbeck return 0; 1047d27446edSbeck return x509_constraints_ipaddr(name->address, 1048d27446edSbeck nlen, constraint->address, clen); 1049d27446edSbeck } 1050d27446edSbeck if (name->type == GEN_EMAIL) { 1051d27446edSbeck if (constraint->local) { 1052d27446edSbeck /* mailbox local and domain parts must exactly match */ 1053d27446edSbeck return (strcmp(name->local, constraint->local) == 0 && 1054d27446edSbeck strcmp(name->name, constraint->name) == 0); 1055d27446edSbeck } 1056d27446edSbeck /* otherwise match the constraint to the domain part */ 1057d27446edSbeck return x509_constraints_domain(name->name, 1058d27446edSbeck strlen(name->name), constraint->name, 1059d27446edSbeck strlen(constraint->name)); 1060d27446edSbeck } 1061d27446edSbeck if (name->type == GEN_DIRNAME) 1062d27446edSbeck return x509_constraints_dirname(name->der, name->der_len, 1063d27446edSbeck constraint->der, constraint->der_len); 1064d27446edSbeck return 0; 1065d27446edSbeck } 1066d27446edSbeck 1067d27446edSbeck /* 1068d27446edSbeck * Make sure every name in names does not match any excluded 1069d27446edSbeck * constraints, and does match at least one permitted constraint if 1070d27446edSbeck * any are present. Returns 1 if ok, 0, and sets error if not. 1071d27446edSbeck */ 1072d27446edSbeck int 1073d27446edSbeck x509_constraints_check(struct x509_constraints_names *names, 1074d27446edSbeck struct x509_constraints_names *permitted, 1075d27446edSbeck struct x509_constraints_names *excluded, int *error) 1076d27446edSbeck { 1077d27446edSbeck size_t i, j; 1078d27446edSbeck 1079d27446edSbeck for (i = 0; i < names->names_count; i++) { 1080d27446edSbeck int permitted_seen = 0; 1081d27446edSbeck int permitted_matched = 0; 1082d27446edSbeck 1083d27446edSbeck for (j = 0; j < excluded->names_count; j++) { 1084d27446edSbeck if (x509_constraints_match(names->names[i], 1085d27446edSbeck excluded->names[j])) { 1086d27446edSbeck *error = X509_V_ERR_EXCLUDED_VIOLATION; 1087d27446edSbeck return 0; 1088d27446edSbeck } 1089d27446edSbeck } 1090d27446edSbeck for (j = 0; j < permitted->names_count; j++) { 1091d27446edSbeck if (permitted->names[j]->type == names->names[i]->type) 1092d27446edSbeck permitted_seen++; 1093d27446edSbeck if (x509_constraints_match(names->names[i], 1094d27446edSbeck permitted->names[j])) { 1095d27446edSbeck permitted_matched++; 1096d27446edSbeck break; 1097d27446edSbeck } 1098d27446edSbeck } 1099d27446edSbeck if (permitted_seen && !permitted_matched) { 1100d27446edSbeck *error = X509_V_ERR_PERMITTED_VIOLATION; 1101d27446edSbeck return 0; 1102d27446edSbeck } 1103d27446edSbeck } 1104d27446edSbeck return 1; 1105d27446edSbeck } 1106d27446edSbeck 1107d27446edSbeck /* 1108d27446edSbeck * Walk a validated chain of X509 certs, starting at the leaf, and 1109d27446edSbeck * validate the name constraints in the chain. Intended for use with 1110d27446edSbeck * the legacy X509 validtion code in x509_vfy.c 1111d27446edSbeck * 1112d27446edSbeck * returns 1 if the constraints are ok, 0 otherwise, setting error and 1113d27446edSbeck * depth 1114d27446edSbeck */ 1115d27446edSbeck int 1116d27446edSbeck x509_constraints_chain(STACK_OF(X509) *chain, int *error, int *depth) 1117d27446edSbeck { 1118d27446edSbeck int chain_length, verify_err = X509_V_ERR_UNSPECIFIED, i = 0; 1119d27446edSbeck struct x509_constraints_names *names = NULL; 1120d27446edSbeck struct x509_constraints_names *excluded = NULL; 1121d27446edSbeck struct x509_constraints_names *permitted = NULL; 1122d27446edSbeck size_t constraints_count = 0; 1123d27446edSbeck X509 *cert; 1124d27446edSbeck 1125d27446edSbeck if (chain == NULL || (chain_length = sk_X509_num(chain)) == 0) 1126d27446edSbeck goto err; 1127d27446edSbeck if (chain_length == 1) 1128d27446edSbeck return 1; 1129d27446edSbeck if ((names = x509_constraints_names_new()) == NULL) { 1130d27446edSbeck verify_err = X509_V_ERR_OUT_OF_MEM; 1131d27446edSbeck goto err; 1132d27446edSbeck } 1133d27446edSbeck 1134d27446edSbeck if ((cert = sk_X509_value(chain, 0)) == NULL) 1135d27446edSbeck goto err; 1136d27446edSbeck if (!x509_constraints_extract_names(names, cert, 1, &verify_err)) 1137d27446edSbeck goto err; 1138d27446edSbeck for (i = 1; i < chain_length; i++) { 1139d27446edSbeck if ((cert = sk_X509_value(chain, i)) == NULL) 1140d27446edSbeck goto err; 1141d27446edSbeck if (cert->nc != NULL) { 1142d27446edSbeck if ((permitted = 1143d27446edSbeck x509_constraints_names_new()) == NULL) { 1144d27446edSbeck verify_err = X509_V_ERR_OUT_OF_MEM; 1145d27446edSbeck goto err; 1146d27446edSbeck } 1147d27446edSbeck if ((excluded = 1148d27446edSbeck x509_constraints_names_new()) == NULL) { 1149d27446edSbeck verify_err = X509_V_ERR_OUT_OF_MEM; 1150d27446edSbeck goto err; 1151d27446edSbeck } 1152d27446edSbeck if (!x509_constraints_extract_constraints(cert, 1153d27446edSbeck permitted, excluded, &verify_err)) 1154d27446edSbeck goto err; 1155d27446edSbeck constraints_count += permitted->names_count; 1156d27446edSbeck constraints_count += excluded->names_count; 1157d27446edSbeck if (constraints_count > 1158d27446edSbeck X509_VERIFY_MAX_CHAIN_CONSTRAINTS) { 1159d27446edSbeck verify_err = X509_V_ERR_OUT_OF_MEM; 1160d27446edSbeck goto err; 1161d27446edSbeck } 1162d27446edSbeck if (!x509_constraints_check(names, permitted, 1163d27446edSbeck excluded, &verify_err)) 1164d27446edSbeck goto err; 1165d27446edSbeck x509_constraints_names_free(excluded); 1166d27446edSbeck excluded = NULL; 1167d27446edSbeck x509_constraints_names_free(permitted); 1168d27446edSbeck permitted = NULL; 1169d27446edSbeck } 1170d27446edSbeck if (!x509_constraints_extract_names(names, cert, 0, 1171d27446edSbeck &verify_err)) 1172d27446edSbeck goto err; 1173d27446edSbeck if (names->names_count > X509_VERIFY_MAX_CHAIN_NAMES) { 1174d27446edSbeck verify_err = X509_V_ERR_OUT_OF_MEM; 1175d27446edSbeck goto err; 1176d27446edSbeck } 1177d27446edSbeck } 1178d27446edSbeck 1179ab0f9808Sbeck x509_constraints_names_free(names); 1180d27446edSbeck return 1; 1181d27446edSbeck 1182d27446edSbeck err: 1183d27446edSbeck *error = verify_err; 1184d27446edSbeck *depth = i; 1185d27446edSbeck x509_constraints_names_free(excluded); 1186d27446edSbeck x509_constraints_names_free(permitted); 1187d27446edSbeck x509_constraints_names_free(names); 1188d27446edSbeck return 0; 1189d27446edSbeck } 1190