1*381ee599Stb /* $OpenBSD: constraints.c,v 1.5 2024/11/12 09:23:07 tb Exp $ */ 2891d6bceSjob /* 3891d6bceSjob * Copyright (c) 2023 Job Snijders <job@openbsd.org> 4891d6bceSjob * Copyright (c) 2023 Theo Buehler <tb@openbsd.org> 5891d6bceSjob * 6891d6bceSjob * Permission to use, copy, modify, and distribute this software for any 7891d6bceSjob * purpose with or without fee is hereby granted, provided that the above 8891d6bceSjob * copyright notice and this permission notice appear in all copies. 9891d6bceSjob * 10891d6bceSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11891d6bceSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12891d6bceSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13891d6bceSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14891d6bceSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15891d6bceSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16891d6bceSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17891d6bceSjob */ 18891d6bceSjob 19891d6bceSjob #include <sys/socket.h> 20891d6bceSjob 21891d6bceSjob #include <arpa/inet.h> 22891d6bceSjob 23891d6bceSjob #include <ctype.h> 24891d6bceSjob #include <err.h> 25891d6bceSjob #include <errno.h> 26891d6bceSjob #include <fcntl.h> 2789e02b0fSjob #include <libgen.h> 28891d6bceSjob #include <stdint.h> 29891d6bceSjob #include <stdio.h> 30891d6bceSjob #include <stdlib.h> 31891d6bceSjob #include <string.h> 32891d6bceSjob #include <unistd.h> 33891d6bceSjob 34891d6bceSjob #include <openssl/asn1.h> 35891d6bceSjob #include <openssl/x509v3.h> 36891d6bceSjob 37891d6bceSjob #include "extern.h" 38891d6bceSjob 39891d6bceSjob struct tal_constraints { 40891d6bceSjob int fd; /* constraints file descriptor or -1. */ 41891d6bceSjob char *fn; /* constraints filename */ 4289e02b0fSjob char *warn; /* warning msg used for violations */ 43891d6bceSjob struct cert_ip *allow_ips; /* list of allowed IP address ranges */ 44*381ee599Stb size_t num_allow_ips; 45*381ee599Stb struct cert_as *allow_ases; /* allowed AS numbers and ranges */ 46*381ee599Stb size_t num_allow_ases; 47891d6bceSjob struct cert_ip *deny_ips; /* forbidden IP address ranges */ 48*381ee599Stb size_t num_deny_ips; 49*381ee599Stb struct cert_as *deny_ases; /* forbidden AS numbers and ranges */ 50*381ee599Stb size_t num_deny_ases; 51891d6bceSjob } tal_constraints[TALSZ_MAX]; 52891d6bceSjob 53891d6bceSjob /* 54891d6bceSjob * If there is a .constraints file next to a .tal file, load its contents 55891d6bceSjob * into into tal_constraints[talid]. The load function only opens the fd 56891d6bceSjob * and stores the filename. The actual parsing happens in constraints_parse(). 57891d6bceSjob * Resources of EE certs can then be constrained using constraints_validate(). 58891d6bceSjob */ 59891d6bceSjob 60891d6bceSjob static void 61891d6bceSjob constraints_load_talid(int talid) 62891d6bceSjob { 63891d6bceSjob const char *tal = tals[talid]; 6489e02b0fSjob char *constraints = NULL, *warning = NULL, *cbn; 65891d6bceSjob int fd; 66891d6bceSjob size_t len; 67891d6bceSjob int saved_errno; 68891d6bceSjob 69891d6bceSjob tal_constraints[talid].fd = -1; 70891d6bceSjob 71891d6bceSjob if (rtype_from_file_extension(tal) != RTYPE_TAL) 72891d6bceSjob return; 73891d6bceSjob 74891d6bceSjob /* Replace .tal suffix with .constraints. */ 75891d6bceSjob len = strlen(tal) - 4; 76891d6bceSjob if (asprintf(&constraints, "%.*s.constraints", (int)len, tal) == -1) 7789e02b0fSjob err(1, NULL); 7889e02b0fSjob 7989e02b0fSjob /* prepare warning message for when violations are detected */ 8089e02b0fSjob if ((cbn = basename(constraints)) == NULL) 8189e02b0fSjob err(1, "basename"); 8289e02b0fSjob if (asprintf(&warning, "resource violates %s", cbn) == -1) 8389e02b0fSjob err(1, NULL); 84891d6bceSjob 85891d6bceSjob saved_errno = errno; 86891d6bceSjob 87891d6bceSjob fd = open(constraints, O_RDONLY); 88891d6bceSjob if (fd == -1 && errno != ENOENT) 89891d6bceSjob err(1, "failed to load constraints for %s", tal); 90891d6bceSjob 91891d6bceSjob tal_constraints[talid].fn = constraints; 92891d6bceSjob tal_constraints[talid].fd = fd; 9389e02b0fSjob tal_constraints[talid].warn = warning; 94891d6bceSjob 95891d6bceSjob errno = saved_errno; 96891d6bceSjob } 97891d6bceSjob 98891d6bceSjob /* 99891d6bceSjob * Iterate over all TALs and load the corresponding constraints files. 100891d6bceSjob */ 101891d6bceSjob void 102891d6bceSjob constraints_load(void) 103891d6bceSjob { 104891d6bceSjob int talid; 105891d6bceSjob 106891d6bceSjob for (talid = 0; talid < talsz; talid++) 107891d6bceSjob constraints_load_talid(talid); 108891d6bceSjob } 109891d6bceSjob 110891d6bceSjob void 111891d6bceSjob constraints_unload(void) 112891d6bceSjob { 113891d6bceSjob int saved_errno, talid; 114891d6bceSjob 115891d6bceSjob saved_errno = errno; 116891d6bceSjob for (talid = 0; talid < talsz; talid++) { 117891d6bceSjob if (tal_constraints[talid].fd != -1) 118891d6bceSjob close(tal_constraints[talid].fd); 119891d6bceSjob free(tal_constraints[talid].fn); 12089e02b0fSjob free(tal_constraints[talid].warn); 121891d6bceSjob tal_constraints[talid].fd = -1; 122891d6bceSjob tal_constraints[talid].fn = NULL; 12389e02b0fSjob tal_constraints[talid].warn = NULL; 124891d6bceSjob } 125891d6bceSjob errno = saved_errno; 126891d6bceSjob } 127891d6bceSjob 128891d6bceSjob /* 129891d6bceSjob * Split a string at '-' and trim whitespace around the '-'. 130891d6bceSjob * Assumes leading and trailing whitespace in p has already been trimmed. 131891d6bceSjob */ 132891d6bceSjob static int 133891d6bceSjob constraints_split_range(char *p, const char **min, const char **max) 134891d6bceSjob { 135891d6bceSjob char *pp; 136891d6bceSjob 137891d6bceSjob *min = p; 138891d6bceSjob if ((*max = pp = strchr(p, '-')) == NULL) 139891d6bceSjob return 0; 140891d6bceSjob 141891d6bceSjob /* Trim whitespace before '-'. */ 142891d6bceSjob while (pp > *min && isspace((unsigned char)pp[-1])) 143891d6bceSjob pp--; 144891d6bceSjob *pp = '\0'; 145891d6bceSjob 146891d6bceSjob /* Skip past '-' and whitespace following it. */ 147891d6bceSjob (*max)++; 148891d6bceSjob while (isspace((unsigned char)**max)) 149891d6bceSjob (*max)++; 150891d6bceSjob 151891d6bceSjob return 1; 152891d6bceSjob } 153891d6bceSjob 154891d6bceSjob /* 155891d6bceSjob * Helper functions to parse textual representations of IP prefixes or ranges. 156891d6bceSjob * The RFC 3779 API has poor error reporting, so as a debugging aid, we call 157891d6bceSjob * the prohibitively expensive X509v3_addr_canonize() in high verbosity mode. 158891d6bceSjob */ 159891d6bceSjob 160891d6bceSjob static void 161891d6bceSjob constraints_parse_ip_prefix(const char *fn, const char *prefix, enum afi afi, 162891d6bceSjob IPAddrBlocks *addrs) 163891d6bceSjob { 164891d6bceSjob unsigned char addr[16] = { 0 }; 165891d6bceSjob int af = afi == AFI_IPV4 ? AF_INET : AF_INET6; 166891d6bceSjob int plen; 167891d6bceSjob 168891d6bceSjob if ((plen = inet_net_pton(af, prefix, addr, sizeof(addr))) == -1) 169891d6bceSjob errx(1, "%s: failed to parse %s", fn, prefix); 170891d6bceSjob 171891d6bceSjob if (!X509v3_addr_add_prefix(addrs, afi, NULL, addr, plen)) 172891d6bceSjob errx(1, "%s: failed to add prefix %s", fn, prefix); 173891d6bceSjob 174891d6bceSjob if (verbose < 3) 175891d6bceSjob return; 176891d6bceSjob 177891d6bceSjob if (!X509v3_addr_canonize(addrs)) 178891d6bceSjob errx(1, "%s: failed to canonize with prefix %s", fn, prefix); 179891d6bceSjob } 180891d6bceSjob 181891d6bceSjob static void 182891d6bceSjob constraints_parse_ip_range(const char *fn, const char *min, const char *max, 183891d6bceSjob enum afi afi, IPAddrBlocks *addrs) 184891d6bceSjob { 185891d6bceSjob unsigned char min_addr[16] = {0}, max_addr[16] = {0}; 186891d6bceSjob int af = afi == AFI_IPV4 ? AF_INET : AF_INET6; 187891d6bceSjob 188891d6bceSjob if (inet_pton(af, min, min_addr) != 1) 189891d6bceSjob errx(1, "%s: failed to parse %s", fn, min); 190891d6bceSjob if (inet_pton(af, max, max_addr) != 1) 191891d6bceSjob errx(1, "%s: failed to parse %s", fn, max); 192891d6bceSjob 193891d6bceSjob if (!X509v3_addr_add_range(addrs, afi, NULL, min_addr, max_addr)) 194891d6bceSjob errx(1, "%s: failed to add range %s--%s", fn, min, max); 195891d6bceSjob 196891d6bceSjob if (verbose < 3) 197891d6bceSjob return; 198891d6bceSjob 199891d6bceSjob if (!X509v3_addr_canonize(addrs)) 200891d6bceSjob errx(1, "%s: failed to canonize with range %s--%s", fn, 201891d6bceSjob min, max); 202891d6bceSjob } 203891d6bceSjob 204891d6bceSjob static void 205891d6bceSjob constraints_parse_ip(const char *fn, char *p, enum afi afi, IPAddrBlocks *addrs) 206891d6bceSjob { 207891d6bceSjob const char *min, *max; 208891d6bceSjob 209891d6bceSjob if (strchr(p, '-') == NULL) { 210891d6bceSjob constraints_parse_ip_prefix(fn, p, afi, addrs); 211891d6bceSjob return; 212891d6bceSjob } 213891d6bceSjob 214891d6bceSjob if (!constraints_split_range(p, &min, &max)) 215891d6bceSjob errx(1, "%s: failed to split range: %s", fn, p); 216891d6bceSjob 217891d6bceSjob constraints_parse_ip_range(fn, min, max, afi, addrs); 218891d6bceSjob } 219891d6bceSjob 220891d6bceSjob /* 221891d6bceSjob * Helper functions to parse textual representations of AS numbers or ranges. 222891d6bceSjob * The RFC 3779 API has poor error reporting, so as a debugging aid, we call 223891d6bceSjob * the prohibitively expensive X509v3_asid_canonize() in high verbosity mode. 224891d6bceSjob */ 225891d6bceSjob 226891d6bceSjob static void 227891d6bceSjob constraints_parse_asn(const char *fn, const char *asn, ASIdentifiers *asids) 228891d6bceSjob { 229891d6bceSjob ASN1_INTEGER *id; 230891d6bceSjob 231891d6bceSjob if ((id = s2i_ASN1_INTEGER(NULL, asn)) == NULL) 232891d6bceSjob errx(1, "%s: failed to parse AS %s", fn, asn); 233891d6bceSjob 234891d6bceSjob if (!X509v3_asid_add_id_or_range(asids, V3_ASID_ASNUM, id, NULL)) 235891d6bceSjob errx(1, "%s: failed to add AS %s", fn, asn); 236891d6bceSjob 237891d6bceSjob if (verbose < 3) 238891d6bceSjob return; 239891d6bceSjob 240891d6bceSjob if (!X509v3_asid_canonize(asids)) 241891d6bceSjob errx(1, "%s: failed to canonize with AS %s", fn, asn); 242891d6bceSjob } 243891d6bceSjob 244891d6bceSjob static void 245891d6bceSjob constraints_parse_asn_range(const char *fn, const char *min, const char *max, 246891d6bceSjob ASIdentifiers *asids) 247891d6bceSjob { 248891d6bceSjob ASN1_INTEGER *min_as, *max_as; 249891d6bceSjob 250891d6bceSjob if ((min_as = s2i_ASN1_INTEGER(NULL, min)) == NULL) 251891d6bceSjob errx(1, "%s: failed to parse AS %s", fn, min); 252891d6bceSjob if ((max_as = s2i_ASN1_INTEGER(NULL, max)) == NULL) 253891d6bceSjob errx(1, "%s: failed to parse AS %s", fn, max); 254891d6bceSjob 255891d6bceSjob if (!X509v3_asid_add_id_or_range(asids, V3_ASID_ASNUM, min_as, max_as)) 256891d6bceSjob errx(1, "%s: failed to add AS range %s--%s", fn, min, max); 257891d6bceSjob 258891d6bceSjob if (verbose < 3) 259891d6bceSjob return; 260891d6bceSjob 261891d6bceSjob if (!X509v3_asid_canonize(asids)) 262891d6bceSjob errx(1, "%s: failed to canonize with AS range %s--%s", fn, 263891d6bceSjob min, max); 264891d6bceSjob } 265891d6bceSjob 266891d6bceSjob static void 267891d6bceSjob constraints_parse_as(const char *fn, char *p, ASIdentifiers *asids) 268891d6bceSjob { 269891d6bceSjob const char *min, *max; 270891d6bceSjob 271891d6bceSjob if (strchr(p, '-') == NULL) { 272891d6bceSjob constraints_parse_asn(fn, p, asids); 273891d6bceSjob return; 274891d6bceSjob } 275891d6bceSjob 276891d6bceSjob if (!constraints_split_range(p, &min, &max)) 277891d6bceSjob errx(1, "%s: failed to split range: %s", fn, p); 278891d6bceSjob 279891d6bceSjob constraints_parse_asn_range(fn, min, max, asids); 280891d6bceSjob } 281891d6bceSjob 282891d6bceSjob /* 283891d6bceSjob * Work around an annoying bug in X509v3_addr_add_range(). The upper bound 284891d6bceSjob * of a range can have unused bits set in its ASN1_BIT_STRING representation. 285891d6bceSjob * This triggers a check in ip_addr_parse(). A round trip through DER fixes 286891d6bceSjob * this mess up. For extra special fun, {d2i,i2d}_IPAddrBlocks() isn't part 287891d6bceSjob * of the API and implementing them for OpenSSL 3 is hairy, so do the round 288891d6bceSjob * tripping once per address family. 289891d6bceSjob */ 290891d6bceSjob static void 291891d6bceSjob constraints_normalize_ip_addrblocks(const char *fn, IPAddrBlocks **addrs) 292891d6bceSjob { 293891d6bceSjob IPAddrBlocks *new_addrs; 294891d6bceSjob IPAddressFamily *af; 295891d6bceSjob const unsigned char *p; 296891d6bceSjob unsigned char *der; 297891d6bceSjob int der_len, i; 298891d6bceSjob 299891d6bceSjob if ((new_addrs = IPAddrBlocks_new()) == NULL) 300891d6bceSjob err(1, NULL); 301891d6bceSjob 302891d6bceSjob for (i = 0; i < sk_IPAddressFamily_num(*addrs); i++) { 303891d6bceSjob af = sk_IPAddressFamily_value(*addrs, i); 304891d6bceSjob 305891d6bceSjob der = NULL; 306891d6bceSjob if ((der_len = i2d_IPAddressFamily(af, &der)) <= 0) 307891d6bceSjob errx(1, "%s: failed to convert to DER", fn); 308891d6bceSjob p = der; 309891d6bceSjob if ((af = d2i_IPAddressFamily(NULL, &p, der_len)) == NULL) 310891d6bceSjob errx(1, "%s: failed to convert from DER", fn); 311891d6bceSjob free(der); 312891d6bceSjob 313891d6bceSjob if (!sk_IPAddressFamily_push(new_addrs, af)) 314891d6bceSjob errx(1, "%s: failed to push constraints", fn); 315891d6bceSjob } 316891d6bceSjob 317891d6bceSjob IPAddrBlocks_free(*addrs); 318891d6bceSjob *addrs = new_addrs; 319891d6bceSjob } 320891d6bceSjob 321891d6bceSjob /* 322891d6bceSjob * If there is a constraints file for tals[talid], load it into a buffer 323891d6bceSjob * and parse it line by line. Leverage the above parse helpers to build up 324891d6bceSjob * IPAddrBlocks and ASIdentifiers. We use the RFC 3779 API to benefit from 325891d6bceSjob * the limited abilities of X509v3_{addr,asid}_canonize() to sort and merge 326891d6bceSjob * adjacent ranges. This doesn't deal with overlaps or duplicates, but it's 327891d6bceSjob * better than nothing. 328891d6bceSjob */ 329891d6bceSjob 330891d6bceSjob static void 331891d6bceSjob constraints_parse_talid(int talid) 332891d6bceSjob { 333891d6bceSjob IPAddrBlocks *allow_addrs, *deny_addrs; 334891d6bceSjob ASIdentifiers *allow_asids, *deny_asids; 335891d6bceSjob FILE *f; 336891d6bceSjob char *fn, *p, *pp; 337*381ee599Stb struct cert_as *allow_ases = NULL, *deny_ases = NULL; 338891d6bceSjob struct cert_ip *allow_ips = NULL, *deny_ips = NULL; 339*381ee599Stb size_t num_allow_ases = 0, num_allow_ips = 0, 340*381ee599Stb num_deny_as = 0, num_deny_ips = 0; 341891d6bceSjob char *line = NULL; 342891d6bceSjob size_t len = 0; 343891d6bceSjob ssize_t n; 344891d6bceSjob int fd, have_allow_as = 0, have_allow_ips = 0, 345891d6bceSjob have_deny_as = 0, have_deny_ips = 0; 346891d6bceSjob 347891d6bceSjob fd = tal_constraints[talid].fd; 348891d6bceSjob fn = tal_constraints[talid].fn; 349891d6bceSjob tal_constraints[talid].fd = -1; 350891d6bceSjob tal_constraints[talid].fn = NULL; 351891d6bceSjob 352891d6bceSjob if (fd == -1) { 353891d6bceSjob free(fn); 354891d6bceSjob return; 355891d6bceSjob } 356891d6bceSjob 357891d6bceSjob if ((f = fdopen(fd, "r")) == NULL) 358891d6bceSjob err(1, "fdopen"); 359891d6bceSjob 360891d6bceSjob if ((allow_addrs = IPAddrBlocks_new()) == NULL) 361891d6bceSjob err(1, NULL); 362891d6bceSjob if ((allow_asids = ASIdentifiers_new()) == NULL) 363891d6bceSjob err(1, NULL); 364891d6bceSjob if ((deny_addrs = IPAddrBlocks_new()) == NULL) 365891d6bceSjob err(1, NULL); 366891d6bceSjob if ((deny_asids = ASIdentifiers_new()) == NULL) 367891d6bceSjob err(1, NULL); 368891d6bceSjob 369891d6bceSjob while ((n = getline(&line, &len, f)) != -1) { 370891d6bceSjob if (line[n - 1] == '\n') 371891d6bceSjob line[n - 1] = '\0'; 372891d6bceSjob 373891d6bceSjob p = line; 374891d6bceSjob 375891d6bceSjob /* Zap leading whitespace */ 376891d6bceSjob while (isspace((unsigned char)*p)) 377891d6bceSjob p++; 378891d6bceSjob 379891d6bceSjob /* Zap comments */ 380891d6bceSjob if ((pp = strchr(p, '#')) != NULL) 381891d6bceSjob *pp = '\0'; 382891d6bceSjob 383891d6bceSjob /* Zap trailing whitespace */ 384891d6bceSjob if (pp == NULL) 385891d6bceSjob pp = p + strlen(p); 386891d6bceSjob while (pp > p && isspace((unsigned char)pp[-1])) 387891d6bceSjob pp--; 388891d6bceSjob *pp = '\0'; 389891d6bceSjob 390891d6bceSjob if (strlen(p) == 0) 391891d6bceSjob continue; 392891d6bceSjob 393891d6bceSjob if (strncmp(p, "allow", strlen("allow")) == 0) { 394891d6bceSjob p += strlen("allow"); 395891d6bceSjob 396891d6bceSjob /* Ensure there's whitespace and jump over it. */ 397891d6bceSjob if (!isspace((unsigned char)*p)) 398891d6bceSjob errx(1, "%s: failed to parse %s", fn, p); 399891d6bceSjob while (isspace((unsigned char)*p)) 400891d6bceSjob p++; 401891d6bceSjob 402891d6bceSjob if (strchr(p, '.') != NULL) { 403891d6bceSjob constraints_parse_ip(fn, p, AFI_IPV4, 404891d6bceSjob allow_addrs); 405891d6bceSjob have_allow_ips = 1; 406891d6bceSjob } else if (strchr(p, ':') != NULL) { 407891d6bceSjob constraints_parse_ip(fn, p, AFI_IPV6, 408891d6bceSjob allow_addrs); 409891d6bceSjob have_allow_ips = 1; 410891d6bceSjob } else { 411891d6bceSjob constraints_parse_as(fn, p, allow_asids); 412891d6bceSjob have_allow_as = 1; 413891d6bceSjob } 414891d6bceSjob } else if (strncmp(p, "deny", strlen("deny")) == 0) { 415891d6bceSjob p += strlen("deny"); 416891d6bceSjob 417891d6bceSjob /* Ensure there's whitespace and jump over it. */ 418891d6bceSjob if (!isspace((unsigned char)*p)) 419891d6bceSjob errx(1, "%s: failed to parse %s", fn, p); 420891d6bceSjob /* Zap leading whitespace */ 421891d6bceSjob while (isspace((unsigned char)*p)) 422891d6bceSjob p++; 423891d6bceSjob 424891d6bceSjob if (strchr(p, '.') != NULL) { 425891d6bceSjob constraints_parse_ip(fn, p, AFI_IPV4, 426891d6bceSjob deny_addrs); 427891d6bceSjob have_deny_ips = 1; 428891d6bceSjob } else if (strchr(p, ':') != NULL) { 429891d6bceSjob constraints_parse_ip(fn, p, AFI_IPV6, 430891d6bceSjob deny_addrs); 431891d6bceSjob have_deny_ips = 1; 432891d6bceSjob } else { 433891d6bceSjob constraints_parse_as(fn, p, deny_asids); 434891d6bceSjob have_deny_as = 1; 435891d6bceSjob } 436891d6bceSjob } else 437891d6bceSjob errx(1, "%s: failed to parse %s", fn, p); 438891d6bceSjob } 439891d6bceSjob free(line); 440891d6bceSjob 441891d6bceSjob if (ferror(f)) 442891d6bceSjob err(1, "%s", fn); 443891d6bceSjob fclose(f); 444891d6bceSjob 445891d6bceSjob if (!X509v3_addr_canonize(allow_addrs)) 446891d6bceSjob errx(1, "%s: failed to canonize IP addresses allowlist", fn); 447891d6bceSjob if (!X509v3_asid_canonize(allow_asids)) 448891d6bceSjob errx(1, "%s: failed to canonize AS numbers allowlist", fn); 449891d6bceSjob if (!X509v3_addr_canonize(deny_addrs)) 450891d6bceSjob errx(1, "%s: failed to canonize IP addresses denylist", fn); 451891d6bceSjob if (!X509v3_asid_canonize(deny_asids)) 452891d6bceSjob errx(1, "%s: failed to canonize AS numbers denylist", fn); 453891d6bceSjob 454891d6bceSjob if (have_allow_as) { 455*381ee599Stb if (!sbgp_parse_assysnum(fn, allow_asids, &allow_ases, 456*381ee599Stb &num_allow_ases)) 457891d6bceSjob errx(1, "%s: failed to parse AS identifiers allowlist", 458891d6bceSjob fn); 459891d6bceSjob } 460891d6bceSjob if (have_deny_as) { 461*381ee599Stb if (!sbgp_parse_assysnum(fn, deny_asids, &deny_ases, 462*381ee599Stb &num_deny_as)) 463891d6bceSjob errx(1, "%s: failed to parse AS identifiers denylist", 464891d6bceSjob fn); 465891d6bceSjob } 466891d6bceSjob if (have_allow_ips) { 467891d6bceSjob constraints_normalize_ip_addrblocks(fn, &allow_addrs); 468891d6bceSjob 469891d6bceSjob if (!sbgp_parse_ipaddrblk(fn, allow_addrs, &allow_ips, 470*381ee599Stb &num_allow_ips)) 471891d6bceSjob errx(1, "%s: failed to parse IP addresses allowlist", 472891d6bceSjob fn); 473891d6bceSjob } 474891d6bceSjob if (have_deny_ips) { 475891d6bceSjob constraints_normalize_ip_addrblocks(fn, &deny_addrs); 476891d6bceSjob 477891d6bceSjob if (!sbgp_parse_ipaddrblk(fn, deny_addrs, &deny_ips, 478*381ee599Stb &num_deny_ips)) 479891d6bceSjob errx(1, "%s: failed to parse IP addresses denylist", 480891d6bceSjob fn); 481891d6bceSjob } 482891d6bceSjob 483*381ee599Stb tal_constraints[talid].allow_ases = allow_ases; 484*381ee599Stb tal_constraints[talid].num_allow_ases = num_allow_ases; 485891d6bceSjob tal_constraints[talid].allow_ips = allow_ips; 486*381ee599Stb tal_constraints[talid].num_allow_ips = num_allow_ips; 487*381ee599Stb tal_constraints[talid].deny_ases = deny_ases; 488*381ee599Stb tal_constraints[talid].num_deny_ases = num_deny_as; 489891d6bceSjob tal_constraints[talid].deny_ips = deny_ips; 490*381ee599Stb tal_constraints[talid].num_deny_ips = num_deny_ips; 491891d6bceSjob 492891d6bceSjob IPAddrBlocks_free(allow_addrs); 493891d6bceSjob IPAddrBlocks_free(deny_addrs); 494891d6bceSjob ASIdentifiers_free(allow_asids); 495891d6bceSjob ASIdentifiers_free(deny_asids); 496891d6bceSjob 497891d6bceSjob free(fn); 498891d6bceSjob } 499891d6bceSjob 500891d6bceSjob /* 501891d6bceSjob * Iterate over all TALs and parse the constraints files loaded previously. 502891d6bceSjob */ 503891d6bceSjob void 504891d6bceSjob constraints_parse(void) 505891d6bceSjob { 506891d6bceSjob int talid; 507891d6bceSjob 508891d6bceSjob for (talid = 0; talid < talsz; talid++) 509891d6bceSjob constraints_parse_talid(talid); 510891d6bceSjob } 511891d6bceSjob 512891d6bceSjob static int 513891d6bceSjob constraints_check_as(const char *fn, struct cert_as *cert, 514*381ee599Stb const struct cert_as *allow_ases, size_t num_allow_ases, 515*381ee599Stb const struct cert_as *deny_ases, size_t num_deny_ases) 516891d6bceSjob { 517891d6bceSjob uint32_t min, max; 518891d6bceSjob 519891d6bceSjob /* Inheriting EE resources are not to be constrained. */ 520891d6bceSjob if (cert->type == CERT_AS_INHERIT) 521891d6bceSjob return 1; 522891d6bceSjob 523891d6bceSjob if (cert->type == CERT_AS_ID) { 524891d6bceSjob min = cert->id; 525891d6bceSjob max = cert->id; 526891d6bceSjob } else { 527891d6bceSjob min = cert->range.min; 528891d6bceSjob max = cert->range.max; 529891d6bceSjob } 530891d6bceSjob 531*381ee599Stb if (deny_ases != NULL) { 532*381ee599Stb if (!as_check_overlap(cert, fn, deny_ases, num_deny_ases, 1)) 533891d6bceSjob return 0; 534891d6bceSjob } 535*381ee599Stb if (allow_ases != NULL) { 536*381ee599Stb if (as_check_covered(min, max, allow_ases, num_allow_ases) <= 0) 537891d6bceSjob return 0; 538891d6bceSjob } 539891d6bceSjob return 1; 540891d6bceSjob } 541891d6bceSjob 542891d6bceSjob static int 543891d6bceSjob constraints_check_ips(const char *fn, struct cert_ip *cert, 544*381ee599Stb const struct cert_ip *allow_ips, size_t num_allow_ips, 545*381ee599Stb const struct cert_ip *deny_ips, size_t num_deny_ips) 546891d6bceSjob { 547891d6bceSjob /* Inheriting EE resources are not to be constrained. */ 548891d6bceSjob if (cert->type == CERT_IP_INHERIT) 549891d6bceSjob return 1; 550891d6bceSjob 551891d6bceSjob if (deny_ips != NULL) { 552*381ee599Stb if (!ip_addr_check_overlap(cert, fn, deny_ips, num_deny_ips, 1)) 553891d6bceSjob return 0; 554891d6bceSjob } 555891d6bceSjob if (allow_ips != NULL) { 556891d6bceSjob if (ip_addr_check_covered(cert->afi, cert->min, cert->max, 557*381ee599Stb allow_ips, num_allow_ips) <= 0) 558891d6bceSjob return 0; 559891d6bceSjob } 560891d6bceSjob return 1; 561891d6bceSjob } 562891d6bceSjob 563891d6bceSjob /* 564891d6bceSjob * Check whether an EE cert's resources are covered by its TAL's constraints. 565891d6bceSjob * We accept certs with a negative talid as "unknown TAL" for filemode. The 566891d6bceSjob * logic nearly duplicates valid_cert(). 567891d6bceSjob */ 568891d6bceSjob int 569891d6bceSjob constraints_validate(const char *fn, const struct cert *cert) 570891d6bceSjob { 571891d6bceSjob int talid = cert->talid; 572*381ee599Stb struct cert_as *allow_ases, *deny_ases; 573891d6bceSjob struct cert_ip *allow_ips, *deny_ips; 574*381ee599Stb size_t num_allow_ases, num_allow_ips; 575*381ee599Stb size_t num_deny_ases, num_deny_ips; 576*381ee599Stb size_t i; 577891d6bceSjob 578891d6bceSjob /* Accept negative talid to bypass validation. */ 579891d6bceSjob if (talid < 0) 580891d6bceSjob return 1; 581891d6bceSjob if (talid >= talsz) 582891d6bceSjob errx(1, "%s: talid out of range %d", fn, talid); 583891d6bceSjob 584*381ee599Stb allow_ases = tal_constraints[talid].allow_ases; 585*381ee599Stb num_allow_ases = tal_constraints[talid].num_allow_ases; 586*381ee599Stb deny_ases = tal_constraints[talid].deny_ases; 587*381ee599Stb num_deny_ases = tal_constraints[talid].num_deny_ases; 588891d6bceSjob 589*381ee599Stb for (i = 0; i < cert->num_ases; i++) { 590*381ee599Stb if (constraints_check_as(fn, &cert->ases[i], 591*381ee599Stb allow_ases, num_allow_ases, deny_ases, num_deny_ases)) 592891d6bceSjob continue; 593891d6bceSjob 594*381ee599Stb as_warn(fn, tal_constraints[talid].warn, &cert->ases[i]); 595891d6bceSjob return 0; 596891d6bceSjob } 597891d6bceSjob 598891d6bceSjob allow_ips = tal_constraints[talid].allow_ips; 599*381ee599Stb num_allow_ips = tal_constraints[talid].num_allow_ips; 600891d6bceSjob deny_ips = tal_constraints[talid].deny_ips; 601*381ee599Stb num_deny_ips = tal_constraints[talid].num_deny_ips; 602891d6bceSjob 603*381ee599Stb for (i = 0; i < cert->num_ips; i++) { 604891d6bceSjob if (constraints_check_ips(fn, &cert->ips[i], allow_ips, 605*381ee599Stb num_allow_ips, deny_ips, num_deny_ips)) 606891d6bceSjob continue; 607891d6bceSjob 60889e02b0fSjob ip_warn(fn, tal_constraints[talid].warn, &cert->ips[i]); 609891d6bceSjob return 0; 610891d6bceSjob } 611891d6bceSjob 612891d6bceSjob return 1; 613891d6bceSjob } 614