1 /* $OpenBSD: constraints.c,v 1.15 2022/11/28 07:24:03 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\178bsd.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 "///", 158 "//", 159 "/", 160 "", 161 NULL, 162 }; 163 164 static int 165 test_valid_hostnames(void) 166 { 167 int i, failure = 0; 168 169 for (i = 0; valid_hostnames[i] != NULL; i++) { 170 CBS cbs; 171 CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i])); 172 if (!x509_constraints_valid_host(&cbs)) { 173 FAIL("Valid hostname '%s' rejected\n", 174 valid_hostnames[i]); 175 failure = 1; 176 goto done; 177 } 178 CBS_init(&cbs, valid_hostnames[i], strlen(valid_hostnames[i])); 179 if (!x509_constraints_valid_sandns(&cbs)) { 180 FAIL("Valid sandns '%s' rejected\n", 181 valid_hostnames[i]); 182 failure = 1; 183 goto done; 184 } 185 } 186 done: 187 return failure; 188 } 189 190 static int 191 test_valid_sandns_names(void) 192 { 193 int i, failure = 0; 194 for (i = 0; valid_sandns_names[i] != NULL; i++) { 195 CBS cbs; 196 CBS_init(&cbs, valid_sandns_names[i], 197 strlen(valid_sandns_names[i])); 198 if (!x509_constraints_valid_sandns(&cbs)) { 199 FAIL("Valid dnsname '%s' rejected\n", 200 valid_sandns_names[i]); 201 failure = 1; 202 goto done; 203 } 204 } 205 done: 206 return failure; 207 } 208 209 static int 210 test_valid_domain_constraints(void) 211 { 212 int i, failure = 0; 213 for (i = 0; valid_domain_constraints[i] != NULL; i++) { 214 CBS cbs; 215 CBS_init(&cbs, valid_domain_constraints[i], 216 strlen(valid_domain_constraints[i])); 217 if (!x509_constraints_valid_domain_constraint(&cbs)) { 218 FAIL("Valid dnsname '%s' rejected\n", 219 valid_domain_constraints[i]); 220 failure = 1; 221 goto done; 222 } 223 } 224 done: 225 return failure; 226 } 227 228 static int 229 test_valid_mbox_names(void) 230 { 231 struct x509_constraints_name name = {0}; 232 int i, failure = 0; 233 for (i = 0; valid_mbox_names[i] != NULL; i++) { 234 CBS cbs; 235 CBS_init(&cbs, valid_mbox_names[i], 236 strlen(valid_mbox_names[i])); 237 if (!x509_constraints_parse_mailbox(&cbs, &name)) { 238 FAIL("Valid mailbox name '%s' rejected\n", 239 valid_mbox_names[i]); 240 failure = 1; 241 goto done; 242 } 243 free(name.name); 244 name.name = NULL; 245 free(name.local); 246 name.local = NULL; 247 } 248 done: 249 return failure; 250 } 251 252 static int 253 test_invalid_hostnames(void) 254 { 255 int i, failure = 0; 256 char *nulhost = "www.openbsd.org\0"; 257 CBS cbs; 258 259 for (i = 0; invalid_hostnames[i] != NULL; i++) { 260 CBS_init(&cbs, invalid_hostnames[i], 261 strlen(invalid_hostnames[i])); 262 if (x509_constraints_valid_host(&cbs)) { 263 FAIL("Invalid hostname '%s' accepted\n", 264 invalid_hostnames[i]); 265 failure = 1; 266 goto done; 267 } 268 } 269 CBS_init(&cbs, nulhost, strlen(nulhost) + 1); 270 if (x509_constraints_valid_host(&cbs)) { 271 FAIL("hostname with NUL byte accepted\n"); 272 failure = 1; 273 goto done; 274 } 275 CBS_init(&cbs, nulhost, strlen(nulhost) + 1); 276 if (x509_constraints_valid_sandns(&cbs)) { 277 FAIL("sandns with NUL byte accepted\n"); 278 failure = 1; 279 goto done; 280 } 281 done: 282 return failure; 283 } 284 285 static int 286 test_invalid_sandns_names(void) 287 { 288 int i, failure = 0; 289 for (i = 0; invalid_sandns_names[i] != NULL; i++) { 290 CBS cbs; 291 CBS_init(&cbs, invalid_sandns_names[i], 292 strlen(invalid_sandns_names[i])); 293 if (x509_constraints_valid_sandns(&cbs)) { 294 FAIL("Valid dnsname '%s' rejected\n", 295 invalid_sandns_names[i]); 296 failure = 1; 297 goto done; 298 } 299 } 300 done: 301 return failure; 302 } 303 304 static int 305 test_invalid_mbox_names(void) 306 { 307 int i, failure = 0; 308 struct x509_constraints_name name = {0}; 309 for (i = 0; invalid_mbox_names[i] != NULL; i++) { 310 CBS cbs; 311 CBS_init(&cbs, invalid_mbox_names[i], 312 strlen(invalid_mbox_names[i])); 313 if (x509_constraints_parse_mailbox(&cbs, &name)) { 314 FAIL("invalid mailbox name '%s' accepted\n", 315 invalid_mbox_names[i]); 316 failure = 1; 317 goto done; 318 } 319 free(name.name); 320 name.name = NULL; 321 free(name.local); 322 name.local = NULL; 323 } 324 done: 325 return failure; 326 } 327 328 static int 329 test_invalid_domain_constraints(void) 330 { 331 int i, failure = 0; 332 for (i = 0; invalid_domain_constraints[i] != NULL; i++) { 333 CBS cbs; 334 CBS_init(&cbs, invalid_domain_constraints[i], 335 strlen(invalid_domain_constraints[i])); 336 if (x509_constraints_valid_domain_constraint(&cbs)) { 337 FAIL("invalid dnsname '%s' accepted\n", 338 invalid_domain_constraints[i]); 339 failure = 1; 340 goto done; 341 } 342 } 343 done: 344 return failure; 345 } 346 347 static int 348 test_invalid_uri(void) 349 { 350 int j, failure = 0; 351 char *hostpart = NULL; 352 353 for (j = 0; invaliduri[j] != NULL; j++) { 354 if (x509_constraints_uri_host(invaliduri[j], 355 strlen(invaliduri[j]), &hostpart) != 0) { 356 FAIL("invalid URI '%s' accepted\n", 357 invaliduri[j]); 358 failure = 1; 359 goto done; 360 } 361 free(hostpart); 362 hostpart = NULL; 363 } 364 365 done: 366 return failure; 367 } 368 369 static int 370 test_constraints1(void) 371 { 372 char *c; 373 size_t cl; 374 char *d; 375 size_t dl; 376 int failure = 0; 377 int error = 0; 378 int i, j; 379 unsigned char *constraints[] = { 380 ".org", 381 ".openbsd.org", 382 "www.openbsd.org", 383 NULL, 384 }; 385 unsigned char *failing[] = { 386 ".ca", 387 "openbsd.ca", 388 "org", 389 NULL, 390 }; 391 unsigned char *matching[] = { 392 "www.openbsd.org", 393 NULL, 394 }; 395 unsigned char *matchinguri[] = { 396 "https://www.openbsd.org", 397 "https://www.openbsd.org/", 398 "https://www.openbsd.org?", 399 "https://www.openbsd.org#", 400 "herp://beck@www.openbsd.org:", 401 "spiffe://beck@www.openbsd.org/this/is/so/spiffe/", 402 NULL, 403 }; 404 unsigned char *failinguri[] = { 405 "https://www.openbsd.ca", 406 "https://www.freebsd.com/", 407 "https://www.openbsd.net?", 408 "https://org#", 409 "herp://beck@org:", 410 "///", 411 "//", 412 "/", 413 "", 414 NULL, 415 }; 416 unsigned char *noauthority[] = { 417 "urn:open62541.server.application", 418 NULL, 419 }; 420 for (i = 0; constraints[i] != NULL; i++) { 421 char *constraint = constraints[i]; 422 size_t clen = strlen(constraints[i]); 423 for (j = 0; matching[j] != NULL; j++) { 424 if (!x509_constraints_domain(matching[j], 425 strlen(matching[j]), constraint, clen)) { 426 FAIL("constraint '%s' should have matched" 427 " '%s'\n", 428 constraint, matching[j]); 429 failure = 1; 430 goto done; 431 } 432 } 433 for (j = 0; matchinguri[j] != NULL; j++) { 434 error = 0; 435 if (!x509_constraints_uri(matchinguri[j], 436 strlen(matchinguri[j]), constraint, clen, &error)) { 437 FAIL("constraint '%s' should have matched URI" 438 " '%s' (error %d)\n", 439 constraint, matchinguri[j], error); 440 failure = 1; 441 goto done; 442 } 443 } 444 for (j = 0; failing[j] != NULL; j++) { 445 if (x509_constraints_domain(failing[j], 446 strlen(failing[j]), constraint, clen)) { 447 FAIL("constraint '%s' should not have matched" 448 " '%s'\n", 449 constraint, failing[j]); 450 failure = 1; 451 goto done; 452 } 453 } 454 for (j = 0; failinguri[j] != NULL; j++) { 455 error = 0; 456 if (x509_constraints_uri(failinguri[j], 457 strlen(failinguri[j]), constraint, clen, &error)) { 458 FAIL("constraint '%s' should not have matched URI" 459 " '%s' (error %d)\n", 460 constraint, failinguri[j], error); 461 failure = 1; 462 goto done; 463 } 464 } 465 for (j = 0; noauthority[j] != NULL; j++) { 466 char *hostpart = NULL; 467 error = 0; 468 if (!x509_constraints_uri_host(noauthority[j], 469 strlen(noauthority[j]), NULL) || 470 !x509_constraints_uri_host(noauthority[j], 471 strlen(noauthority[j]), &hostpart)) { 472 FAIL("name '%s' should parse as a URI", 473 noauthority[j]); 474 failure = 1; 475 free(hostpart); 476 goto done; 477 } 478 free(hostpart); 479 480 if (x509_constraints_uri(noauthority[j], 481 strlen(noauthority[j]), constraint, clen, &error)) { 482 FAIL("constraint '%s' should not have matched URI" 483 " '%s' (error %d)\n", 484 constraint, failinguri[j], error); 485 failure = 1; 486 goto done; 487 } 488 } 489 } 490 c = ".openbsd.org"; 491 cl = strlen(".openbsd.org"); 492 d = "*.openbsd.org"; 493 dl = strlen("*.openbsd.org"); 494 if (!x509_constraints_domain(d, dl, c, cl)) { 495 FAIL("constraint '%s' should have matched '%s'\n", 496 c, d); 497 failure = 1; 498 goto done; 499 } 500 c = "www.openbsd.org"; 501 cl = strlen("www.openbsd.org"); 502 if (x509_constraints_domain(d, dl, c, cl)) { 503 FAIL("constraint '%s' should not have matched '%s'\n", 504 c, d); 505 failure = 1; 506 goto done; 507 } 508 c = ""; 509 cl = 0; 510 if (!x509_constraints_domain(d, dl, c, cl)) { 511 FAIL("constraint '%s' should have matched '%s'\n", 512 c, d); 513 failure = 1; 514 goto done; 515 } 516 done: 517 return failure; 518 } 519 520 int 521 main(int argc, char **argv) 522 { 523 int failed = 0; 524 525 failed |= test_valid_hostnames(); 526 failed |= test_invalid_hostnames(); 527 failed |= test_valid_sandns_names(); 528 failed |= test_invalid_sandns_names(); 529 failed |= test_valid_mbox_names(); 530 failed |= test_invalid_mbox_names(); 531 failed |= test_valid_domain_constraints(); 532 failed |= test_invalid_domain_constraints(); 533 failed |= test_invalid_uri(); 534 failed |= test_constraints1(); 535 536 return (failed); 537 } 538