1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * COPYRIGHT (C) 2007 4 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN 5 * ALL RIGHTS RESERVED 6 * 7 * Permission is granted to use, copy, create derivative works 8 * and redistribute this software and such derivative works 9 * for any purpose, so long as the name of The University of 10 * Michigan is not used in any advertising or publicity 11 * pertaining to the use of distribution of this software 12 * without specific, written prior authorization. If the 13 * above copyright notice or any other identification of the 14 * University of Michigan is included in any copy of any 15 * portion of this software, then the disclaimer below must 16 * also be included. 17 * 18 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 19 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY 20 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF 21 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 22 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF 23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 24 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 25 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 26 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING 27 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN 28 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGES. 30 */ 31 32 #include "pkinit.h" 33 34 static void 35 free_list(char **list) 36 { 37 size_t i; 38 39 if (list == NULL) 40 return; 41 42 for (i = 0; list[i] != NULL; i++) 43 free(list[i]); 44 free(list); 45 } 46 47 static krb5_error_code 48 copy_list(char ***dst, char **src) 49 { 50 size_t i; 51 char **newlist; 52 53 if (dst == NULL) 54 return EINVAL; 55 *dst = NULL; 56 57 if (src == NULL) 58 return 0; 59 60 for (i = 0; src[i] != NULL; i++); 61 62 newlist = calloc(1, (i + 1) * sizeof(*newlist)); 63 if (newlist == NULL) 64 return ENOMEM; 65 66 for (i = 0; src[i] != NULL; i++) { 67 newlist[i] = strdup(src[i]); 68 if (newlist[i] == NULL) 69 goto cleanup; 70 } 71 newlist[i] = NULL; 72 *dst = newlist; 73 return 0; 74 cleanup: 75 free_list(newlist); 76 return ENOMEM; 77 } 78 79 char * 80 idtype2string(int idtype) 81 { 82 switch(idtype) { 83 case IDTYPE_FILE: return "FILE"; break; 84 case IDTYPE_DIR: return "DIR"; break; 85 case IDTYPE_PKCS11: return "PKCS11"; break; 86 case IDTYPE_PKCS12: return "PKCS12"; break; 87 case IDTYPE_ENVVAR: return "ENV"; break; 88 default: return "INVALID"; break; 89 } 90 } 91 92 char * 93 catype2string(int catype) 94 { 95 switch(catype) { 96 case CATYPE_ANCHORS: return "ANCHORS"; break; 97 case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; break; 98 case CATYPE_CRLS: return "CRLS"; break; 99 default: return "INVALID"; break; 100 } 101 } 102 103 krb5_error_code 104 pkinit_init_identity_opts(pkinit_identity_opts **idopts) 105 { 106 pkinit_identity_opts *opts = NULL; 107 108 *idopts = NULL; 109 opts = calloc(1, sizeof(pkinit_identity_opts)); 110 if (opts == NULL) 111 return ENOMEM; 112 113 opts->identity = NULL; 114 opts->anchors = NULL; 115 opts->intermediates = NULL; 116 opts->crls = NULL; 117 118 opts->cert_filename = NULL; 119 opts->key_filename = NULL; 120 #ifndef WITHOUT_PKCS11 121 opts->p11_module_name = NULL; 122 opts->slotid = PK_NOSLOT; 123 opts->token_label = NULL; 124 opts->cert_id_string = NULL; 125 opts->cert_label = NULL; 126 #endif 127 128 *idopts = opts; 129 130 return 0; 131 } 132 133 krb5_error_code 134 pkinit_dup_identity_opts(pkinit_identity_opts *src_opts, 135 pkinit_identity_opts **dest_opts) 136 { 137 pkinit_identity_opts *newopts; 138 krb5_error_code retval; 139 140 *dest_opts = NULL; 141 retval = pkinit_init_identity_opts(&newopts); 142 if (retval) 143 return retval; 144 145 retval = ENOMEM; 146 147 if (src_opts->identity != NULL) { 148 newopts->identity = strdup(src_opts->identity); 149 if (newopts->identity == NULL) 150 goto cleanup; 151 } 152 153 retval = copy_list(&newopts->anchors, src_opts->anchors); 154 if (retval) 155 goto cleanup; 156 157 retval = copy_list(&newopts->intermediates,src_opts->intermediates); 158 if (retval) 159 goto cleanup; 160 161 retval = copy_list(&newopts->crls, src_opts->crls); 162 if (retval) 163 goto cleanup; 164 165 if (src_opts->cert_filename != NULL) { 166 newopts->cert_filename = strdup(src_opts->cert_filename); 167 if (newopts->cert_filename == NULL) 168 goto cleanup; 169 } 170 171 if (src_opts->key_filename != NULL) { 172 newopts->key_filename = strdup(src_opts->key_filename); 173 if (newopts->key_filename == NULL) 174 goto cleanup; 175 } 176 177 #ifndef WITHOUT_PKCS11 178 if (src_opts->p11_module_name != NULL) { 179 newopts->p11_module_name = strdup(src_opts->p11_module_name); 180 if (newopts->p11_module_name == NULL) 181 goto cleanup; 182 } 183 184 newopts->slotid = src_opts->slotid; 185 186 if (src_opts->token_label != NULL) { 187 newopts->token_label = strdup(src_opts->token_label); 188 if (newopts->token_label == NULL) 189 goto cleanup; 190 } 191 192 if (src_opts->cert_id_string != NULL) { 193 newopts->cert_id_string = strdup(src_opts->cert_id_string); 194 if (newopts->cert_id_string == NULL) 195 goto cleanup; 196 } 197 198 if (src_opts->cert_label != NULL) { 199 newopts->cert_label = strdup(src_opts->cert_label); 200 if (newopts->cert_label == NULL) 201 goto cleanup; 202 } 203 #endif 204 205 206 *dest_opts = newopts; 207 return 0; 208 cleanup: 209 pkinit_fini_identity_opts(newopts); 210 return retval; 211 } 212 213 void 214 pkinit_fini_identity_opts(pkinit_identity_opts *idopts) 215 { 216 if (idopts == NULL) 217 return; 218 219 if (idopts->identity != NULL) 220 free(idopts->identity); 221 free_list(idopts->anchors); 222 free_list(idopts->intermediates); 223 free_list(idopts->crls); 224 free_list(idopts->identity_alt); 225 226 free(idopts->cert_filename); 227 free(idopts->key_filename); 228 #ifndef WITHOUT_PKCS11 229 free(idopts->p11_module_name); 230 free(idopts->token_label); 231 free(idopts->cert_id_string); 232 free(idopts->cert_label); 233 #endif 234 free(idopts); 235 } 236 237 #ifndef WITHOUT_PKCS11 238 static krb5_error_code 239 parse_pkcs11_options(krb5_context context, 240 pkinit_identity_opts *idopts, 241 const char *residual) 242 { 243 char *s, *cp, *vp, *save; 244 krb5_error_code retval = ENOMEM; 245 246 if (residual == NULL || residual[0] == '\0') 247 return 0; 248 249 /* Split string into attr=value substrings */ 250 s = strdup(residual); 251 if (s == NULL) 252 return retval; 253 254 for (cp = strtok_r(s, ":", &save); cp; cp = strtok_r(NULL, ":", &save)) { 255 vp = strchr(cp, '='); 256 257 /* If there is no "=", this is a pkcs11 module name */ 258 if (vp == NULL) { 259 free(idopts->p11_module_name); 260 idopts->p11_module_name = strdup(cp); 261 if (idopts->p11_module_name == NULL) 262 goto cleanup; 263 continue; 264 } 265 *vp++ = '\0'; 266 if (!strcmp(cp, "module_name")) { 267 free(idopts->p11_module_name); 268 idopts->p11_module_name = strdup(vp); 269 if (idopts->p11_module_name == NULL) 270 goto cleanup; 271 } else if (!strcmp(cp, "slotid")) { 272 long slotid = strtol(vp, NULL, 10); 273 if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) { 274 retval = EINVAL; 275 goto cleanup; 276 } 277 if ((long) (int) slotid != slotid) { 278 retval = EINVAL; 279 goto cleanup; 280 } 281 idopts->slotid = slotid; 282 } else if (!strcmp(cp, "token")) { 283 free(idopts->token_label); 284 idopts->token_label = strdup(vp); 285 if (idopts->token_label == NULL) 286 goto cleanup; 287 } else if (!strcmp(cp, "certid")) { 288 free(idopts->cert_id_string); 289 idopts->cert_id_string = strdup(vp); 290 if (idopts->cert_id_string == NULL) 291 goto cleanup; 292 } else if (!strcmp(cp, "certlabel")) { 293 free(idopts->cert_label); 294 idopts->cert_label = strdup(vp); 295 if (idopts->cert_label == NULL) 296 goto cleanup; 297 } 298 } 299 retval = 0; 300 cleanup: 301 free(s); 302 return retval; 303 } 304 #endif 305 306 static krb5_error_code 307 parse_fs_options(krb5_context context, 308 pkinit_identity_opts *idopts, 309 const char *residual) 310 { 311 char *certname, *keyname, *save; 312 char *copy = NULL, *cert_filename = NULL, *key_filename = NULL; 313 krb5_error_code retval = ENOMEM; 314 315 if (residual == NULL || residual[0] == '\0' || residual[0] == ',') 316 return EINVAL; 317 318 copy = strdup(residual); 319 if (copy == NULL) 320 goto cleanup; 321 322 certname = strtok_r(copy, ",", &save); 323 if (certname == NULL) 324 goto cleanup; 325 keyname = strtok_r(NULL, ",", &save); 326 327 cert_filename = strdup(certname); 328 if (cert_filename == NULL) 329 goto cleanup; 330 331 key_filename = strdup((keyname != NULL) ? keyname : certname); 332 if (key_filename == NULL) 333 goto cleanup; 334 335 free(idopts->cert_filename); 336 free(idopts->key_filename); 337 idopts->cert_filename = cert_filename; 338 idopts->key_filename = key_filename; 339 cert_filename = key_filename = NULL; 340 retval = 0; 341 342 cleanup: 343 free(copy); 344 free(cert_filename); 345 free(key_filename); 346 return retval; 347 } 348 349 static krb5_error_code 350 parse_pkcs12_options(krb5_context context, 351 pkinit_identity_opts *idopts, 352 const char *residual) 353 { 354 krb5_error_code retval = ENOMEM; 355 356 if (residual == NULL || residual[0] == '\0') 357 return 0; 358 359 free(idopts->cert_filename); 360 idopts->cert_filename = strdup(residual); 361 if (idopts->cert_filename == NULL) 362 goto cleanup; 363 364 free(idopts->key_filename); 365 idopts->key_filename = strdup(residual); 366 if (idopts->key_filename == NULL) 367 goto cleanup; 368 369 pkiDebug("%s: cert_filename '%s' key_filename '%s'\n", 370 __FUNCTION__, idopts->cert_filename, 371 idopts->key_filename); 372 retval = 0; 373 cleanup: 374 return retval; 375 } 376 377 static krb5_error_code 378 process_option_identity(krb5_context context, 379 pkinit_plg_crypto_context plg_cryptoctx, 380 pkinit_req_crypto_context req_cryptoctx, 381 pkinit_identity_opts *idopts, 382 pkinit_identity_crypto_context id_cryptoctx, 383 krb5_principal princ, const char *value) 384 { 385 const char *residual; 386 int idtype; 387 krb5_error_code retval = 0; 388 389 TRACE_PKINIT_IDENTITY_OPTION(context, value); 390 if (value == NULL) 391 return EINVAL; 392 393 residual = strchr(value, ':'); 394 if (residual != NULL) { 395 unsigned int typelen; 396 residual++; /* skip past colon */ 397 typelen = residual - value; 398 if (strncmp(value, "FILE:", typelen) == 0) { 399 idtype = IDTYPE_FILE; 400 #ifndef WITHOUT_PKCS11 401 } else if (strncmp(value, "PKCS11:", typelen) == 0) { 402 idtype = IDTYPE_PKCS11; 403 #endif 404 } else if (strncmp(value, "PKCS12:", typelen) == 0) { 405 idtype = IDTYPE_PKCS12; 406 } else if (strncmp(value, "DIR:", typelen) == 0) { 407 idtype = IDTYPE_DIR; 408 } else if (strncmp(value, "ENV:", typelen) == 0) { 409 idtype = IDTYPE_ENVVAR; 410 } else { 411 pkiDebug("%s: Unsupported type while processing '%s'\n", 412 __FUNCTION__, value); 413 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 414 _("Unsupported type while processing " 415 "'%s'\n"), value); 416 return KRB5_PREAUTH_FAILED; 417 } 418 } else { 419 idtype = IDTYPE_FILE; 420 residual = value; 421 } 422 423 idopts->idtype = idtype; 424 pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype)); 425 switch (idtype) { 426 case IDTYPE_ENVVAR: 427 return process_option_identity(context, plg_cryptoctx, req_cryptoctx, 428 idopts, id_cryptoctx, princ, 429 secure_getenv(residual)); 430 break; 431 case IDTYPE_FILE: 432 retval = parse_fs_options(context, idopts, residual); 433 break; 434 case IDTYPE_PKCS12: 435 retval = parse_pkcs12_options(context, idopts, residual); 436 break; 437 #ifndef WITHOUT_PKCS11 438 case IDTYPE_PKCS11: 439 retval = parse_pkcs11_options(context, idopts, residual); 440 break; 441 #endif 442 case IDTYPE_DIR: 443 free(idopts->cert_filename); 444 idopts->cert_filename = strdup(residual); 445 if (idopts->cert_filename == NULL) 446 retval = ENOMEM; 447 break; 448 default: 449 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 450 _("Internal error parsing " 451 "X509_user_identity\n")); 452 retval = EINVAL; 453 break; 454 } 455 if (retval) 456 return retval; 457 458 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, idopts, 459 id_cryptoctx, princ, TRUE); 460 if (retval) 461 return retval; 462 463 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, id_cryptoctx); 464 return 0; 465 } 466 467 static krb5_error_code 468 process_option_ca_crl(krb5_context context, 469 pkinit_plg_crypto_context plg_cryptoctx, 470 pkinit_req_crypto_context req_cryptoctx, 471 pkinit_identity_opts *idopts, 472 pkinit_identity_crypto_context id_cryptoctx, 473 const char *value, 474 int catype) 475 { 476 char *residual; 477 unsigned int typelen; 478 int idtype; 479 480 pkiDebug("%s: processing catype %s, value '%s'\n", 481 __FUNCTION__, catype2string(catype), value); 482 residual = strchr(value, ':'); 483 if (residual == NULL) { 484 pkiDebug("No type given for '%s'\n", value); 485 return EINVAL; 486 } 487 residual++; /* skip past colon */ 488 typelen = residual - value; 489 if (strncmp(value, "FILE:", typelen) == 0) { 490 idtype = IDTYPE_FILE; 491 } else if (strncmp(value, "DIR:", typelen) == 0) { 492 idtype = IDTYPE_DIR; 493 } else { 494 return ENOTSUP; 495 } 496 return crypto_load_cas_and_crls(context, 497 plg_cryptoctx, 498 req_cryptoctx, 499 idopts, id_cryptoctx, 500 idtype, catype, residual); 501 } 502 503 /* 504 * Load any identity information which doesn't require us to ask a controlling 505 * user any questions, and record the names of anything else which would 506 * require us to ask questions. 507 */ 508 krb5_error_code 509 pkinit_identity_initialize(krb5_context context, 510 pkinit_plg_crypto_context plg_cryptoctx, 511 pkinit_req_crypto_context req_cryptoctx, 512 pkinit_identity_opts *idopts, 513 pkinit_identity_crypto_context id_cryptoctx, 514 krb5_clpreauth_callbacks cb, 515 krb5_clpreauth_rock rock, 516 krb5_principal princ) 517 { 518 krb5_error_code retval = EINVAL; 519 size_t i; 520 521 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); 522 if (!(princ && 523 krb5_principal_compare_any_realm(context, princ, 524 krb5_anonymous_principal()))) { 525 if (idopts == NULL || id_cryptoctx == NULL) 526 goto errout; 527 528 /* 529 * If identity was specified, use that. (For the kdc, this 530 * is specified as pkinit_identity in the kdc.conf. For users, 531 * this is specified on the command line via X509_user_identity.) 532 * If a user did not specify identity on the command line, 533 * then we will try alternatives which may have been specified 534 * in the config file. 535 */ 536 if (idopts->identity != NULL) { 537 retval = process_option_identity(context, plg_cryptoctx, 538 req_cryptoctx, idopts, 539 id_cryptoctx, princ, 540 idopts->identity); 541 } else if (idopts->identity_alt != NULL) { 542 for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) { 543 retval = process_option_identity(context, plg_cryptoctx, 544 req_cryptoctx, idopts, 545 id_cryptoctx, princ, 546 idopts->identity_alt[i]); 547 } 548 } else { 549 retval = KRB5_PREAUTH_FAILED; 550 krb5_set_error_message(context, retval, 551 _("No user identity options specified")); 552 pkiDebug("%s: no user identity options specified\n", __FUNCTION__); 553 goto errout; 554 } 555 } else { 556 /* We're the anonymous principal. */ 557 retval = 0; 558 } 559 560 errout: 561 return retval; 562 } 563 564 /* 565 * Load identity information, including that which requires us to ask a 566 * controlling user any questions. If we have PIN/password values which 567 * correspond to a given identity, use that, otherwise, if one is available, 568 * we'll use the prompter callback. 569 */ 570 krb5_error_code 571 pkinit_identity_prompt(krb5_context context, 572 pkinit_plg_crypto_context plg_cryptoctx, 573 pkinit_req_crypto_context req_cryptoctx, 574 pkinit_identity_opts *idopts, 575 pkinit_identity_crypto_context id_cryptoctx, 576 krb5_clpreauth_callbacks cb, 577 krb5_clpreauth_rock rock, 578 int do_matching, 579 krb5_principal princ) 580 { 581 krb5_error_code retval = 0; 582 const char *signer_identity; 583 krb5_boolean valid; 584 size_t i; 585 586 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); 587 if (!(princ && 588 krb5_principal_compare_any_realm(context, princ, 589 krb5_anonymous_principal()))) { 590 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, 591 idopts, id_cryptoctx, princ, FALSE); 592 if (retval) 593 goto errout; 594 595 if (do_matching) { 596 /* 597 * Try to select exactly one certificate based on matching 598 * criteria. Typical used for clients. 599 */ 600 retval = pkinit_cert_matching(context, plg_cryptoctx, 601 req_cryptoctx, id_cryptoctx, princ); 602 if (retval) { 603 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 604 id_cryptoctx); 605 goto errout; 606 } 607 } else { 608 /* 609 * Tell crypto code to use the "default" identity. Typically used 610 * for KDCs. 611 */ 612 retval = crypto_cert_select_default(context, plg_cryptoctx, 613 req_cryptoctx, id_cryptoctx); 614 if (retval) { 615 crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 616 id_cryptoctx); 617 goto errout; 618 } 619 } 620 621 if (rock != NULL && cb != NULL && retval == 0) { 622 /* Save the signer identity if we're the client. */ 623 if (crypto_retrieve_signer_identity(context, id_cryptoctx, 624 &signer_identity) == 0) { 625 cb->set_cc_config(context, rock, "X509_user_identity", 626 signer_identity); 627 } 628 } 629 630 retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 631 id_cryptoctx); 632 if (retval) 633 goto errout; 634 } /* Not anonymous principal */ 635 636 /* Require at least one successful anchor if any are specified. */ 637 valid = FALSE; 638 for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) { 639 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, 640 idopts, id_cryptoctx, 641 idopts->anchors[i], CATYPE_ANCHORS); 642 if (!retval) 643 valid = TRUE; 644 } 645 if (retval && !valid) 646 goto errout; 647 krb5_clear_error_message(context); 648 retval = 0; 649 650 /* Require at least one successful intermediate if any are specified. */ 651 valid = FALSE; 652 for (i = 0; idopts->intermediates != NULL 653 && idopts->intermediates[i] != NULL; i++) { 654 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, 655 idopts, id_cryptoctx, 656 idopts->intermediates[i], 657 CATYPE_INTERMEDIATES); 658 if (!retval) 659 valid = TRUE; 660 } 661 if (retval && !valid) 662 goto errout; 663 krb5_clear_error_message(context); 664 retval = 0; 665 666 for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) { 667 retval = process_option_ca_crl(context, plg_cryptoctx, req_cryptoctx, 668 idopts, id_cryptoctx, idopts->crls[i], 669 CATYPE_CRLS); 670 if (retval) 671 goto errout; 672 } 673 674 errout: 675 return retval; 676 } 677 678 /* 679 * Create an entry in the passed-in list for the named identity, optionally 680 * with the specified token flag value and/or supplied password, replacing any 681 * existing entry with the same identity name. 682 */ 683 krb5_error_code 684 pkinit_set_deferred_id(pkinit_deferred_id **identities, 685 const char *identity, unsigned long ck_flags, 686 const char *password) 687 { 688 size_t i; 689 pkinit_deferred_id *out = NULL, *ids; 690 char *tmp; 691 692 /* Search for an entry that's already in the list. */ 693 ids = *identities; 694 for (i = 0; ids != NULL && ids[i] != NULL; i++) { 695 if (strcmp(ids[i]->identity, identity) == 0) { 696 /* Replace its password value, then we're done. */ 697 tmp = password ? strdup(password) : NULL; 698 if (password != NULL && tmp == NULL) 699 return ENOMEM; 700 ids[i]->ck_flags = ck_flags; 701 free(ids[i]->password); 702 ids[i]->password = tmp; 703 return 0; 704 } 705 } 706 707 /* Resize the list. */ 708 out = realloc(ids, sizeof(*ids) * (i + 2)); 709 if (out == NULL) 710 goto oom; 711 *identities = out; 712 713 /* Allocate the new final entry. */ 714 out[i] = malloc(sizeof(*(out[i]))); 715 if (out[i] == NULL) 716 goto oom; 717 718 /* Populate the new entry. */ 719 out[i]->magic = PKINIT_DEFERRED_ID_MAGIC; 720 out[i]->identity = strdup(identity); 721 if (out[i]->identity == NULL) 722 goto oom; 723 724 out[i]->ck_flags = ck_flags; 725 out[i]->password = password ? strdup(password) : NULL; 726 if (password != NULL && out[i]->password == NULL) 727 goto oom; 728 729 /* Terminate the list. */ 730 out[i + 1] = NULL; 731 return 0; 732 733 oom: 734 if (out != NULL && out[i] != NULL) { 735 free(out[i]->identity); 736 free(out[i]); 737 out[i] = NULL; 738 } 739 return ENOMEM; 740 } 741 742 /* 743 * Return a password which we've associated with the named identity, if we've 744 * stored one. Otherwise return NULL. 745 */ 746 const char * 747 pkinit_find_deferred_id(pkinit_deferred_id *identities, 748 const char *identity) 749 { 750 size_t i; 751 752 for (i = 0; identities != NULL && identities[i] != NULL; i++) { 753 if (strcmp(identities[i]->identity, identity) == 0) 754 return identities[i]->password; 755 } 756 return NULL; 757 } 758 759 /* 760 * Return the flags associated with the specified identity, or 0 if we don't 761 * have such an identity. 762 */ 763 unsigned long 764 pkinit_get_deferred_id_flags(pkinit_deferred_id *identities, 765 const char *identity) 766 { 767 size_t i; 768 769 for (i = 0; identities != NULL && identities[i] != NULL; i++) { 770 if (strcmp(identities[i]->identity, identity) == 0) 771 return identities[i]->ck_flags; 772 } 773 return 0; 774 } 775 776 /* 777 * Free a deferred_id list. 778 */ 779 void 780 pkinit_free_deferred_ids(pkinit_deferred_id *identities) 781 { 782 size_t i; 783 784 for (i = 0; identities != NULL && identities[i] != NULL; i++) { 785 free(identities[i]->identity); 786 free(identities[i]->password); 787 free(identities[i]); 788 } 789 free(identities); 790 } 791