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