1 /* $OpenBSD: constraints.c */ 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 27 #define FAIL(msg, ...) \ 28 do { \ 29 fprintf(stderr, "[%s:%d] FAIL: ", __FILE__, __LINE__); \ 30 fprintf(stderr, msg, ##__VA_ARGS__); \ 31 } while(0) 32 33 unsigned char *valid_hostnames[] = { 34 "openbsd.org", 35 "op3nbsd.org", 36 "org", 37 "3openbsd.com", 38 "3-0penb-d.c-m", 39 "a", 40 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 41 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 42 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 43 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 44 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 45 "open_bsd.org", /* because this is liberal */ 46 NULL, 47 }; 48 49 unsigned char *valid_sandns_names[] = { 50 "*.ca", 51 "*.op3nbsd.org", 52 "c*.openbsd.org", 53 "foo.*.d*.c*.openbsd.org", 54 NULL, 55 }; 56 57 unsigned char *valid_domain_constraints[] = { 58 "", 59 ".ca", 60 ".op3nbsd.org", 61 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 62 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 63 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 64 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 65 "www.openbsd.org", 66 NULL, 67 }; 68 69 unsigned char *valid_mbox_names[] = { 70 "\"!#$%&\\\"*+-/=?\002^_`{|}~.\"@openbsd.org", 71 "beck@openbsd.org", 72 "beck@openbsd.org", 73 "beck@op3nbsd.org", 74 "beck@org", 75 "beck@3openbsd.com", 76 "beck@3-0penb-d.c-m", 77 "bec@a", 78 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 79 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 80 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 81 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 82 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 83 "beck@open_bsd.org", /* because this is liberal */ 84 NULL, 85 }; 86 87 unsigned char *invalid_hostnames[] = { 88 "openbsd.org.", 89 "openbsd..org", 90 "openbsd.org-", 91 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 92 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 93 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 94 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 95 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 96 "-p3nbsd.org", 97 "openbs-.org", 98 "openbsd\n.org", 99 "open\178bsd.org", 100 "open\255bsd.org", 101 "*.openbsd.org", 102 NULL, 103 }; 104 105 unsigned char *invalid_sandns_names[] = { 106 "", 107 ".", 108 "*.a", 109 "*.", 110 "*.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 111 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 112 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 113 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 114 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 115 "*.-p3nbsd.org", 116 "*.*..openbsd.org", 117 "*..openbsd.org", 118 ".openbsd.org", 119 "c*c.openbsd.org", 120 NULL, 121 }; 122 123 unsigned char *invalid_mbox_names[] = { 124 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 125 "beck@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 126 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 127 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 128 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 129 "beck@.-openbsd.org", 130 "beck@.openbsd.org.", 131 "beck@.a", 132 "beck@.", 133 "beck@", 134 "beck@.ca", 135 "@openbsd.org", 136 NULL, 137 }; 138 139 unsigned char *invalid_domain_constraints[] = { 140 ".", 141 ".a", 142 "..", 143 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", 144 ".aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 145 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 146 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa." 147 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.a", 148 ".-p3nbsd.org", 149 "..openbsd.org", 150 NULL, 151 }; 152 153 unsigned char *invaliduri[] = { 154 "https://-www.openbsd.org", 155 "https://.www.openbsd.org/", 156 "https://www.ope|nbsd.org%", 157 "https://www.openbsd.org.#", 158 "///", 159 "//", 160 "/", 161 "", 162 NULL, 163 }; 164 165 static int 166 test_valid_hostnames(void) 167 { 168 int i, failure = 0; 169 170 for (i = 0; valid_hostnames[i] != NULL; i++) { 171 if (!x509_constraints_valid_host(valid_hostnames[i], 172 strlen(valid_hostnames[i]))) { 173 FAIL("Valid hostname '%s' rejected\n", 174 valid_hostnames[i]); 175 failure = 1; 176 goto done; 177 } 178 if (!x509_constraints_valid_sandns(valid_hostnames[i], 179 strlen(valid_hostnames[i]))) { 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 if (!x509_constraints_valid_sandns(valid_sandns_names[i], 196 strlen(valid_sandns_names[i]))) { 197 FAIL("Valid dnsname '%s' rejected\n", 198 valid_sandns_names[i]); 199 failure = 1; 200 goto done; 201 } 202 } 203 done: 204 return failure; 205 } 206 207 static int 208 test_valid_domain_constraints(void) 209 { 210 int i, failure = 0; 211 for (i = 0; valid_domain_constraints[i] != NULL; i++) { 212 if (!x509_constraints_valid_domain_constraint(valid_domain_constraints[i], 213 strlen(valid_domain_constraints[i]))) { 214 FAIL("Valid dnsname '%s' rejected\n", 215 valid_domain_constraints[i]); 216 failure = 1; 217 goto done; 218 } 219 } 220 done: 221 return failure; 222 } 223 224 static int 225 test_valid_mbox_names(void) 226 { 227 struct x509_constraints_name name = {0}; 228 int i, failure = 0; 229 for (i = 0; valid_mbox_names[i] != NULL; i++) { 230 if (!x509_constraints_parse_mailbox(valid_mbox_names[i], 231 strlen(valid_mbox_names[i]), &name)) { 232 FAIL("Valid mailbox name '%s' rejected\n", 233 valid_mbox_names[i]); 234 failure = 1; 235 goto done; 236 } 237 free(name.name); 238 name.name = NULL; 239 free(name.local); 240 name.local = NULL; 241 } 242 done: 243 return failure; 244 } 245 246 static int 247 test_invalid_hostnames(void) 248 { 249 int i, failure = 0; 250 char *nulhost = "www.openbsd.org\0"; 251 252 for (i = 0; invalid_hostnames[i] != NULL; i++) { 253 if (x509_constraints_valid_host(invalid_hostnames[i], 254 strlen(invalid_hostnames[i]))) { 255 FAIL("Invalid hostname '%s' accepted\n", 256 invalid_hostnames[i]); 257 failure = 1; 258 goto done; 259 } 260 } 261 if (x509_constraints_valid_host(nulhost, 262 strlen(nulhost) + 1)) { 263 FAIL("hostname with NUL byte accepted\n"); 264 failure = 1; 265 goto done; 266 } 267 if (x509_constraints_valid_sandns(nulhost, 268 strlen(nulhost) + 1)) { 269 FAIL("sandns with NUL byte accepted\n"); 270 failure = 1; 271 goto done; 272 } 273 done: 274 return failure; 275 } 276 277 static int 278 test_invalid_sandns_names(void) 279 { 280 int i, failure = 0; 281 for (i = 0; invalid_sandns_names[i] != NULL; i++) { 282 if (x509_constraints_valid_sandns(invalid_sandns_names[i], 283 strlen(invalid_sandns_names[i]))) { 284 FAIL("Valid dnsname '%s' rejected\n", 285 invalid_sandns_names[i]); 286 failure = 1; 287 goto done; 288 } 289 } 290 done: 291 return failure; 292 } 293 294 static int 295 test_invalid_mbox_names(void) 296 { 297 int i, failure = 0; 298 struct x509_constraints_name name = {0}; 299 for (i = 0; invalid_mbox_names[i] != NULL; i++) { 300 if (x509_constraints_parse_mailbox(invalid_mbox_names[i], 301 strlen(invalid_mbox_names[i]), &name)) { 302 FAIL("invalid mailbox name '%s' accepted\n", 303 invalid_mbox_names[i]); 304 failure = 1; 305 goto done; 306 } 307 free(name.name); 308 name.name = NULL; 309 free(name.local); 310 name.local = NULL; 311 } 312 done: 313 return failure; 314 } 315 316 static int 317 test_invalid_domain_constraints(void) 318 { 319 int i, failure = 0; 320 for (i = 0; invalid_domain_constraints[i] != NULL; i++) { 321 if (x509_constraints_valid_domain_constraint(invalid_domain_constraints[i], 322 strlen(invalid_domain_constraints[i]))) { 323 FAIL("invalid dnsname '%s' accepted\n", 324 invalid_domain_constraints[i]); 325 failure = 1; 326 goto done; 327 } 328 } 329 done: 330 return failure; 331 } 332 333 static int 334 test_invalid_uri(void) 335 { 336 int j, failure=0; 337 char *hostpart = NULL; 338 339 for (j = 0; invaliduri[j] != NULL; j++) { 340 if (x509_constraints_uri_host(invaliduri[j], 341 strlen(invaliduri[j]), &hostpart) != 0) { 342 FAIL("invalid URI '%s' accepted\n", 343 invaliduri[j]); 344 failure = 1; 345 goto done; 346 } 347 free(hostpart); 348 hostpart = NULL; 349 } 350 351 done: 352 return failure; 353 } 354 355 static int 356 test_constraints1(void) 357 { 358 char *c; size_t cl; 359 char *d; size_t dl; 360 int failure = 0; 361 int error = 0; 362 int i, j; 363 unsigned char *constraints[] = { 364 ".org", 365 ".openbsd.org", 366 "www.openbsd.org", 367 NULL, 368 }; 369 unsigned char *failing[] = { 370 ".ca", 371 "openbsd.ca", 372 "org", 373 NULL, 374 }; 375 unsigned char *matching[] = { 376 "www.openbsd.org", 377 NULL, 378 }; 379 unsigned char *matchinguri[] = { 380 "https://www.openbsd.org", 381 "https://www.openbsd.org/", 382 "https://www.openbsd.org?", 383 "https://www.openbsd.org#", 384 "herp://beck@www.openbsd.org:", 385 "spiffe://beck@www.openbsd.org/this/is/so/spiffe/", 386 NULL, 387 }; 388 unsigned char *failinguri[] = { 389 "https://www.openbsd.ca", 390 "https://www.freebsd.com/", 391 "https://www.openbsd.net?", 392 "https://org#", 393 "herp://beck@org:", 394 "///", 395 "//", 396 "/", 397 "", 398 NULL, 399 }; 400 for (i = 0; constraints[i] != NULL; i++) { 401 char *constraint = constraints[i]; 402 size_t clen = strlen(constraints[i]); 403 for (j = 0; matching[j] != NULL; j++) { 404 if (!x509_constraints_domain(matching[j], 405 strlen(matching[j]), constraint, clen)) { 406 FAIL("constraint '%s' should have matched" 407 " '%s'\n", 408 constraint, matching[j]); 409 failure = 1; 410 goto done; 411 } 412 } 413 for (j = 0; matchinguri[j] != NULL; j++) { 414 error = 0; 415 if (!x509_constraints_uri(matchinguri[j], 416 strlen(matchinguri[j]), constraint, clen, &error)) { 417 FAIL("constraint '%s' should have matched URI" 418 " '%s' (error %d)\n", 419 constraint, matchinguri[j], error); 420 failure = 1; 421 goto done; 422 } 423 } 424 for (j = 0; failing[j] != NULL; j++) { 425 if (x509_constraints_domain(failing[j], 426 strlen(failing[j]), constraint, clen)) { 427 FAIL("constraint '%s' should not have matched" 428 " '%s'\n", 429 constraint, failing[j]); 430 failure = 1; 431 goto done; 432 } 433 } 434 for (j = 0; failinguri[j] != NULL; j++) { 435 error = 0; 436 if (x509_constraints_uri(failinguri[j], 437 strlen(failinguri[j]), constraint, clen, &error)) { 438 FAIL("constraint '%s' should not have matched URI" 439 " '%s' (error %d)\n", 440 constraint, failinguri[j], error); 441 failure = 1; 442 goto done; 443 } 444 } 445 } 446 c = ".openbsd.org"; 447 cl = strlen(".openbsd.org"); 448 d = "*.openbsd.org"; 449 dl = strlen("*.openbsd.org"); 450 if (!x509_constraints_domain(d, dl, c, cl)) { 451 FAIL("constraint '%s' should have matched '%s'\n", 452 c, d); 453 failure = 1; 454 goto done; 455 } 456 c = "www.openbsd.org"; 457 cl = strlen("www.openbsd.org"); 458 if (x509_constraints_domain(d, dl, c, cl)) { 459 FAIL("constraint '%s' should not have matched '%s'\n", 460 c, d); 461 failure = 1; 462 goto done; 463 } 464 c = ""; 465 cl = 0; 466 if (!x509_constraints_domain(d, dl, c, cl)) { 467 FAIL("constraint '%s' should have matched '%s'\n", 468 c, d); 469 failure = 1; 470 goto done; 471 } 472 done: 473 return failure; 474 } 475 476 int 477 main(int argc, char **argv) 478 { 479 int failed = 0; 480 481 failed |= test_valid_hostnames(); 482 failed |= test_invalid_hostnames(); 483 failed |= test_valid_sandns_names(); 484 failed |= test_invalid_sandns_names(); 485 failed |= test_valid_mbox_names(); 486 failed |= test_invalid_mbox_names(); 487 failed |= test_valid_domain_constraints(); 488 failed |= test_invalid_domain_constraints(); 489 failed |= test_invalid_uri(); 490 failed |= test_constraints1(); 491 492 return (failed); 493 } 494