validate.c revision 1.42
1/* $OpenBSD: validate.c,v 1.42 2022/08/30 18:56:49 job Exp $ */ 2/* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/socket.h> 19 20#include <arpa/inet.h> 21#include <assert.h> 22#include <ctype.h> 23#include <err.h> 24#include <fcntl.h> 25#include <inttypes.h> 26#include <stdarg.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30 31#include "extern.h" 32 33extern ASN1_OBJECT *certpol_oid; 34 35/* 36 * Walk up the chain of certificates trying to match our AS number to 37 * one of the allocations in that chain. 38 * Returns 1 if covered or 0 if not. 39 */ 40static int 41valid_as(struct auth *a, uint32_t min, uint32_t max) 42{ 43 int c; 44 45 if (a == NULL) 46 return 0; 47 48 /* Does this certificate cover our AS number? */ 49 c = as_check_covered(min, max, a->cert->as, a->cert->asz); 50 if (c > 0) 51 return 1; 52 else if (c < 0) 53 return 0; 54 55 /* If it inherits, walk up the chain. */ 56 return valid_as(a->parent, min, max); 57} 58 59/* 60 * Walk up the chain of certificates (really just the last one, but in 61 * the case of inheritance, the ones before) making sure that our IP 62 * prefix is covered in the first non-inheriting specification. 63 * Returns 1 if covered or 0 if not. 64 */ 65static int 66valid_ip(struct auth *a, enum afi afi, 67 const unsigned char *min, const unsigned char *max) 68{ 69 int c; 70 71 if (a == NULL) 72 return 0; 73 74 /* Does this certificate cover our IP prefix? */ 75 c = ip_addr_check_covered(afi, min, max, a->cert->ips, a->cert->ipsz); 76 if (c > 0) 77 return 1; 78 else if (c < 0) 79 return 0; 80 81 /* If it inherits, walk up the chain. */ 82 return valid_ip(a->parent, afi, min, max); 83} 84 85/* 86 * Make sure that the SKI doesn't already exist and return the parent by 87 * its AKI. 88 * Returns the parent auth or NULL on failure. 89 */ 90struct auth * 91valid_ski_aki(const char *fn, struct auth_tree *auths, 92 const char *ski, const char *aki) 93{ 94 struct auth *a; 95 96 if (auth_find(auths, ski) != NULL) { 97 warnx("%s: RFC 6487: duplicate SKI", fn); 98 return NULL; 99 } 100 101 a = auth_find(auths, aki); 102 if (a == NULL) 103 warnx("%s: RFC 6487: unknown AKI", fn); 104 105 return a; 106} 107 108/* 109 * Authenticate a trust anchor by making sure its resources are not 110 * inheriting and that the SKI is unique. 111 * Returns 1 if valid, 0 otherwise. 112 */ 113int 114valid_ta(const char *fn, struct auth_tree *auths, const struct cert *cert) 115{ 116 size_t i; 117 118 /* AS and IP resources must not inherit. */ 119 if (cert->asz && cert->as[0].type == CERT_AS_INHERIT) { 120 warnx("%s: RFC 6487 (trust anchor): " 121 "inheriting AS resources", fn); 122 return 0; 123 } 124 for (i = 0; i < cert->ipsz; i++) 125 if (cert->ips[i].type == CERT_IP_INHERIT) { 126 warnx("%s: RFC 6487 (trust anchor): " 127 "inheriting IP resources", fn); 128 return 0; 129 } 130 131 /* SKI must not be a dupe. */ 132 if (auth_find(auths, cert->ski) != NULL) { 133 warnx("%s: RFC 6487: duplicate SKI", fn); 134 return 0; 135 } 136 137 return 1; 138} 139 140/* 141 * Validate a non-TA certificate: make sure its IP and AS resources are 142 * fully covered by those in the authority key (which must exist). 143 * Returns 1 if valid, 0 otherwise. 144 */ 145int 146valid_cert(const char *fn, struct auth *a, const struct cert *cert) 147{ 148 size_t i; 149 uint32_t min, max; 150 char buf1[64], buf2[64]; 151 152 for (i = 0; i < cert->asz; i++) { 153 if (cert->as[i].type == CERT_AS_INHERIT) { 154 if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER) 155 return 0; /* BGPsec doesn't permit inheriting */ 156 continue; 157 } 158 min = cert->as[i].type == CERT_AS_ID ? 159 cert->as[i].id : cert->as[i].range.min; 160 max = cert->as[i].type == CERT_AS_ID ? 161 cert->as[i].id : cert->as[i].range.max; 162 if (valid_as(a, min, max)) 163 continue; 164 warnx("%s: RFC 6487: uncovered AS: " 165 "%u--%u", fn, min, max); 166 return 0; 167 } 168 169 for (i = 0; i < cert->ipsz; i++) { 170 if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min, 171 cert->ips[i].max)) 172 continue; 173 switch (cert->ips[i].type) { 174 case CERT_IP_RANGE: 175 ip_addr_print(&cert->ips[i].range.min, 176 cert->ips[i].afi, buf1, sizeof(buf1)); 177 ip_addr_print(&cert->ips[i].range.max, 178 cert->ips[i].afi, buf2, sizeof(buf2)); 179 warnx("%s: RFC 6487: uncovered IP: " 180 "%s--%s", fn, buf1, buf2); 181 break; 182 case CERT_IP_ADDR: 183 ip_addr_print(&cert->ips[i].ip, 184 cert->ips[i].afi, buf1, sizeof(buf1)); 185 warnx("%s: RFC 6487: uncovered IP: " 186 "%s", fn, buf1); 187 break; 188 case CERT_IP_INHERIT: 189 warnx("%s: RFC 6487: uncovered IP: " 190 "(inherit)", fn); 191 break; 192 } 193 return 0; 194 } 195 196 return 1; 197} 198 199/* 200 * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained. 201 * Returns 1 if valid, 0 otherwise. 202 */ 203int 204valid_roa(const char *fn, struct cert *cert, struct roa *roa) 205{ 206 size_t i; 207 char buf[64]; 208 209 for (i = 0; i < roa->ipsz; i++) { 210 if (ip_addr_check_covered(roa->ips[i].afi, roa->ips[i].min, 211 roa->ips[i].max, cert->ips, cert->ipsz) > 0) 212 continue; 213 214 ip_addr_print(&roa->ips[i].addr, roa->ips[i].afi, buf, 215 sizeof(buf)); 216 warnx("%s: RFC 6482: uncovered IP: %s", fn, buf); 217 return 0; 218 } 219 220 return 1; 221} 222 223/* 224 * Validate a file by verifying the SHA256 hash of that file. 225 * The file to check is passed as a file descriptor. 226 * Returns 1 if hash matched, 0 otherwise. Closes fd when done. 227 */ 228int 229valid_filehash(int fd, const char *hash, size_t hlen) 230{ 231 SHA256_CTX ctx; 232 char filehash[SHA256_DIGEST_LENGTH]; 233 char buffer[8192]; 234 ssize_t nr; 235 236 if (hlen != sizeof(filehash)) 237 errx(1, "bad hash size"); 238 239 if (fd == -1) 240 return 0; 241 242 SHA256_Init(&ctx); 243 while ((nr = read(fd, buffer, sizeof(buffer))) > 0) 244 SHA256_Update(&ctx, buffer, nr); 245 close(fd); 246 SHA256_Final(filehash, &ctx); 247 248 if (memcmp(hash, filehash, sizeof(filehash)) != 0) 249 return 0; 250 return 1; 251} 252 253/* 254 * Same as above but with a buffer instead of a fd. 255 */ 256int 257valid_hash(unsigned char *buf, size_t len, const char *hash, size_t hlen) 258{ 259 char filehash[SHA256_DIGEST_LENGTH]; 260 261 if (hlen != sizeof(filehash)) 262 errx(1, "bad hash size"); 263 264 if (buf == NULL || len == 0) 265 return 0; 266 267 if (!EVP_Digest(buf, len, filehash, NULL, EVP_sha256(), NULL)) 268 errx(1, "EVP_Digest failed"); 269 270 if (memcmp(hash, filehash, sizeof(filehash)) != 0) 271 return 0; 272 return 1; 273} 274 275/* 276 * Validate that a filename only contains characters from the POSIX portable 277 * filename character set [A-Za-z0-9._-], see IEEE Std 1003.1-2013, 3.278. 278 */ 279int 280valid_filename(const char *fn, size_t len) 281{ 282 const unsigned char *c; 283 size_t i; 284 285 for (c = fn, i = 0; i < len; i++, c++) 286 if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.') 287 return 0; 288 return 1; 289} 290 291/* 292 * Validate a URI to make sure it is pure ASCII and does not point backwards 293 * or doing some other silly tricks. To enforce the protocol pass either 294 * https:// or rsync:// as proto, if NULL is passed no protocol is enforced. 295 * Returns 1 if valid, 0 otherwise. 296 */ 297int 298valid_uri(const char *uri, size_t usz, const char *proto) 299{ 300 size_t s; 301 302 if (usz > MAX_URI_LENGTH) 303 return 0; 304 305 for (s = 0; s < usz; s++) 306 if (!isalnum((unsigned char)uri[s]) && 307 !ispunct((unsigned char)uri[s])) 308 return 0; 309 310 if (proto != NULL) { 311 s = strlen(proto); 312 if (strncasecmp(uri, proto, s) != 0) 313 return 0; 314 } 315 316 /* do not allow files or directories to start with a '.' */ 317 if (strstr(uri, "/.") != NULL) 318 return 0; 319 320 return 1; 321} 322 323/* 324 * Validate that a URI has the same host as the URI passed in proto. 325 * Returns 1 if valid, 0 otherwise. 326 */ 327int 328valid_origin(const char *uri, const char *proto) 329{ 330 const char *to; 331 332 /* extract end of host from proto URI */ 333 to = strstr(proto, "://"); 334 if (to == NULL) 335 return 0; 336 to += strlen("://"); 337 if ((to = strchr(to, '/')) == NULL) 338 return 0; 339 340 /* compare hosts including the / for the start of the path section */ 341 if (strncasecmp(uri, proto, to - proto + 1) != 0) 342 return 0; 343 344 return 1; 345} 346 347/* 348 * Walk the certificate tree to the root and build a certificate 349 * chain from cert->x509. All certs in the tree are validated and 350 * can be loaded as trusted stack into the validator. 351 */ 352static void 353build_chain(const struct auth *a, STACK_OF(X509) **chain) 354{ 355 *chain = NULL; 356 357 if (a == NULL) 358 return; 359 360 if ((*chain = sk_X509_new_null()) == NULL) 361 err(1, "sk_X509_new_null"); 362 for (; a != NULL; a = a->parent) { 363 assert(a->cert->x509 != NULL); 364 if (!sk_X509_push(*chain, a->cert->x509)) 365 errx(1, "sk_X509_push"); 366 } 367} 368 369/* 370 * Add the CRL based on the certs SKI value. 371 * No need to insert any other CRL since those were already checked. 372 */ 373static void 374build_crls(const struct crl *crl, STACK_OF(X509_CRL) **crls) 375{ 376 *crls = NULL; 377 378 if (crl == NULL) 379 return; 380 if ((*crls = sk_X509_CRL_new_null()) == NULL) 381 errx(1, "sk_X509_CRL_new_null"); 382 if (!sk_X509_CRL_push(*crls, crl->x509_crl)) 383 err(1, "sk_X509_CRL_push"); 384} 385 386/* 387 * Validate the X509 certificate. If crl is NULL don't check CRL. 388 * Returns 1 for valid certificates, returns 0 if there is a verify error 389 */ 390int 391valid_x509(char *file, X509_STORE_CTX *store_ctx, X509 *x509, struct auth *a, 392 struct crl *crl, int nowarn) 393{ 394 X509_VERIFY_PARAM *params; 395 ASN1_OBJECT *cp_oid; 396 STACK_OF(X509) *chain; 397 STACK_OF(X509_CRL) *crls = NULL; 398 unsigned long flags; 399 int c; 400 401 build_chain(a, &chain); 402 build_crls(crl, &crls); 403 404 assert(store_ctx != NULL); 405 assert(x509 != NULL); 406 if (!X509_STORE_CTX_init(store_ctx, NULL, x509, NULL)) 407 cryptoerrx("X509_STORE_CTX_init"); 408 409 if ((params = X509_STORE_CTX_get0_param(store_ctx)) == NULL) 410 cryptoerrx("X509_STORE_CTX_get0_param"); 411 if ((cp_oid = OBJ_dup(certpol_oid)) == NULL) 412 cryptoerrx("OBJ_dup"); 413 if (!X509_VERIFY_PARAM_add0_policy(params, cp_oid)) 414 cryptoerrx("X509_VERIFY_PARAM_add0_policy"); 415 416 flags = X509_V_FLAG_CRL_CHECK; 417 flags |= X509_V_FLAG_EXPLICIT_POLICY; 418 flags |= X509_V_FLAG_INHIBIT_MAP; 419 X509_STORE_CTX_set_flags(store_ctx, flags); 420 X509_STORE_CTX_set_depth(store_ctx, MAX_CERT_DEPTH); 421 X509_STORE_CTX_set0_trusted_stack(store_ctx, chain); 422 X509_STORE_CTX_set0_crls(store_ctx, crls); 423 424 if (X509_verify_cert(store_ctx) <= 0) { 425 c = X509_STORE_CTX_get_error(store_ctx); 426 if (!nowarn || verbose > 1) 427 warnx("%s: %s", file, X509_verify_cert_error_string(c)); 428 X509_STORE_CTX_cleanup(store_ctx); 429 sk_X509_free(chain); 430 sk_X509_CRL_free(crls); 431 return 0; 432 } 433 434 X509_STORE_CTX_cleanup(store_ctx); 435 sk_X509_free(chain); 436 sk_X509_CRL_free(crls); 437 return 1; 438} 439 440/* 441 * Validate our RSC: check that all items in the ResourceBlock are contained. 442 * Returns 1 if valid, 0 otherwise. 443 */ 444int 445valid_rsc(const char *fn, struct cert *cert, struct rsc *rsc) 446{ 447 size_t i; 448 uint32_t min, max; 449 char buf1[64], buf2[64]; 450 451 for (i = 0; i < rsc->asz; i++) { 452 if (rsc->as[i].type == CERT_AS_INHERIT) { 453 warnx("%s: RSC ResourceBlock: illegal inherit", fn); 454 return 0; 455 } 456 457 min = rsc->as[i].type == CERT_AS_RANGE ? rsc->as[i].range.min 458 : rsc->as[i].id; 459 max = rsc->as[i].type == CERT_AS_RANGE ? rsc->as[i].range.max 460 : rsc->as[i].id; 461 462 if (as_check_covered(min, max, cert->as, cert->asz) > 0) 463 continue; 464 465 switch (rsc->as[i].type) { 466 case CERT_AS_ID: 467 warnx("%s: RSC resourceBlock: uncovered AS Identifier: " 468 "%u", fn, rsc->as[i].id); 469 break; 470 case CERT_AS_RANGE: 471 warnx("%s: RSC resourceBlock: uncovered AS Range: " 472 "%u--%u", fn, min, max); 473 break; 474 default: 475 break; 476 } 477 return 0; 478 } 479 480 for (i = 0; i < rsc->ipsz; i++) { 481 if (rsc->ips[i].type == CERT_IP_INHERIT) { 482 warnx("%s: RSC ResourceBlock: illegal inherit", fn); 483 return 0; 484 } 485 486 if (ip_addr_check_covered(rsc->ips[i].afi, rsc->ips[i].min, 487 rsc->ips[i].max, cert->ips, cert->ipsz) > 0) 488 continue; 489 490 switch (rsc->ips[i].type) { 491 case CERT_IP_RANGE: 492 ip_addr_print(&rsc->ips[i].range.min, 493 rsc->ips[i].afi, buf1, sizeof(buf1)); 494 ip_addr_print(&rsc->ips[i].range.max, 495 rsc->ips[i].afi, buf2, sizeof(buf2)); 496 warnx("%s: RSC ResourceBlock: uncovered IP Range: " 497 "%s--%s", fn, buf1, buf2); 498 break; 499 case CERT_IP_ADDR: 500 ip_addr_print(&rsc->ips[i].ip, 501 rsc->ips[i].afi, buf1, sizeof(buf1)); 502 warnx("%s: RSC ResourceBlock: uncovered IP: " 503 "%s", fn, buf1); 504 break; 505 default: 506 break; 507 } 508 return 0; 509 } 510 511 return 1; 512} 513 514int 515valid_econtent_version(const char *fn, const ASN1_INTEGER *aint) 516{ 517 long version; 518 519 if (aint == NULL) 520 return 1; 521 522 if ((version = ASN1_INTEGER_get(aint)) < 0) { 523 warnx("%s: ASN1_INTEGER_get failed", fn); 524 return 0; 525 } 526 527 switch (version) { 528 case 0: 529 warnx("%s: incorrect encoding for version 0", fn); 530 return 0; 531 default: 532 warnx("%s: version %ld not supported (yet)", fn, version); 533 return 0; 534 } 535} 536 537/* 538 * Validate the ASPA: check that the customerASID is contained. 539 * Returns 1 if valid, 0 otherwise. 540 */ 541int 542valid_aspa(const char *fn, struct cert *cert, struct aspa *aspa) 543{ 544 545 if (as_check_covered(aspa->custasid, aspa->custasid, 546 cert->as, cert->asz) > 0) 547 return 1; 548 549 warnx("%s: ASPA: uncovered Customer ASID: %u", fn, aspa->custasid); 550 551 return 0; 552} 553