1 /* 2 * COPYRIGHT (C) 2007 3 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN 4 * ALL RIGHTS RESERVED 5 * 6 * Permission is granted to use, copy, create derivative works 7 * and redistribute this software and such derivative works 8 * for any purpose, so long as the name of The University of 9 * Michigan is not used in any advertising or publicity 10 * pertaining to the use of distribution of this software 11 * without specific, written prior authorization. If the 12 * above copyright notice or any other identification of the 13 * University of Michigan is included in any copy of any 14 * portion of this software, then the disclaimer below must 15 * also be included. 16 * 17 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 18 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY 19 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF 20 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 21 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 23 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 24 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 25 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING 26 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN 27 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGES. 29 */ 30 31 #include <errno.h> 32 #include <string.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 #include <regex.h> 37 #include <krb5.h> 38 #include "pkinit.h" 39 40 typedef struct _pkinit_cert_info pkinit_cert_info; 41 42 typedef enum { 43 kw_undefined = 0, 44 kw_subject = 1, 45 kw_issuer = 2, 46 kw_san = 3, 47 kw_eku = 4, 48 kw_ku = 5 49 } keyword_type; 50 51 static char * 52 keyword2string(unsigned int kw) 53 { 54 /* Solaris Kerberos: removed "break"s (lint) */ 55 switch(kw) { 56 case kw_undefined: return "NONE"; 57 case kw_subject: return "SUBJECT"; 58 case kw_issuer: return "ISSUER"; 59 case kw_san: return "SAN"; 60 case kw_eku: return "EKU"; 61 case kw_ku: return "KU"; 62 default: return "INVALID"; 63 } 64 } 65 typedef enum { 66 relation_none = 0, 67 relation_and = 1, 68 relation_or = 2 69 } relation_type; 70 71 static char * 72 relation2string(unsigned int rel) 73 { 74 /* Solaris Kerberos: removed "break"s (lint) */ 75 switch(rel) { 76 case relation_none: return "NONE"; 77 case relation_and: return "AND"; 78 case relation_or: return "OR"; 79 default: return "INVALID"; 80 } 81 } 82 83 typedef enum { 84 kwvaltype_undefined = 0, 85 kwvaltype_regexp = 1, 86 kwvaltype_list = 2 87 } kw_value_type; 88 89 static char * 90 kwval2string(unsigned int kwval) 91 { 92 /* Solaris Kerberos: removed "break"s (lint) */ 93 switch(kwval) { 94 case kwvaltype_undefined: return "NONE"; 95 case kwvaltype_regexp: return "REGEXP"; 96 case kwvaltype_list: return "LIST"; 97 default: return "INVALID"; 98 } 99 } 100 101 struct keyword_desc { 102 const char *value; 103 size_t length; 104 keyword_type kwtype; 105 kw_value_type kwvaltype; 106 } matching_keywords[] = { 107 { "<KU>", 4, kw_ku, kwvaltype_list }, 108 { "<EKU>", 5, kw_eku, kwvaltype_list }, 109 { "<SAN>", 5, kw_san, kwvaltype_regexp }, 110 { "<ISSUER>", 8, kw_issuer, kwvaltype_regexp }, 111 { "<SUBJECT>", 9, kw_subject, kwvaltype_regexp }, 112 { NULL, 0, kw_undefined, kwvaltype_undefined}, 113 }; 114 115 struct ku_desc { 116 const char *value; 117 size_t length; 118 unsigned int bitval; 119 }; 120 121 struct ku_desc ku_keywords[] = { 122 { "digitalSignature", 16, PKINIT_KU_DIGITALSIGNATURE }, 123 { "keyEncipherment", 15, PKINIT_KU_KEYENCIPHERMENT }, 124 { NULL, 0, 0 }, 125 }; 126 127 struct ku_desc eku_keywords[] = { 128 { "pkinit", 6, PKINIT_EKU_PKINIT }, 129 { "msScLogin", 9, PKINIT_EKU_MSSCLOGIN }, 130 { "clientAuth", 10, PKINIT_EKU_CLIENTAUTH }, 131 { "emailProtection", 15, PKINIT_EKU_EMAILPROTECTION }, 132 { NULL, 0, 0 }, 133 }; 134 135 /* Rule component */ 136 typedef struct _rule_component { 137 struct _rule_component *next; 138 keyword_type kw_type; 139 kw_value_type kwval_type; 140 regex_t regexp; /* Compiled regular expression */ 141 char *regsrc; /* The regular expression source (for debugging) */ 142 unsigned int ku_bits; 143 unsigned int eku_bits; 144 } rule_component; 145 146 /* Set rule components */ 147 typedef struct _rule_set { 148 relation_type relation; 149 int num_crs; 150 rule_component *crs; 151 } rule_set; 152 153 /* ARGSUSED */ 154 static krb5_error_code 155 free_rule_component(krb5_context context, 156 rule_component *rc) 157 { 158 if (rc == NULL) 159 return 0; 160 161 if (rc->kwval_type == kwvaltype_regexp) { 162 if (rc->regsrc) 163 free(rc->regsrc); 164 regfree(&rc->regexp); 165 } 166 free(rc); 167 return 0; 168 } 169 170 static krb5_error_code 171 free_rule_set(krb5_context context, 172 rule_set *rs) 173 { 174 rule_component *rc, *trc; 175 176 if (rs == NULL) 177 return 0; 178 for (rc = rs->crs; rc != NULL;) { 179 trc = rc->next; 180 /* Solaris Kerberos */ 181 (void) free_rule_component(context, rc); 182 rc = trc; 183 } 184 free(rs); 185 return 0; 186 } 187 188 /* ARGSUSED */ 189 static krb5_error_code 190 parse_list_value(krb5_context context, 191 keyword_type type, 192 char *value, 193 rule_component *rc) 194 { 195 krb5_error_code retval; 196 char *comma; 197 struct ku_desc *ku = NULL; 198 int found; 199 size_t len; 200 unsigned int *bitptr; 201 202 203 if (value == NULL || value[0] == '\0') { 204 pkiDebug("%s: Missing or empty value for list keyword type %d\n", 205 __FUNCTION__, type); 206 retval = EINVAL; 207 goto out; 208 } 209 210 if (type == kw_eku) { 211 bitptr = &rc->eku_bits; 212 } else if (type == kw_ku) { 213 bitptr = &rc->ku_bits; 214 } else { 215 pkiDebug("%s: Unknown list keyword type %d\n", __FUNCTION__, type); 216 retval = EINVAL; 217 goto out; 218 } 219 220 do { 221 found = 0; 222 comma = strchr(value, ','); 223 if (comma != NULL) 224 len = comma - value; 225 else 226 len = strlen(value); 227 228 if (type == kw_eku) { 229 ku = eku_keywords; 230 } else if (type == kw_ku) { 231 ku = ku_keywords; 232 } 233 234 for (; ku->value != NULL; ku++) { 235 if (strncasecmp(value, ku->value, len) == 0) { 236 *bitptr |= ku->bitval; 237 found = 1; 238 pkiDebug("%s: Found value '%s', bitfield is now 0x%x\n", 239 __FUNCTION__, ku->value, *bitptr); 240 break; 241 } 242 } 243 if (found) { 244 value += ku->length; 245 if (*value == ',') 246 value += 1; 247 } else { 248 pkiDebug("%s: Urecognized value '%s'\n", __FUNCTION__, value); 249 retval = EINVAL; 250 goto out; 251 } 252 } while (found && *value != '\0'); 253 254 retval = 0; 255 out: 256 pkiDebug("%s: returning %d\n", __FUNCTION__, retval); 257 return retval; 258 } 259 260 static krb5_error_code 261 parse_rule_component(krb5_context context, 262 const char **rule, 263 int *remaining, 264 rule_component **ret_rule) 265 { 266 krb5_error_code retval; 267 rule_component *rc = NULL; 268 keyword_type kw_type; 269 kw_value_type kwval_type; 270 char err_buf[128]; 271 int ret; 272 struct keyword_desc *kw, *nextkw; 273 char *nk; 274 int found_next_kw = 0; 275 char *value = NULL; 276 size_t len; 277 278 for (kw = matching_keywords; kw->value != NULL; kw++) { 279 if (strncmp(*rule, kw->value, kw->length) == 0) { 280 kw_type = kw->kwtype; 281 kwval_type = kw->kwvaltype; 282 *rule += kw->length; 283 *remaining -= kw->length; 284 break; 285 } 286 } 287 if (kw->value == NULL) { 288 pkiDebug("%s: Missing or invalid keyword in rule '%s'\n", 289 __FUNCTION__, *rule); 290 retval = ENOENT; 291 goto out; 292 } 293 294 pkiDebug("%s: found keyword '%s'\n", __FUNCTION__, kw->value); 295 296 rc = calloc(1, sizeof(*rc)); 297 if (rc == NULL) { 298 retval = ENOMEM; 299 goto out; 300 } 301 rc->next = NULL; 302 rc->kw_type = kw_type; 303 rc->kwval_type = kwval_type; 304 305 /* 306 * Before procesing the value for this keyword, 307 * (compiling the regular expression or processing the list) 308 * we need to find the end of it. That means parsing for the 309 * beginning of the next keyword (or the end of the rule). 310 */ 311 nk = strchr(*rule, '<'); 312 while (nk != NULL) { 313 /* Possibly another keyword, check it out */ 314 for (nextkw = matching_keywords; nextkw->value != NULL; nextkw++) { 315 if (strncmp(nk, nextkw->value, nextkw->length) == 0) { 316 /* Found a keyword, nk points to the beginning */ 317 found_next_kw = 1; 318 break; /* Need to break out of the while! */ 319 } 320 } 321 if (!found_next_kw) 322 nk = strchr(nk+1, '<'); /* keep looking */ 323 else 324 break; 325 } 326 327 if (nk != NULL && found_next_kw) 328 len = (nk - *rule); 329 else 330 len = (*remaining); 331 332 if (len == 0) { 333 pkiDebug("%s: Missing value for keyword '%s'\n", 334 __FUNCTION__, kw->value); 335 retval = EINVAL; 336 goto out; 337 } 338 339 value = calloc(1, len+1); 340 if (value == NULL) { 341 retval = ENOMEM; 342 goto out; 343 } 344 (void) memcpy(value, *rule, len); 345 *remaining -= len; 346 *rule += len; 347 pkiDebug("%s: found value '%s'\n", __FUNCTION__, value); 348 349 if (kw->kwvaltype == kwvaltype_regexp) { 350 ret = regcomp(&rc->regexp, value, REG_EXTENDED); 351 if (ret) { 352 (void) regerror(ret, &rc->regexp, err_buf, sizeof(err_buf)); 353 pkiDebug("%s: Error compiling reg-exp '%s': %s\n", 354 __FUNCTION__, value, err_buf); 355 retval = ret; 356 goto out; 357 } 358 rc->regsrc = strdup(value); 359 if (rc->regsrc == NULL) { 360 retval = ENOMEM; 361 goto out; 362 } 363 } else if (kw->kwvaltype == kwvaltype_list) { 364 retval = parse_list_value(context, rc->kw_type, value, rc); 365 if (retval) { 366 pkiDebug("%s: Error %d, parsing list values for keyword %s\n", 367 __FUNCTION__, retval, kw->value); 368 goto out; 369 } 370 } 371 372 *ret_rule = rc; 373 retval = 0; 374 out: 375 if (value != NULL) 376 free(value); 377 if (retval && rc != NULL) 378 (void) free_rule_component(context, rc); 379 pkiDebug("%s: returning %d\n", __FUNCTION__, retval); 380 return retval; 381 } 382 383 /* ARGSUSED */ 384 static krb5_error_code 385 parse_rule_set(krb5_context context, 386 const char *rule_in, 387 rule_set **out_rs) 388 { 389 const char *rule; 390 /* Solaris Kerberos */ 391 int remaining; 392 krb5_error_code ret, retval; 393 rule_component *rc = NULL, *trc; 394 rule_set *rs; 395 396 397 if (rule_in == NULL) 398 return EINVAL; 399 rule = rule_in; 400 /* Solaris Kerberos */ 401 remaining = strlen(rule); 402 403 rs = calloc(1, sizeof(*rs)); 404 if (rs == NULL) { 405 retval = ENOMEM; 406 goto cleanup; 407 } 408 409 rs->relation = relation_none; 410 if (remaining > 1) { 411 if (rule[0] == '&' && rule[1] == '&') { 412 rs->relation = relation_and; 413 rule += 2; 414 remaining -= 2; 415 } else if (rule_in[0] == '|' && rule_in[1] == '|') { 416 rs->relation = relation_or; 417 rule +=2; 418 remaining -= 2; 419 } 420 } 421 rs->num_crs = 0; 422 while (remaining > 0) { 423 if (rs->relation == relation_none && rs->num_crs > 1) { 424 pkiDebug("%s: Assuming AND relation for multiple components in rule '%s'\n", 425 __FUNCTION__, rule_in); 426 rs->relation = relation_and; 427 } 428 ret = parse_rule_component(context, &rule, &remaining, &rc); 429 if (ret) { 430 retval = ret; 431 goto cleanup; 432 } 433 pkiDebug("%s: After parse_rule_component, remaining %d, rule '%s'\n", 434 __FUNCTION__, remaining, rule); 435 rs->num_crs++; 436 437 /* 438 * Chain the new component on the end (order matters since 439 * we can short-circuit an OR or an AND relation if an 440 * earlier check passes 441 */ 442 for (trc = rs->crs; trc != NULL && trc->next != NULL; trc = trc->next); 443 if (trc == NULL) 444 rs->crs = rc; 445 else { 446 trc->next = rc; 447 } 448 } 449 450 *out_rs = rs; 451 452 retval = 0; 453 cleanup: 454 if (retval && rs != NULL) { 455 (void) free_rule_set(context, rs); 456 } 457 pkiDebug("%s: returning %d\n", __FUNCTION__, retval); 458 return retval; 459 } 460 461 /* ARGSUSED */ 462 static int 463 regexp_match(krb5_context context, rule_component *rc, char *value) 464 { 465 int code; 466 467 pkiDebug("%s: checking %s rule '%s' with value '%s'\n", 468 __FUNCTION__, keyword2string(rc->kw_type), rc->regsrc, value); 469 470 code = regexec(&rc->regexp, value, 0, NULL, 0); 471 472 pkiDebug("%s: the result is%s a match\n", __FUNCTION__, 473 code == REG_NOMATCH ? " NOT" : ""); 474 475 return (code == 0 ? 1: 0); 476 } 477 478 static int 479 component_match(krb5_context context, 480 rule_component *rc, 481 pkinit_cert_matching_data *md) 482 { 483 int match = 0; 484 int i; 485 krb5_principal p; 486 char *princ_string; 487 488 switch (rc->kwval_type) { 489 case kwvaltype_regexp: 490 switch (rc->kw_type) { 491 case kw_subject: 492 match = regexp_match(context, rc, md->subject_dn); 493 break; 494 case kw_issuer: 495 match = regexp_match(context, rc, md->issuer_dn); 496 break; 497 case kw_san: 498 if (md->sans == NULL) 499 break; 500 for (i = 0, p = md->sans[i]; p != NULL; p = md->sans[++i]) { 501 krb5_unparse_name(context, p, &princ_string); 502 match = regexp_match(context, rc, princ_string); 503 krb5_free_unparsed_name(context, princ_string); 504 if (match) 505 break; 506 } 507 break; 508 default: 509 pkiDebug("%s: keyword %s, keyword value %s mismatch\n", 510 __FUNCTION__, keyword2string(rc->kw_type), 511 kwval2string(kwvaltype_regexp)); 512 break; 513 } 514 break; 515 case kwvaltype_list: 516 switch(rc->kw_type) { 517 case kw_eku: 518 pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n", 519 __FUNCTION__, keyword2string(rc->kw_type), 520 rc->eku_bits, md->eku_bits); 521 if ((rc->eku_bits & md->eku_bits) == rc->eku_bits) 522 match = 1; 523 break; 524 case kw_ku: 525 pkiDebug("%s: checking %s: rule 0x%08x, cert 0x%08x\n", 526 __FUNCTION__, keyword2string(rc->kw_type), 527 rc->ku_bits, md->ku_bits); 528 if ((rc->ku_bits & md->ku_bits) == rc->ku_bits) 529 match = 1; 530 break; 531 default: 532 pkiDebug("%s: keyword %s, keyword value %s mismatch\n", 533 __FUNCTION__, keyword2string(rc->kw_type), 534 kwval2string(kwvaltype_regexp)); 535 break; 536 } 537 break; 538 default: 539 pkiDebug("%s: unknown keyword value type %d\n", 540 __FUNCTION__, rc->kwval_type); 541 break; 542 } 543 pkiDebug("%s: returning match = %d\n", __FUNCTION__, match); 544 return match; 545 } 546 /* 547 * Returns match_found == 1 only if exactly one certificate matches 548 * the given rule 549 */ 550 /* ARGSUSED */ 551 static krb5_error_code 552 check_all_certs(krb5_context context, 553 pkinit_plg_crypto_context plg_cryptoctx, 554 pkinit_req_crypto_context req_cryptoctx, 555 pkinit_identity_crypto_context id_cryptoctx, 556 krb5_principal princ, 557 rule_set *rs, /* rule to check */ 558 pkinit_cert_matching_data **matchdata, 559 int *match_found, 560 pkinit_cert_matching_data **matching_cert) 561 { 562 krb5_error_code retval; 563 pkinit_cert_matching_data *md; 564 int i; 565 int comp_match = 0; 566 int total_cert_matches = 0; 567 rule_component *rc; 568 int certs_checked = 0; 569 pkinit_cert_matching_data *save_match = NULL; 570 571 if (match_found == NULL || matching_cert == NULL) 572 return EINVAL; 573 574 *matching_cert = NULL; 575 *match_found = 0; 576 577 pkiDebug("%s: matching rule relation is %s with %d components\n", 578 __FUNCTION__, relation2string(rs->relation), rs->num_crs); 579 580 /* 581 * Loop through all the certs available and count 582 * how many match the rule 583 */ 584 for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) { 585 pkiDebug("%s: subject: '%s'\n", __FUNCTION__, md->subject_dn); 586 #if 0 587 pkiDebug("%s: issuer: '%s'\n", __FUNCTION__, md->subject_dn); 588 for (j = 0, p = md->sans[j]; p != NULL; p = md->sans[++j]) { 589 char *san_string; 590 krb5_unparse_name(context, p, &san_string); 591 pkiDebug("%s: san: '%s'\n", __FUNCTION__, san_string); 592 krb5_free_unparsed_name(context, san_string); 593 } 594 #endif 595 certs_checked++; 596 for (rc = rs->crs; rc != NULL; rc = rc->next) { 597 comp_match = component_match(context, rc, md); 598 if (comp_match) { 599 pkiDebug("%s: match for keyword type %s\n", 600 __FUNCTION__, keyword2string(rc->kw_type)); 601 } 602 if (comp_match && rs->relation == relation_or) { 603 pkiDebug("%s: cert matches rule (OR relation)\n", 604 __FUNCTION__); 605 total_cert_matches++; 606 save_match = md; 607 goto nextcert; 608 } 609 if (!comp_match && rs->relation == relation_and) { 610 pkiDebug("%s: cert does not match rule (AND relation)\n", 611 __FUNCTION__); 612 goto nextcert; 613 } 614 } 615 if (rc == NULL && comp_match) { 616 pkiDebug("%s: cert matches rule (AND relation)\n", __FUNCTION__); 617 total_cert_matches++; 618 save_match = md; 619 } 620 nextcert: 621 continue; 622 } 623 pkiDebug("%s: After checking %d certs, we found %d matches\n", 624 __FUNCTION__, certs_checked, total_cert_matches); 625 if (total_cert_matches == 1) { 626 *match_found = 1; 627 *matching_cert = save_match; 628 } 629 630 retval = 0; 631 632 pkiDebug("%s: returning %d, match_found %d\n", 633 __FUNCTION__, retval, *match_found); 634 return retval; 635 } 636 637 static krb5_error_code 638 free_all_cert_matching_data(krb5_context context, 639 pkinit_cert_matching_data **matchdata) 640 { 641 krb5_error_code retval; 642 pkinit_cert_matching_data *md; 643 int i; 644 645 if (matchdata == NULL) 646 return EINVAL; 647 648 for (i = 0, md = matchdata[i]; md != NULL; md = matchdata[++i]) { 649 pkinit_cert_handle ch = md->ch; 650 retval = crypto_cert_free_matching_data(context, md); 651 if (retval) { 652 pkiDebug("%s: crypto_cert_free_matching_data error %d, %s\n", 653 __FUNCTION__, retval, error_message(retval)); 654 goto cleanup; 655 } 656 retval = crypto_cert_release(context, ch); 657 if (retval) { 658 pkiDebug("%s: crypto_cert_release error %d, %s\n", 659 __FUNCTION__, retval, error_message(retval)); 660 goto cleanup; 661 } 662 } 663 free(matchdata); 664 retval = 0; 665 666 cleanup: 667 return retval; 668 } 669 670 static krb5_error_code 671 obtain_all_cert_matching_data(krb5_context context, 672 pkinit_plg_crypto_context plg_cryptoctx, 673 pkinit_req_crypto_context req_cryptoctx, 674 pkinit_identity_crypto_context id_cryptoctx, 675 pkinit_cert_matching_data ***all_matching_data) 676 { 677 krb5_error_code retval; 678 int i, cert_count; 679 pkinit_cert_iter_handle ih = NULL; 680 pkinit_cert_handle ch; 681 pkinit_cert_matching_data **matchdata = NULL; 682 683 retval = crypto_cert_get_count(context, plg_cryptoctx, req_cryptoctx, 684 id_cryptoctx, &cert_count); 685 if (retval) { 686 pkiDebug("%s: crypto_cert_get_count error %d, %s\n", 687 __FUNCTION__, retval, error_message(retval)); 688 goto cleanup; 689 } 690 691 pkiDebug("%s: crypto_cert_get_count says there are %d certs\n", 692 __FUNCTION__, cert_count); 693 694 matchdata = calloc((size_t)cert_count + 1, sizeof(*matchdata)); 695 if (matchdata == NULL) 696 return ENOMEM; 697 698 retval = crypto_cert_iteration_begin(context, plg_cryptoctx, req_cryptoctx, 699 id_cryptoctx, &ih); 700 if (retval) { 701 pkiDebug("%s: crypto_cert_iteration_begin returned %d, %s\n", 702 __FUNCTION__, retval, error_message(retval)); 703 goto cleanup; 704 } 705 706 for (i = 0; i < cert_count; i++) { 707 retval = crypto_cert_iteration_next(context, ih, &ch); 708 if (retval) { 709 if (retval == PKINIT_ITER_NO_MORE) 710 pkiDebug("%s: We thought there were %d certs, but " 711 "crypto_cert_iteration_next stopped after %d?\n", 712 __FUNCTION__, cert_count, i); 713 else 714 pkiDebug("%s: crypto_cert_iteration_next error %d, %s\n", 715 __FUNCTION__, retval, error_message(retval)); 716 goto cleanup; 717 } 718 719 retval = crypto_cert_get_matching_data(context, ch, &matchdata[i]); 720 if (retval) { 721 pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n", 722 __FUNCTION__, retval, error_message(retval)); 723 goto cleanup; 724 } 725 726 } 727 728 *all_matching_data = matchdata; 729 retval = 0; 730 cleanup: 731 if (ih != NULL) 732 /* Solaris Kerberos */ 733 (void) crypto_cert_iteration_end(context, ih); 734 if (retval) { 735 if (matchdata != NULL) 736 (void) free_all_cert_matching_data(context, matchdata); 737 } 738 pkiDebug("%s: returning %d, certinfo %p\n", 739 __FUNCTION__, retval, *all_matching_data); 740 return retval; 741 } 742 743 krb5_error_code 744 pkinit_cert_matching(krb5_context context, 745 pkinit_plg_crypto_context plg_cryptoctx, 746 pkinit_req_crypto_context req_cryptoctx, 747 pkinit_identity_crypto_context id_cryptoctx, 748 krb5_principal princ) 749 { 750 751 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 752 int x; 753 char **rules = NULL; 754 rule_set *rs = NULL; 755 int match_found = 0; 756 pkinit_cert_matching_data **matchdata = NULL; 757 pkinit_cert_matching_data *the_matching_cert = NULL; 758 759 /* If no matching rules, select the default cert and we're done */ 760 (void) pkinit_libdefault_strings(context, krb5_princ_realm(context, princ), 761 "pkinit_cert_match", &rules); 762 if (rules == NULL) { 763 pkiDebug("%s: no matching rules found in config file\n", __FUNCTION__); 764 retval = crypto_cert_select_default(context, plg_cryptoctx, 765 req_cryptoctx, id_cryptoctx); 766 goto cleanup; 767 } 768 769 /* parse each rule line one at a time and check all the certs against it */ 770 for (x = 0; rules[x] != NULL; x++) { 771 pkiDebug("%s: Processing rule '%s'\n", __FUNCTION__, rules[x]); 772 773 /* Free rules from previous time through... */ 774 if (rs != NULL) { 775 (void) free_rule_set(context, rs); 776 rs = NULL; 777 } 778 retval = parse_rule_set(context, rules[x], &rs); 779 if (retval) { 780 if (retval == EINVAL) { 781 pkiDebug("%s: Ignoring invalid rule pkinit_cert_match = '%s'\n", 782 __FUNCTION__, rules[x]); 783 continue; 784 } 785 goto cleanup; 786 } 787 788 /* 789 * Optimize so that we do not get cert info unless we have 790 * valid rules to check. Once obtained, keep it around 791 * until we are done. 792 */ 793 if (matchdata == NULL) { 794 retval = obtain_all_cert_matching_data(context, plg_cryptoctx, 795 req_cryptoctx, id_cryptoctx, 796 &matchdata); 797 if (retval || matchdata == NULL) { 798 pkiDebug("%s: Error %d obtaining certificate information\n", 799 __FUNCTION__, retval); 800 retval = ENOENT; 801 goto cleanup; 802 } 803 } 804 805 retval = check_all_certs(context, plg_cryptoctx, req_cryptoctx, 806 id_cryptoctx, princ, rs, matchdata, 807 &match_found, &the_matching_cert); 808 if (retval) { 809 pkiDebug("%s: Error %d, checking certs against rule '%s'\n", 810 __FUNCTION__, retval, rules[x]); 811 goto cleanup; 812 } 813 if (match_found) { 814 pkiDebug("%s: We have an exact match with rule '%s'\n", 815 __FUNCTION__, rules[x]); 816 break; 817 } 818 } 819 820 if (match_found && the_matching_cert != NULL) { 821 pkiDebug("%s: Selecting the matching cert!\n", __FUNCTION__); 822 retval = crypto_cert_select(context, the_matching_cert); 823 if (retval) { 824 pkiDebug("%s: crypto_cert_select error %d, %s\n", 825 __FUNCTION__, retval, error_message(retval)); 826 goto cleanup; 827 } 828 } else { 829 retval = ENOENT; /* XXX */ 830 goto cleanup; 831 } 832 833 retval = 0; 834 cleanup: 835 if (rules != NULL) 836 profile_free_list(rules); 837 if (rs != NULL) 838 /* Solaris Kerberos */ 839 (void) free_rule_set(context, rs); 840 if (matchdata != NULL) 841 (void) free_all_cert_matching_data(context, matchdata); 842 return retval; 843 } 844