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 NULL, 156 }; 157 158 static int 159 test_valid_hostnames(void) 160 { 161 int i, failure = 0; 162 163 for (i = 0; valid_hostnames[i] != NULL; i++) { 164 if (!x509_constraints_valid_host(valid_hostnames[i], 165 strlen(valid_hostnames[i]))) { 166 FAIL("Valid hostname '%s' rejected\n", 167 valid_hostnames[i]); 168 failure = 1; 169 goto done; 170 } 171 if (!x509_constraints_valid_sandns(valid_hostnames[i], 172 strlen(valid_hostnames[i]))) { 173 FAIL("Valid sandns '%s' rejected\n", 174 valid_hostnames[i]); 175 failure = 1; 176 goto done; 177 } 178 } 179 done: 180 return failure; 181 } 182 183 static int 184 test_valid_sandns_names(void) 185 { 186 int i, failure = 0; 187 for (i = 0; valid_sandns_names[i] != NULL; i++) { 188 if (!x509_constraints_valid_sandns(valid_sandns_names[i], 189 strlen(valid_sandns_names[i]))) { 190 FAIL("Valid dnsname '%s' rejected\n", 191 valid_sandns_names[i]); 192 failure = 1; 193 goto done; 194 } 195 } 196 done: 197 return failure; 198 } 199 200 static int 201 test_valid_domain_constraints(void) 202 { 203 int i, failure = 0; 204 for (i = 0; valid_domain_constraints[i] != NULL; i++) { 205 if (!x509_constraints_valid_domain_constraint(valid_domain_constraints[i], 206 strlen(valid_domain_constraints[i]))) { 207 FAIL("Valid dnsname '%s' rejected\n", 208 valid_domain_constraints[i]); 209 failure = 1; 210 goto done; 211 } 212 } 213 done: 214 return failure; 215 } 216 217 static int 218 test_valid_mbox_names(void) 219 { 220 struct x509_constraints_name name = {0}; 221 int i, failure = 0; 222 for (i = 0; valid_mbox_names[i] != NULL; i++) { 223 if (!x509_constraints_parse_mailbox(valid_mbox_names[i], 224 strlen(valid_mbox_names[i]), &name)) { 225 FAIL("Valid mailbox name '%s' rejected\n", 226 valid_mbox_names[i]); 227 failure = 1; 228 goto done; 229 } 230 free(name.name); 231 name.name = NULL; 232 free(name.local); 233 name.local = NULL; 234 } 235 done: 236 return failure; 237 } 238 239 static int 240 test_invalid_hostnames(void) 241 { 242 int i, failure = 0; 243 char *nulhost = "www.openbsd.org\0"; 244 245 for (i = 0; invalid_hostnames[i] != NULL; i++) { 246 if (x509_constraints_valid_host(invalid_hostnames[i], 247 strlen(invalid_hostnames[i]))) { 248 FAIL("Invalid hostname '%s' accepted\n", 249 invalid_hostnames[i]); 250 failure = 1; 251 goto done; 252 } 253 if (x509_constraints_valid_sandns(invalid_hostnames[i], 254 strlen(invalid_hostnames[i]))) { 255 FAIL("Invalid sandns '%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 } 344 goto done; 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 NULL, 390 }; 391 for (i = 0; constraints[i] != NULL; i++) { 392 char *constraint = constraints[i]; 393 size_t clen = strlen(constraints[i]); 394 for (j = 0; matching[j] != NULL; j++) { 395 if (!x509_constraints_domain(matching[j], 396 strlen(matching[j]), constraint, clen)) { 397 FAIL("constraint '%s' should have matched" 398 " '%s'\n", 399 constraint, matching[j]); 400 failure = 1; 401 goto done; 402 } 403 } 404 for (j = 0; matchinguri[j] != NULL; j++) { 405 error = 0; 406 if (!x509_constraints_uri(matchinguri[j], 407 strlen(matchinguri[j]), constraint, clen, &error)) { 408 FAIL("constraint '%s' should have matched URI" 409 " '%s' (error %d)\n", 410 constraint, matchinguri[j], error); 411 failure = 1; 412 goto done; 413 } 414 } 415 for (j = 0; failing[j] != NULL; j++) { 416 if (x509_constraints_domain(failing[j], 417 strlen(failing[j]), constraint, clen)) { 418 FAIL("constraint '%s' should not have matched" 419 " '%s'\n", 420 constraint, failing[j]); 421 failure = 1; 422 goto done; 423 } 424 } 425 for (j = 0; failinguri[j] != NULL; j++) { 426 error = 0; 427 if (x509_constraints_uri(failinguri[j], 428 strlen(failinguri[j]), constraint, clen, &error)) { 429 FAIL("constraint '%s' should not have matched URI" 430 " '%s' (error %d)\n", 431 constraint, failinguri[j], error); 432 failure = 1; 433 goto done; 434 } 435 } 436 } 437 c = ".openbsd.org"; 438 cl = strlen(".openbsd.org"); 439 d = "*.openbsd.org"; 440 dl = strlen("*.openbsd.org"); 441 if (!x509_constraints_domain(d, dl, c, cl)) { 442 FAIL("constraint '%s' should have matched '%s'\n", 443 c, d); 444 failure = 1; 445 goto done; 446 } 447 c = "www.openbsd.org"; 448 cl = strlen("www.openbsd.org"); 449 if (x509_constraints_domain(d, dl, c, cl)) { 450 FAIL("constraint '%s' should not have matched '%s'\n", 451 c, d); 452 failure = 1; 453 goto done; 454 } 455 c = ""; 456 cl = 0; 457 if (!x509_constraints_domain(d, dl, c, cl)) { 458 FAIL("constraint '%s' should have matched '%s'\n", 459 c, d); 460 failure = 1; 461 goto done; 462 } 463 done: 464 return failure; 465 } 466 467 int 468 main(int argc, char **argv) 469 { 470 int failed = 0; 471 472 failed |= test_valid_hostnames(); 473 failed |= test_invalid_hostnames(); 474 failed |= test_valid_sandns_names(); 475 failed |= test_invalid_sandns_names(); 476 failed |= test_valid_mbox_names(); 477 failed |= test_invalid_mbox_names(); 478 failed |= test_valid_domain_constraints(); 479 failed |= test_invalid_domain_constraints(); 480 failed |= test_invalid_uri(); 481 failed |= test_constraints1(); 482 483 return (failed); 484 } 485