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 int j, failure=0; 336 char *hostpart; 337 for (j = 0; invaliduri[j] != NULL; j++) { 338 if (x509_constraints_uri_host(invaliduri[j], 339 strlen(invaliduri[j]), &hostpart) != 0) { 340 FAIL("invalid URI '%s' accepted\n", 341 invaliduri[j]); 342 failure = 1; 343 goto done; 344 } 345 } 346 done: 347 return failure; 348 } 349 350 static int 351 test_constraints1(void) 352 { 353 char *c; size_t cl; 354 char *d; size_t dl; 355 int failure = 0; 356 int error = 0; 357 int i, j; 358 unsigned char *constraints[] = { 359 ".org", 360 ".openbsd.org", 361 "www.openbsd.org", 362 NULL, 363 }; 364 unsigned char *failing[] = { 365 ".ca", 366 "openbsd.ca", 367 "org", 368 NULL, 369 }; 370 unsigned char *matching[] = { 371 "www.openbsd.org", 372 NULL, 373 }; 374 unsigned char *matchinguri[] = { 375 "https://www.openbsd.org", 376 "https://www.openbsd.org/", 377 "https://www.openbsd.org?", 378 "https://www.openbsd.org#", 379 "herp://beck@www.openbsd.org:", 380 "spiffe://beck@www.openbsd.org/this/is/so/spiffe/", 381 NULL, 382 }; 383 unsigned char *failinguri[] = { 384 "https://www.openbsd.ca", 385 "https://www.freebsd.com/", 386 "https://www.openbsd.net?", 387 "https://org#", 388 "herp://beck@org:", 389 "///", 390 "//", 391 "/", 392 "", 393 NULL, 394 }; 395 for (i = 0; constraints[i] != NULL; i++) { 396 char *constraint = constraints[i]; 397 size_t clen = strlen(constraints[i]); 398 for (j = 0; matching[j] != NULL; j++) { 399 if (!x509_constraints_domain(matching[j], 400 strlen(matching[j]), constraint, clen)) { 401 FAIL("constraint '%s' should have matched" 402 " '%s'\n", 403 constraint, matching[j]); 404 failure = 1; 405 goto done; 406 } 407 } 408 for (j = 0; matchinguri[j] != NULL; j++) { 409 error = 0; 410 if (!x509_constraints_uri(matchinguri[j], 411 strlen(matchinguri[j]), constraint, clen, &error)) { 412 FAIL("constraint '%s' should have matched URI" 413 " '%s' (error %d)\n", 414 constraint, matchinguri[j], error); 415 failure = 1; 416 goto done; 417 } 418 } 419 for (j = 0; failing[j] != NULL; j++) { 420 if (x509_constraints_domain(failing[j], 421 strlen(failing[j]), constraint, clen)) { 422 FAIL("constraint '%s' should not have matched" 423 " '%s'\n", 424 constraint, failing[j]); 425 failure = 1; 426 goto done; 427 } 428 } 429 for (j = 0; failinguri[j] != NULL; j++) { 430 error = 0; 431 if (x509_constraints_uri(failinguri[j], 432 strlen(failinguri[j]), constraint, clen, &error)) { 433 FAIL("constraint '%s' should not have matched URI" 434 " '%s' (error %d)\n", 435 constraint, failinguri[j], error); 436 failure = 1; 437 goto done; 438 } 439 } 440 } 441 c = ".openbsd.org"; 442 cl = strlen(".openbsd.org"); 443 d = "*.openbsd.org"; 444 dl = strlen("*.openbsd.org"); 445 if (!x509_constraints_domain(d, dl, c, cl)) { 446 FAIL("constraint '%s' should have matched '%s'\n", 447 c, d); 448 failure = 1; 449 goto done; 450 } 451 c = "www.openbsd.org"; 452 cl = strlen("www.openbsd.org"); 453 if (x509_constraints_domain(d, dl, c, cl)) { 454 FAIL("constraint '%s' should not have matched '%s'\n", 455 c, d); 456 failure = 1; 457 goto done; 458 } 459 c = ""; 460 cl = 0; 461 if (!x509_constraints_domain(d, dl, c, cl)) { 462 FAIL("constraint '%s' should have matched '%s'\n", 463 c, d); 464 failure = 1; 465 goto done; 466 } 467 done: 468 return failure; 469 } 470 471 int 472 main(int argc, char **argv) 473 { 474 int failed = 0; 475 476 failed |= test_valid_hostnames(); 477 failed |= test_invalid_hostnames(); 478 failed |= test_valid_sandns_names(); 479 failed |= test_invalid_sandns_names(); 480 failed |= test_valid_mbox_names(); 481 failed |= test_invalid_mbox_names(); 482 failed |= test_valid_domain_constraints(); 483 failed |= test_invalid_domain_constraints(); 484 failed |= test_invalid_uri(); 485 failed |= test_constraints1(); 486 487 return (failed); 488 } 489