1 /* $OpenBSD: verifytest.c,v 1.7 2017/04/30 03:53:31 jsing Exp $ */ 2 /* 3 * Copyright (c) 2014 Joel Sing <jsing@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 <stdio.h> 20 #include <stdlib.h> 21 22 #include <openssl/x509v3.h> 23 #include <tls.h> 24 25 extern int tls_check_name(struct tls *ctx, X509 *cert, const char *name, 26 int *match); 27 28 struct alt_name { 29 const char name[128]; 30 int name_len; 31 int name_type; 32 }; 33 34 struct verify_test { 35 const char common_name[128]; 36 int common_name_len; 37 struct alt_name alt_name1; 38 struct alt_name alt_name2; 39 struct alt_name alt_name3; 40 const char name[128]; 41 int want_return; 42 int want_match; 43 }; 44 45 struct verify_test verify_tests[] = { 46 { 47 /* CN without SANs - matching. */ 48 .common_name = "www.openbsd.org", 49 .common_name_len = -1, 50 .name = "www.openbsd.org", 51 .want_return = 0, 52 .want_match = 1, 53 }, 54 { 55 /* Zero length name - non-matching. */ 56 .common_name = "www.openbsd.org", 57 .common_name_len = -1, 58 .name = "", 59 .want_return = 0, 60 .want_match = 0, 61 }, 62 { 63 /* CN wildcard without SANs - matching. */ 64 .common_name = "*.openbsd.org", 65 .common_name_len = -1, 66 .name = "www.openbsd.org", 67 .want_return = 0, 68 .want_match = 1, 69 }, 70 { 71 /* CN without SANs - non-matching. */ 72 .common_name = "www.openbsdfoundation.org", 73 .common_name_len = -1, 74 .name = "www.openbsd.org", 75 .want_return = 0, 76 .want_match = 0, 77 }, 78 { 79 /* CN wildcard without SANs - invalid CN wildcard. */ 80 .common_name = "w*.openbsd.org", 81 .common_name_len = -1, 82 .name = "www.openbsd.org", 83 .want_return = 0, 84 .want_match = 0, 85 }, 86 { 87 /* CN wildcard without SANs - invalid CN wildcard. */ 88 .common_name = "www.*.org", 89 .common_name_len = -1, 90 .name = "www.openbsd.org", 91 .want_return = 0, 92 .want_match = 0, 93 }, 94 { 95 /* CN wildcard without SANs - invalid CN wildcard. */ 96 .common_name = "www.openbsd.*", 97 .common_name_len = -1, 98 .name = "www.openbsd.org", 99 .want_return = 0, 100 .want_match = 0, 101 }, 102 { 103 /* CN wildcard without SANs - invalid CN wildcard. */ 104 .common_name = "*", 105 .common_name_len = -1, 106 .name = "www.openbsd.org", 107 .want_return = 0, 108 .want_match = 0, 109 }, 110 { 111 /* CN wildcard without SANs - invalid CN wildcard. */ 112 .common_name = "*.org", 113 .common_name_len = -1, 114 .name = "www.openbsd.org", 115 .want_return = 0, 116 .want_match = 0, 117 }, 118 { 119 /* CN wildcard without SANs - invalid CN wildcard. */ 120 .common_name = "*.org", 121 .common_name_len = -1, 122 .name = "openbsd.org", 123 .want_return = 0, 124 .want_match = 0, 125 }, 126 { 127 /* CN IPv4 without SANs - matching. */ 128 .common_name = "1.2.3.4", 129 .common_name_len = -1, 130 .name = "1.2.3.4", 131 .want_return = 0, 132 .want_match = 1, 133 }, 134 { 135 /* CN IPv4 wildcard without SANS - invalid IP wildcard. */ 136 .common_name = "*.2.3.4", 137 .common_name_len = -1, 138 .name = "1.2.3.4", 139 .want_return = 0, 140 .want_match = 0, 141 }, 142 { 143 /* CN IPv6 without SANs - matching. */ 144 .common_name = "cafe::beef", 145 .common_name_len = -1, 146 .name = "cafe::beef", 147 .want_return = 0, 148 .want_match = 1, 149 }, 150 { 151 /* CN without SANs - error due to embedded NUL in CN. */ 152 .common_name = { 153 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 154 0x62, 0x73, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x00, 155 0x6e, 0x61, 0x73, 0x74, 0x79, 0x2e, 0x6f, 0x72, 156 0x67, 157 }, 158 .common_name_len = 25, 159 .name = "www.openbsd.org", 160 .want_return = -1, 161 .want_match = 0, 162 }, 163 { 164 /* CN wildcard without SANs - invalid non-matching name. */ 165 .common_name = "*.openbsd.org", 166 .common_name_len = -1, 167 .name = ".openbsd.org", 168 .want_return = 0, 169 .want_match = 0, 170 }, 171 { 172 /* CN with SANs - matching on first SAN. */ 173 .common_name = "www.openbsd.org", 174 .common_name_len = -1, 175 .alt_name1 = { 176 .name = "www.openbsd.org", 177 .name_len = -1, 178 .name_type = GEN_DNS, 179 }, 180 .alt_name2 = { 181 .name = "ftp.openbsd.org", 182 .name_len = -1, 183 .name_type = GEN_DNS, 184 }, 185 .name = "www.openbsd.org", 186 .want_return = 0, 187 .want_match = 1, 188 }, 189 { 190 /* SANs only - matching on first SAN. */ 191 .common_name_len = 0, 192 .alt_name1 = { 193 .name = "www.openbsd.org", 194 .name_len = -1, 195 .name_type = GEN_DNS, 196 }, 197 .alt_name2 = { 198 .name = "ftp.openbsd.org", 199 .name_len = -1, 200 .name_type = GEN_DNS, 201 }, 202 .name = "www.openbsd.org", 203 .want_return = 0, 204 .want_match = 1, 205 }, 206 { 207 /* SANs only - matching on second SAN. */ 208 .common_name_len = 0, 209 .alt_name1 = { 210 .name = "www.openbsd.org", 211 .name_len = -1, 212 .name_type = GEN_DNS, 213 }, 214 .alt_name2 = { 215 .name = "ftp.openbsd.org", 216 .name_len = -1, 217 .name_type = GEN_DNS, 218 }, 219 .name = "ftp.openbsd.org", 220 .want_return = 0, 221 .want_match = 1, 222 }, 223 { 224 /* SANs only - non-matching. */ 225 .common_name_len = 0, 226 .alt_name1 = { 227 .name = "www.openbsd.org", 228 .name_len = -1, 229 .name_type = GEN_DNS, 230 }, 231 .alt_name2 = { 232 .name = "ftp.openbsd.org", 233 .name_len = -1, 234 .name_type = GEN_DNS, 235 }, 236 .name = "mail.openbsd.org", 237 .want_return = 0, 238 .want_match = 0, 239 }, 240 { 241 /* CN with SANs - matching on second SAN. */ 242 .common_name = "www.openbsd.org", 243 .common_name_len = -1, 244 .alt_name1 = { 245 .name = "www.openbsd.org", 246 .name_len = -1, 247 .name_type = GEN_DNS, 248 }, 249 .alt_name2 = { 250 .name = "ftp.openbsd.org", 251 .name_len = -1, 252 .name_type = GEN_DNS, 253 }, 254 .name = "ftp.openbsd.org", 255 .want_return = 0, 256 .want_match = 1, 257 }, 258 { 259 /* CN with SANs - matching on wildcard second SAN. */ 260 .common_name = "www.openbsdfoundation.org", 261 .common_name_len = -1, 262 .alt_name1 = { 263 .name = "www.openbsdfoundation.org", 264 .name_len = -1, 265 .name_type = GEN_DNS, 266 }, 267 .alt_name2 = { 268 .name = "*.openbsd.org", 269 .name_len = -1, 270 .name_type = GEN_DNS, 271 }, 272 .name = "www.openbsd.org", 273 .want_return = 0, 274 .want_match = 1, 275 }, 276 { 277 /* CN with SANs - non-matching invalid wildcard. */ 278 .common_name = "www.openbsdfoundation.org", 279 .common_name_len = -1, 280 .alt_name1 = { 281 .name = "www.openbsdfoundation.org", 282 .name_len = -1, 283 .name_type = GEN_DNS, 284 }, 285 .alt_name2 = { 286 .name = "*.org", 287 .name_len = -1, 288 .name_type = GEN_DNS, 289 }, 290 .name = "www.openbsd.org", 291 .want_return = 0, 292 .want_match = 0, 293 }, 294 { 295 /* CN with SANs - non-matching IPv4 due to GEN_DNS SAN. */ 296 .common_name = "www.openbsd.org", 297 .common_name_len = -1, 298 .alt_name1 = { 299 .name = "www.openbsd.org", 300 .name_len = -1, 301 .name_type = GEN_DNS, 302 }, 303 .alt_name2 = { 304 .name = "1.2.3.4", 305 .name_len = -1, 306 .name_type = GEN_DNS, 307 }, 308 .name = "1.2.3.4", 309 .want_return = 0, 310 .want_match = 0, 311 }, 312 { 313 /* CN with SANs - matching IPv4 on GEN_IPADD SAN. */ 314 .common_name = "www.openbsd.org", 315 .common_name_len = -1, 316 .alt_name1 = { 317 .name = "www.openbsd.org", 318 .name_len = -1, 319 .name_type = GEN_DNS, 320 }, 321 .alt_name2 = { 322 .name = {0x01, 0x02, 0x03, 0x04}, 323 .name_len = 4, 324 .name_type = GEN_IPADD, 325 }, 326 .name = "1.2.3.4", 327 .want_return = 0, 328 .want_match = 1, 329 }, 330 { 331 /* CN with SANs - matching IPv6 on GEN_IPADD SAN. */ 332 .common_name = "www.openbsd.org", 333 .common_name_len = -1, 334 .alt_name1 = { 335 .name = "www.openbsd.org", 336 .name_len = -1, 337 .name_type = GEN_DNS, 338 }, 339 .alt_name2 = { 340 .name = { 341 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xef, 343 }, 344 .name_len = 16, 345 .name_type = GEN_IPADD, 346 }, 347 .name = "cafe::beef", 348 .want_return = 0, 349 .want_match = 1, 350 }, 351 { 352 /* CN with SANs - error due to embedded NUL in GEN_DNS. */ 353 .common_name = "www.openbsd.org.nasty.org", 354 .common_name_len = -1, 355 .alt_name1 = { 356 .name = "www.openbsd.org.nasty.org", 357 .name_len = -1, 358 .name_type = GEN_DNS, 359 }, 360 .alt_name2 = { 361 .name = { 362 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 363 0x62, 0x73, 0x64, 0x2e, 0x6f, 0x72, 0x67, 0x00, 364 0x6e, 0x61, 0x73, 0x74, 0x79, 0x2e, 0x6f, 0x72, 365 0x67, 366 }, 367 .name_len = 25, 368 .name_type = GEN_DNS, 369 }, 370 .name = "www.openbsd.org", 371 .want_return = -1, 372 .want_match = 0, 373 }, 374 { 375 /* CN with SAN - non-matching due to non-matching SAN. */ 376 .common_name = "www.openbsd.org", 377 .common_name_len = -1, 378 .alt_name1 = { 379 .name = "ftp.openbsd.org", 380 .name_len = -1, 381 .name_type = GEN_DNS, 382 }, 383 .name = "www.openbsd.org", 384 .want_return = 0, 385 .want_match = 0, 386 }, 387 { 388 /* CN with SAN - error due to illegal dNSName. */ 389 .common_name = "www.openbsd.org", 390 .common_name_len = -1, 391 .alt_name1 = { 392 .name = " ", 393 .name_len = -1, 394 .name_type = GEN_DNS, 395 }, 396 .name = "www.openbsd.org", 397 .want_return = -1, 398 .want_match = 0, 399 }, 400 }; 401 402 #define N_VERIFY_TESTS \ 403 (sizeof(verify_tests) / sizeof(*verify_tests)) 404 405 static void 406 alt_names_add(STACK_OF(GENERAL_NAME) *alt_name_stack, struct alt_name *alt) 407 { 408 ASN1_STRING *alt_name_str; 409 GENERAL_NAME *alt_name; 410 411 if ((alt_name = GENERAL_NAME_new()) == NULL) 412 errx(1, "failed to malloc GENERAL_NAME"); 413 alt_name->type = alt->name_type; 414 415 if ((alt_name_str = ASN1_STRING_new()) == NULL) 416 errx(1, "failed to malloc alt name"); 417 if (ASN1_STRING_set(alt_name_str, alt->name, alt->name_len) == 0) 418 errx(1, "failed to set alt name"); 419 420 switch (alt_name->type) { 421 case GEN_DNS: 422 alt_name->d.dNSName = alt_name_str; 423 break; 424 case GEN_IPADD: 425 alt_name->d.iPAddress = alt_name_str; 426 break; 427 default: 428 errx(1, "unknown alt name type (%i)", alt_name->type); 429 } 430 431 if (sk_GENERAL_NAME_push(alt_name_stack, alt_name) == 0) 432 errx(1, "failed to push alt_name"); 433 } 434 435 static void 436 cert_add_alt_names(X509 *cert, struct verify_test *vt) 437 { 438 STACK_OF(GENERAL_NAME) *alt_name_stack = NULL; 439 440 if (vt->alt_name1.name_type == 0) 441 return; 442 443 if ((alt_name_stack = sk_GENERAL_NAME_new_null()) == NULL) 444 errx(1, "failed to malloc sk_GENERAL_NAME"); 445 446 if (vt->alt_name1.name_type != 0) 447 alt_names_add(alt_name_stack, &vt->alt_name1); 448 if (vt->alt_name2.name_type != 0) 449 alt_names_add(alt_name_stack, &vt->alt_name2); 450 if (vt->alt_name3.name_type != 0) 451 alt_names_add(alt_name_stack, &vt->alt_name3); 452 453 if (X509_add1_ext_i2d(cert, NID_subject_alt_name, 454 alt_name_stack, 0, 0) == 0) 455 errx(1, "failed to set subject alt name"); 456 457 sk_GENERAL_NAME_pop_free(alt_name_stack, GENERAL_NAME_free); 458 } 459 460 static int 461 do_verify_test(int test_no, struct verify_test *vt) 462 { 463 struct tls *tls; 464 X509_NAME *name; 465 X509 *cert; 466 int failed = 1; 467 int match; 468 469 /* Build certificate structure. */ 470 if ((cert = X509_new()) == NULL) 471 errx(1, "failed to malloc X509"); 472 473 if (vt->common_name_len != 0) { 474 if ((name = X509_NAME_new()) == NULL) 475 errx(1, "failed to malloc X509_NAME"); 476 if (X509_NAME_add_entry_by_NID(name, NID_commonName, 477 MBSTRING_ASC, (unsigned char *)vt->common_name, 478 vt->common_name_len, -1, 0) == 0) 479 errx(1, "failed to add name entry"); 480 if (X509_set_subject_name(cert, name) == 0) 481 errx(1, "failed to set subject name"); 482 X509_NAME_free(name); 483 } 484 485 if ((tls = tls_client()) == NULL) 486 errx(1, "failed to malloc tls_client"); 487 488 cert_add_alt_names(cert, vt); 489 490 match = 1; 491 492 if (tls_check_name(tls, cert, vt->name, &match) != vt->want_return) { 493 fprintf(stderr, "FAIL: test %i failed for check name '%s': " 494 "%s\n", test_no, vt->name, tls_error(tls)); 495 goto done; 496 } 497 if (match != vt->want_match) { 498 fprintf(stderr, "FAIL: test %i failed to match name '%s'\n", 499 test_no, vt->name); 500 goto done; 501 } 502 503 failed = 0; 504 505 done: 506 X509_free(cert); 507 tls_free(tls); 508 509 return (failed); 510 } 511 512 int 513 main(int argc, char **argv) 514 { 515 int failed = 0; 516 size_t i; 517 518 tls_init(); 519 520 for (i = 0; i < N_VERIFY_TESTS; i++) 521 failed += do_verify_test(i, &verify_tests[i]); 522 523 return (failed); 524 } 525