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