1 /* $OpenBSD: constraints.c,v 1.18 2023/12/13 05:59:50 tb Exp $ */ 2 /* 3 * Copyright (c) 2020 Bob Beck <beck@openbsd.org> 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 <err.h> 19 #include <string.h> 20 21 #include <openssl/safestack.h> 22 #include <openssl/x509.h> 23 #include <openssl/x509v3.h> 24 #include "x509_internal.h" 25 26 #define FAIL(msg, ...) \ 27 do { \ 28 fprintf(stderr, "[%s:%d] FAIL: ", __FILE__, __LINE__); \ 29 fprintf(stderr, msg, ##__VA_ARGS__); \ 30 } while(0) 31 32 unsigned char *valid_hostnames[] = { 33 "openbsd.org", 34 "op3nbsd.org", 35 "org", 36 "3openbsd.com", 37 "3-0penb-d.c-m", 38 "a", 39 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 40 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 41 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 42 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 43 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 44 "open_bsd.org", /* because this is liberal */ 45 NULL, 46 }; 47 48 unsigned char *valid_sandns_names[] = { 49 "*.ca", 50 "*.op3nbsd.org", 51 "c*.openbsd.org", 52 "foo.*.d*.c*.openbsd.org", 53 NULL, 54 }; 55 56 unsigned char *valid_domain_constraints[] = { 57 "", 58 ".ca", 59 ".op3nbsd.org", 60 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 61 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 62 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 63 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 64 "www.openbsd.org", 65 NULL, 66 }; 67 68 unsigned char *valid_mbox_names[] = { 69 "\"!#$%&\\\"*+-/=?\002^_`{|}~.\"@openbsd.org", 70 "beck@openbsd.org", 71 "beck@openbsd.org", 72 "beck@op3nbsd.org", 73 "beck@org", 74 "beck@3openbsd.com", 75 "beck@3-0penb-d.c-m", 76 "bec@a", 77 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 78 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 79 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 80 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 81 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 82 "beck@open_bsd.org", /* because this is liberal */ 83 NULL, 84 }; 85 86 unsigned char *invalid_hostnames[] = { 87 "openbsd.org.", 88 "openbsd..org", 89 "openbsd.org-", 90 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 91 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 92 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 93 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 94 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 95 "-p3nbsd.org", 96 "openbs-.org", 97 "openbsd\n.org", 98 "open\177bsd.org", 99 "open\255bsd.org", 100 "*.openbsd.org", 101 NULL, 102 }; 103 104 unsigned char *invalid_sandns_names[] = { 105 "", 106 ".", 107 "*.a", 108 "*.", 109 "*.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 110 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 111 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 112 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 113 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 114 "*.-p3nbsd.org", 115 "*.*..openbsd.org", 116 "*..openbsd.org", 117 ".openbsd.org", 118 "c*c.openbsd.org", 119 NULL, 120 }; 121 122 unsigned char *invalid_mbox_names[] = { 123 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 124 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 125 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 126 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 127 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 128 "beck@.-openbsd.org", 129 "beck@.openbsd.org.", 130 "beck@.a", 131 "beck@.", 132 "beck@", 133 "beck@.ca", 134 "@openbsd.org", 135 NULL, 136 }; 137 138 unsigned char *invalid_domain_constraints[] = { 139 ".", 140 ".a", 141 "..", 142 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 143 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 144 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 145 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 146 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 147 ".-p3nbsd.org", 148 "..openbsd.org", 149 NULL, 150 }; 151 152 unsigned char *invaliduri[] = { 153 "https://-www.openbsd.org", 154 "https://.www.openbsd.org/", 155 "https://www.ope|nbsd.org%", 156 "https://www.openbsd.org.#", 157 "https://192.168.1.1./", 158 "https://192.168.1.1|/", 159 "https://.192.168.1.1/", 160 "https://192.168..1.1/", 161 "https://.2001:0DB8:AC10:FE01::/", 162 "https://.2001:0DB8:AC10:FE01::|/", 163 "///", 164 "//", 165 "/", 166 "", 167 NULL, 168 }; 169 170 unsigned char *validuri[] = { 171 "https://www.openbsd.org/meep/meep/meep/", 172 "https://192.168.1.1/", 173 "https://2001:0DB8:AC10:FE01::/", 174 "https://192.168.1/", /* Not an IP, but valid component */ 175 "https://999.999.999.999/", /* Not an IP, but valid component */ 176 NULL, 177 }; 178 179 static int 180 test_valid_hostnames(void) 181 { 182 int i, failure = 0; 183 184 for (i = 0; valid_hostnames[i] != NULL; i++) { 185 CBS cbs; 186 CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i])); 187 if (!x509_constraints_valid_host(&cbs, 0)) { 188 FAIL("Valid hostname '%s' rejected\n", 189 valid_hostnames[i]); 190 failure = 1; 191 goto done; 192 } 193 CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i])); 194 if (!x509_constraints_valid_sandns(&cbs)) { 195 FAIL("Valid sandns '%s' rejected\n", 196 valid_hostnames[i]); 197 failure = 1; 198 goto done; 199 } 200 } 201 202 done: 203 return failure; 204 } 205 206 static int 207 test_valid_sandns_names(void) 208 { 209 int i, failure = 0; 210 for (i = 0; valid_sandns_names[i] != NULL; i++) { 211 CBS cbs; 212 CBS_init(&cbs, valid_sandns_names[i], 213 strlen(valid_sandns_names[i])); 214 if (!x509_constraints_valid_sandns(&cbs)) { 215 FAIL("Valid dnsname '%s' rejected\n", 216 valid_sandns_names[i]); 217 failure = 1; 218 goto done; 219 } 220 } 221 222 done: 223 return failure; 224 } 225 226 static int 227 test_valid_domain_constraints(void) 228 { 229 int i, failure = 0; 230 for (i = 0; valid_domain_constraints[i] != NULL; i++) { 231 CBS cbs; 232 CBS_init(&cbs, valid_domain_constraints[i], 233 strlen(valid_domain_constraints[i])); 234 if (!x509_constraints_valid_domain_constraint(&cbs)) { 235 FAIL("Valid dnsname '%s' rejected\n", 236 valid_domain_constraints[i]); 237 failure = 1; 238 goto done; 239 } 240 } 241 242 done: 243 return failure; 244 } 245 246 static int 247 test_valid_mbox_names(void) 248 { 249 struct x509_constraints_name name = {0}; 250 int i, failure = 0; 251 for (i = 0; valid_mbox_names[i] != NULL; i++) { 252 CBS cbs; 253 CBS_init(&cbs, valid_mbox_names[i], 254 strlen(valid_mbox_names[i])); 255 if (!x509_constraints_parse_mailbox(&cbs, &name)) { 256 FAIL("Valid mailbox name '%s' rejected\n", 257 valid_mbox_names[i]); 258 failure = 1; 259 goto done; 260 } 261 free(name.name); 262 name.name = NULL; 263 free(name.local); 264 name.local = NULL; 265 } 266 267 done: 268 return failure; 269 } 270 271 static int 272 test_invalid_hostnames(void) 273 { 274 int i, failure = 0; 275 char *nulhost = "www.openbsd.org\0"; 276 CBS cbs; 277 278 for (i = 0; invalid_hostnames[i] != NULL; i++) { 279 CBS_init(&cbs, invalid_hostnames[i], 280 strlen(invalid_hostnames[i])); 281 if (x509_constraints_valid_host(&cbs, 0)) { 282 FAIL("Invalid hostname '%s' accepted\n", 283 invalid_hostnames[i]); 284 failure = 1; 285 goto done; 286 } 287 } 288 CBS_init(&cbs, nulhost, strlen(nulhost) + 1); 289 if (x509_constraints_valid_host(&cbs, 0)) { 290 FAIL("hostname with NUL byte accepted\n"); 291 failure = 1; 292 goto done; 293 } 294 CBS_init(&cbs, nulhost, strlen(nulhost) + 1); 295 if (x509_constraints_valid_sandns(&cbs)) { 296 FAIL("sandns with NUL byte accepted\n"); 297 failure = 1; 298 goto done; 299 } 300 301 done: 302 return failure; 303 } 304 305 static int 306 test_invalid_sandns_names(void) 307 { 308 int i, failure = 0; 309 for (i = 0; invalid_sandns_names[i] != NULL; i++) { 310 CBS cbs; 311 CBS_init(&cbs, invalid_sandns_names[i], 312 strlen(invalid_sandns_names[i])); 313 if (x509_constraints_valid_sandns(&cbs)) { 314 FAIL("Valid dnsname '%s' rejected\n", 315 invalid_sandns_names[i]); 316 failure = 1; 317 goto done; 318 } 319 } 320 321 done: 322 return failure; 323 } 324 325 static int 326 test_invalid_mbox_names(void) 327 { 328 int i, failure = 0; 329 struct x509_constraints_name name = {0}; 330 for (i = 0; invalid_mbox_names[i] != NULL; i++) { 331 CBS cbs; 332 CBS_init(&cbs, invalid_mbox_names[i], 333 strlen(invalid_mbox_names[i])); 334 if (x509_constraints_parse_mailbox(&cbs, &name)) { 335 FAIL("invalid mailbox name '%s' accepted\n", 336 invalid_mbox_names[i]); 337 failure = 1; 338 goto done; 339 } 340 free(name.name); 341 name.name = NULL; 342 free(name.local); 343 name.local = NULL; 344 } 345 346 done: 347 return failure; 348 } 349 350 static int 351 test_invalid_domain_constraints(void) 352 { 353 int i, failure = 0; 354 for (i = 0; invalid_domain_constraints[i] != NULL; i++) { 355 CBS cbs; 356 CBS_init(&cbs, invalid_domain_constraints[i], 357 strlen(invalid_domain_constraints[i])); 358 if (x509_constraints_valid_domain_constraint(&cbs)) { 359 FAIL("invalid dnsname '%s' accepted\n", 360 invalid_domain_constraints[i]); 361 failure = 1; 362 goto done; 363 } 364 } 365 366 done: 367 return failure; 368 } 369 370 static int 371 test_invalid_uri(void) 372 { 373 int j, failure = 0; 374 char *hostpart = NULL; 375 376 for (j = 0; invaliduri[j] != NULL; j++) { 377 if (x509_constraints_uri_host(invaliduri[j], 378 strlen(invaliduri[j]), &hostpart) != 0) { 379 FAIL("invalid URI '%s' accepted\n", 380 invaliduri[j]); 381 failure = 1; 382 goto done; 383 } 384 free(hostpart); 385 hostpart = NULL; 386 } 387 388 done: 389 return failure; 390 } 391 392 static int 393 test_valid_uri(void) 394 { 395 int j, failure = 0; 396 char *hostpart = NULL; 397 398 for (j = 0; validuri[j] != NULL; j++) { 399 if (x509_constraints_uri_host(validuri[j], 400 strlen(invaliduri[j]), &hostpart) == 0) { 401 FAIL("Valid URI '%s' NOT accepted\n", 402 validuri[j]); 403 failure = 1; 404 goto done; 405 } 406 free(hostpart); 407 hostpart = NULL; 408 } 409 410 done: 411 return failure; 412 } 413 414 static int 415 test_constraints1(void) 416 { 417 char *c; 418 size_t cl; 419 char *d; 420 size_t dl; 421 int failure = 0; 422 int error = 0; 423 int i, j; 424 unsigned char *constraints[] = { 425 ".org", 426 ".openbsd.org", 427 "www.openbsd.org", 428 NULL, 429 }; 430 unsigned char *failing[] = { 431 ".ca", 432 "openbsd.ca", 433 "org", 434 NULL, 435 }; 436 unsigned char *matching[] = { 437 "www.openbsd.org", 438 NULL, 439 }; 440 unsigned char *matchinguri[] = { 441 "https://www.openbsd.org", 442 "https://www.openbsd.org/", 443 "https://www.openbsd.org?", 444 "https://www.openbsd.org#", 445 "herp://beck@www.openbsd.org:", 446 "spiffe://beck@www.openbsd.org/this/is/so/spiffe/", 447 NULL, 448 }; 449 unsigned char *failinguri[] = { 450 "https://www.openbsd.ca", 451 "https://www.freebsd.com/", 452 "https://www.openbsd.net?", 453 "https://org#", 454 "herp://beck@org:", 455 "///", 456 "//", 457 "/", 458 "", 459 NULL, 460 }; 461 unsigned char *noauthority[] = { 462 "urn:open62541.server.application", 463 NULL, 464 }; 465 for (i = 0; constraints[i] != NULL; i++) { 466 char *constraint = constraints[i]; 467 size_t clen = strlen(constraints[i]); 468 for (j = 0; matching[j] != NULL; j++) { 469 if (!x509_constraints_domain(matching[j], 470 strlen(matching[j]), constraint, clen)) { 471 FAIL("constraint '%s' should have matched" 472 " '%s'\n", 473 constraint, matching[j]); 474 failure = 1; 475 goto done; 476 } 477 } 478 for (j = 0; matchinguri[j] != NULL; j++) { 479 error = 0; 480 if (!x509_constraints_uri(matchinguri[j], 481 strlen(matchinguri[j]), constraint, clen, &error)) { 482 FAIL("constraint '%s' should have matched URI" 483 " '%s' (error %d)\n", 484 constraint, matchinguri[j], error); 485 failure = 1; 486 goto done; 487 } 488 } 489 for (j = 0; failing[j] != NULL; j++) { 490 if (x509_constraints_domain(failing[j], 491 strlen(failing[j]), constraint, clen)) { 492 FAIL("constraint '%s' should not have matched" 493 " '%s'\n", 494 constraint, failing[j]); 495 failure = 1; 496 goto done; 497 } 498 } 499 for (j = 0; failinguri[j] != NULL; j++) { 500 error = 0; 501 if (x509_constraints_uri(failinguri[j], 502 strlen(failinguri[j]), constraint, clen, &error)) { 503 FAIL("constraint '%s' should not have matched URI" 504 " '%s' (error %d)\n", 505 constraint, failinguri[j], error); 506 failure = 1; 507 goto done; 508 } 509 } 510 for (j = 0; noauthority[j] != NULL; j++) { 511 char *hostpart = NULL; 512 error = 0; 513 if (!x509_constraints_uri_host(noauthority[j], 514 strlen(noauthority[j]), NULL) || 515 !x509_constraints_uri_host(noauthority[j], 516 strlen(noauthority[j]), &hostpart)) { 517 FAIL("name '%s' should parse as a URI", 518 noauthority[j]); 519 failure = 1; 520 free(hostpart); 521 goto done; 522 } 523 free(hostpart); 524 525 if (x509_constraints_uri(noauthority[j], 526 strlen(noauthority[j]), constraint, clen, &error)) { 527 FAIL("constraint '%s' should not have matched URI" 528 " '%s' (error %d)\n", 529 constraint, failinguri[j], error); 530 failure = 1; 531 goto done; 532 } 533 } 534 } 535 c = ".openbsd.org"; 536 cl = strlen(".openbsd.org"); 537 d = "*.openbsd.org"; 538 dl = strlen("*.openbsd.org"); 539 if (!x509_constraints_domain(d, dl, c, cl)) { 540 FAIL("constraint '%s' should have matched '%s'\n", 541 c, d); 542 failure = 1; 543 goto done; 544 } 545 c = "www.openbsd.org"; 546 cl = strlen("www.openbsd.org"); 547 if (x509_constraints_domain(d, dl, c, cl)) { 548 FAIL("constraint '%s' should not have matched '%s'\n", 549 c, d); 550 failure = 1; 551 goto done; 552 } 553 c = ""; 554 cl = 0; 555 if (!x509_constraints_domain(d, dl, c, cl)) { 556 FAIL("constraint '%s' should have matched '%s'\n", 557 c, d); 558 failure = 1; 559 goto done; 560 } 561 562 done: 563 return failure; 564 } 565 566 int 567 main(int argc, char **argv) 568 { 569 int failed = 0; 570 571 failed |= test_valid_hostnames(); 572 failed |= test_invalid_hostnames(); 573 failed |= test_valid_sandns_names(); 574 failed |= test_invalid_sandns_names(); 575 failed |= test_valid_mbox_names(); 576 failed |= test_invalid_mbox_names(); 577 failed |= test_valid_domain_constraints(); 578 failed |= test_invalid_domain_constraints(); 579 failed |= test_invalid_uri(); 580 failed |= test_valid_uri(); 581 failed |= test_constraints1(); 582 583 return (failed); 584 } 585