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