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 <dlfcn.h> 40 #include <unistd.h> 41 #include <dirent.h> 42 43 #include <libintl.h> 44 45 #include "pkinit.h" 46 47 static void 48 free_list(char **list) 49 { 50 int i; 51 52 if (list == NULL) 53 return; 54 55 for (i = 0; list[i] != NULL; i++) 56 free(list[i]); 57 free(list); 58 } 59 60 static krb5_error_code 61 copy_list(char ***dst, char **src) 62 { 63 int i; 64 char **newlist; 65 66 if (dst == NULL) 67 return EINVAL; 68 *dst = NULL; 69 70 if (src == NULL) 71 return 0; 72 73 for (i = 0; src[i] != NULL; i++); 74 75 newlist = calloc(1, (i + 1) * sizeof(*newlist)); 76 if (newlist == NULL) 77 return ENOMEM; 78 79 for (i = 0; src[i] != NULL; i++) { 80 newlist[i] = strdup(src[i]); 81 if (newlist[i] == NULL) 82 goto cleanup; 83 } 84 newlist[i] = NULL; 85 *dst = newlist; 86 return 0; 87 cleanup: 88 free_list(newlist); 89 return ENOMEM; 90 } 91 92 char * 93 idtype2string(int idtype) 94 { 95 /* Solaris Kerberos: Removed "break"s (lint) */ 96 switch(idtype) { 97 case IDTYPE_FILE: return "FILE"; 98 case IDTYPE_DIR: return "DIR"; 99 case IDTYPE_PKCS11: return "PKCS11"; 100 case IDTYPE_PKCS12: return "PKCS12"; 101 case IDTYPE_ENVVAR: return "ENV"; 102 default: return "INVALID"; 103 } 104 } 105 106 char * 107 catype2string(int catype) 108 { 109 /* Solaris Kerberos: Removed "break"s (lint) */ 110 switch(catype) { 111 case CATYPE_ANCHORS: return "ANCHORS"; 112 case CATYPE_INTERMEDIATES: return "INTERMEDIATES"; 113 case CATYPE_CRLS: return "CRLS"; 114 default: return "INVALID"; 115 } 116 } 117 118 krb5_error_code 119 pkinit_init_identity_opts(pkinit_identity_opts **idopts) 120 { 121 pkinit_identity_opts *opts = NULL; 122 123 *idopts = NULL; 124 opts = (pkinit_identity_opts *) calloc(1, sizeof(pkinit_identity_opts)); 125 if (opts == NULL) 126 return ENOMEM; 127 128 opts->identity = NULL; 129 opts->anchors = NULL; 130 opts->intermediates = NULL; 131 opts->crls = NULL; 132 opts->ocsp = NULL; 133 opts->dn_mapping_file = NULL; 134 135 opts->cert_filename = NULL; 136 opts->key_filename = NULL; 137 #ifndef WITHOUT_PKCS11 138 opts->p11_module_name = NULL; 139 opts->slotid = PK_NOSLOT; 140 opts->token_label = NULL; 141 opts->cert_id_string = NULL; 142 opts->cert_label = NULL; 143 opts->PIN = NULL; 144 #endif 145 146 *idopts = opts; 147 148 return 0; 149 } 150 151 krb5_error_code 152 pkinit_dup_identity_opts(pkinit_identity_opts *src_opts, 153 pkinit_identity_opts **dest_opts) 154 { 155 pkinit_identity_opts *newopts; 156 krb5_error_code retval; 157 158 *dest_opts = NULL; 159 retval = pkinit_init_identity_opts(&newopts); 160 if (retval) 161 return retval; 162 163 retval = ENOMEM; 164 165 if (src_opts->identity != NULL) { 166 newopts->identity = strdup(src_opts->identity); 167 if (newopts->identity == NULL) 168 goto cleanup; 169 } 170 171 retval = copy_list(&newopts->anchors, src_opts->anchors); 172 if (retval) 173 goto cleanup; 174 175 retval = copy_list(&newopts->intermediates,src_opts->intermediates); 176 if (retval) 177 goto cleanup; 178 179 retval = copy_list(&newopts->crls, src_opts->crls); 180 if (retval) 181 goto cleanup; 182 183 if (src_opts->ocsp != NULL) { 184 newopts->ocsp = strdup(src_opts->ocsp); 185 if (newopts->ocsp == NULL) 186 goto cleanup; 187 } 188 189 if (src_opts->cert_filename != NULL) { 190 newopts->cert_filename = strdup(src_opts->cert_filename); 191 if (newopts->cert_filename == NULL) 192 goto cleanup; 193 } 194 195 if (src_opts->key_filename != NULL) { 196 newopts->key_filename = strdup(src_opts->key_filename); 197 if (newopts->key_filename == NULL) 198 goto cleanup; 199 } 200 201 #ifndef WITHOUT_PKCS11 202 if (src_opts->p11_module_name != NULL) { 203 newopts->p11_module_name = strdup(src_opts->p11_module_name); 204 if (newopts->p11_module_name == NULL) 205 goto cleanup; 206 } 207 208 newopts->slotid = src_opts->slotid; 209 210 if (src_opts->token_label != NULL) { 211 newopts->token_label = strdup(src_opts->token_label); 212 if (newopts->token_label == NULL) 213 goto cleanup; 214 } 215 216 if (src_opts->cert_id_string != NULL) { 217 newopts->cert_id_string = strdup(src_opts->cert_id_string); 218 if (newopts->cert_id_string == NULL) 219 goto cleanup; 220 } 221 222 if (src_opts->cert_label != NULL) { 223 newopts->cert_label = strdup(src_opts->cert_label); 224 if (newopts->cert_label == NULL) 225 goto cleanup; 226 } 227 if (src_opts->PIN != NULL) { 228 newopts->PIN = strdup(src_opts->PIN); 229 if (newopts->PIN == NULL) 230 goto cleanup; 231 } 232 #endif 233 234 235 *dest_opts = newopts; 236 return 0; 237 cleanup: 238 pkinit_fini_identity_opts(newopts); 239 return retval; 240 } 241 242 void 243 pkinit_fini_identity_opts(pkinit_identity_opts *idopts) 244 { 245 if (idopts == NULL) 246 return; 247 248 if (idopts->identity != NULL) 249 free(idopts->identity); 250 free_list(idopts->anchors); 251 free_list(idopts->intermediates); 252 free_list(idopts->crls); 253 free_list(idopts->identity_alt); 254 255 if (idopts->cert_filename != NULL) 256 free(idopts->cert_filename); 257 if (idopts->key_filename != NULL) 258 free(idopts->key_filename); 259 #ifndef WITHOUT_PKCS11 260 if (idopts->p11_module_name != NULL) 261 free(idopts->p11_module_name); 262 if (idopts->token_label != NULL) 263 free(idopts->token_label); 264 if (idopts->cert_id_string != NULL) 265 free(idopts->cert_id_string); 266 if (idopts->cert_label != NULL) 267 free(idopts->cert_label); 268 if (idopts->PIN != NULL) { 269 (void) memset(idopts->PIN, 0, strlen(idopts->PIN)); 270 free(idopts->PIN); 271 } 272 #endif 273 free(idopts); 274 } 275 276 #ifndef WITHOUT_PKCS11 277 /* ARGSUSED */ 278 static krb5_error_code 279 parse_pkcs11_options(krb5_context context, 280 pkinit_identity_opts *idopts, 281 const char *residual) 282 { 283 char *s, *cp, *vp; 284 krb5_error_code retval = ENOMEM; 285 286 if (residual == NULL || residual[0] == '\0') 287 return 0; 288 289 /* Split string into attr=value substrings */ 290 s = strdup(residual); 291 if (s == NULL) 292 return retval; 293 294 for ((cp = strtok(s, ":")); cp; (cp = strtok(NULL, ":"))) { 295 vp = strchr(cp, '='); 296 297 /* If there is no "=", this is a pkcs11 module name */ 298 if (vp == NULL) { 299 if (idopts->p11_module_name != NULL) 300 free(idopts->p11_module_name); 301 idopts->p11_module_name = strdup(cp); 302 if (idopts->p11_module_name == NULL) 303 goto cleanup; 304 continue; 305 } 306 *vp++ = '\0'; 307 if (!strcmp(cp, "module_name")) { 308 if (idopts->p11_module_name != NULL) 309 free(idopts->p11_module_name); 310 idopts->p11_module_name = strdup(vp); 311 if (idopts->p11_module_name == NULL) 312 goto cleanup; 313 } else if (!strcmp(cp, "slotid")) { 314 long slotid = strtol(vp, NULL, 10); 315 if ((slotid == LONG_MIN || slotid == LONG_MAX) && errno != 0) { 316 retval = EINVAL; 317 goto cleanup; 318 } 319 if ((long) (int) slotid != slotid) { 320 retval = EINVAL; 321 goto cleanup; 322 } 323 idopts->slotid = slotid; 324 } else if (!strcmp(cp, "token")) { 325 if (idopts->token_label != NULL) 326 free(idopts->token_label); 327 idopts->token_label = strdup(vp); 328 if (idopts->token_label == NULL) 329 goto cleanup; 330 } else if (!strcmp(cp, "certid")) { 331 if (idopts->cert_id_string != NULL) 332 free(idopts->cert_id_string); 333 idopts->cert_id_string = strdup(vp); 334 if (idopts->cert_id_string == NULL) 335 goto cleanup; 336 } else if (!strcmp(cp, "certlabel")) { 337 if (idopts->cert_label != NULL) 338 free(idopts->cert_label); 339 idopts->cert_label = strdup(vp); 340 if (idopts->cert_label == NULL) 341 goto cleanup; 342 } 343 } 344 retval = 0; 345 cleanup: 346 free(s); 347 return retval; 348 } 349 #endif 350 351 /* ARGSUSED */ 352 static krb5_error_code 353 parse_fs_options(krb5_context context, 354 pkinit_identity_opts *idopts, 355 const char *residual) 356 { 357 char *certname, *keyname; 358 krb5_error_code retval = ENOMEM; 359 360 if (residual == NULL || residual[0] == '\0') 361 return 0; 362 363 certname = strdup(residual); 364 if (certname == NULL) 365 goto cleanup; 366 367 certname = strtok(certname, ","); 368 keyname = strtok(NULL, ","); 369 370 idopts->cert_filename = strdup(certname); 371 if (idopts->cert_filename == NULL) 372 goto cleanup; 373 374 idopts->key_filename = strdup(keyname ? keyname : certname); 375 if (idopts->key_filename == NULL) 376 goto cleanup; 377 378 retval = 0; 379 cleanup: 380 if (certname != NULL) 381 free(certname); 382 return retval; 383 } 384 385 /* ARGSUSED */ 386 static krb5_error_code 387 parse_pkcs12_options(krb5_context context, 388 pkinit_identity_opts *idopts, 389 const char *residual) 390 { 391 krb5_error_code retval = ENOMEM; 392 393 if (residual == NULL || residual[0] == '\0') 394 return 0; 395 396 idopts->cert_filename = strdup(residual); 397 if (idopts->cert_filename == NULL) 398 goto cleanup; 399 400 idopts->key_filename = strdup(residual); 401 if (idopts->key_filename == NULL) 402 goto cleanup; 403 404 pkiDebug("%s: cert_filename '%s' key_filename '%s'\n", 405 __FUNCTION__, idopts->cert_filename, 406 idopts->key_filename); 407 retval = 0; 408 cleanup: 409 return retval; 410 } 411 412 static krb5_error_code 413 process_option_identity(krb5_context context, 414 pkinit_plg_crypto_context plg_cryptoctx, 415 pkinit_req_crypto_context req_cryptoctx, 416 pkinit_identity_opts *idopts, 417 pkinit_identity_crypto_context id_cryptoctx, 418 const char *value) 419 { 420 const char *residual; 421 int idtype; 422 krb5_error_code retval = 0; 423 424 pkiDebug("%s: processing value '%s'\n", 425 __FUNCTION__, value ? value : "NULL"); 426 if (value == NULL) 427 return EINVAL; 428 429 residual = strchr(value, ':'); 430 if (residual != NULL) { 431 unsigned int typelen; 432 residual++; /* skip past colon */ 433 typelen = residual - value; 434 if (strncmp(value, "FILE:", typelen) == 0) { 435 idtype = IDTYPE_FILE; 436 #ifndef WITHOUT_PKCS11 437 } else if (strncmp(value, "PKCS11:", typelen) == 0) { 438 idtype = IDTYPE_PKCS11; 439 #endif 440 } else if (strncmp(value, "PKCS12:", typelen) == 0) { 441 idtype = IDTYPE_PKCS12; 442 } else if (strncmp(value, "DIR:", typelen) == 0) { 443 idtype = IDTYPE_DIR; 444 } else if (strncmp(value, "ENV:", typelen) == 0) { 445 idtype = IDTYPE_ENVVAR; 446 } else { 447 pkiDebug("%s: Unsupported type while processing '%s'\n", 448 __FUNCTION__, value); 449 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 450 "Unsupported type while processing '%s'\n", 451 value); 452 return KRB5_PREAUTH_FAILED; 453 } 454 } else { 455 idtype = IDTYPE_FILE; 456 residual = value; 457 } 458 459 idopts->idtype = idtype; 460 pkiDebug("%s: idtype is %s\n", __FUNCTION__, idtype2string(idopts->idtype)); 461 switch (idtype) { 462 case IDTYPE_ENVVAR: { 463 /* Solaris Kerberos: Improved error messages */ 464 char *envvar = getenv(residual); 465 if (envvar == NULL) { 466 krb5_set_error_message(context, EINVAL, 467 gettext("failed to find environmental variable \'%s\'"), 468 residual); 469 return EINVAL; 470 } 471 return process_option_identity(context, plg_cryptoctx, 472 req_cryptoctx, idopts, id_cryptoctx, 473 envvar); 474 /* Solaris Kerberos: not reached */ 475 } 476 case IDTYPE_FILE: 477 retval = parse_fs_options(context, idopts, residual); 478 break; 479 case IDTYPE_PKCS12: 480 retval = parse_pkcs12_options(context, idopts, residual); 481 break; 482 #ifndef WITHOUT_PKCS11 483 case IDTYPE_PKCS11: 484 retval = parse_pkcs11_options(context, idopts, residual); 485 break; 486 #endif 487 case IDTYPE_DIR: 488 idopts->cert_filename = strdup(residual); 489 if (idopts->cert_filename == NULL) 490 retval = ENOMEM; 491 break; 492 default: 493 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 494 "Internal error parsing X509_user_identity\n"); 495 retval = EINVAL; 496 break; 497 } 498 return retval; 499 } 500 501 static krb5_error_code 502 process_option_ca_crl(krb5_context context, 503 pkinit_plg_crypto_context plg_cryptoctx, 504 pkinit_req_crypto_context req_cryptoctx, 505 pkinit_identity_opts *idopts, 506 pkinit_identity_crypto_context id_cryptoctx, 507 const char *value, 508 int catype) 509 { 510 char *residual; 511 unsigned int typelen; 512 int idtype; 513 514 pkiDebug("%s: processing catype %s, value '%s'\n", 515 __FUNCTION__, catype2string(catype), value); 516 residual = strchr(value, ':'); 517 if (residual == NULL) { 518 pkiDebug("No type given for '%s'\n", value); 519 return EINVAL; 520 } 521 residual++; /* skip past colon */ 522 typelen = residual - value; 523 if (strncmp(value, "FILE:", typelen) == 0) { 524 idtype = IDTYPE_FILE; 525 } else if (strncmp(value, "DIR:", typelen) == 0) { 526 idtype = IDTYPE_DIR; 527 } else { 528 return ENOTSUP; 529 } 530 return crypto_load_cas_and_crls(context, 531 plg_cryptoctx, 532 req_cryptoctx, 533 idopts, id_cryptoctx, 534 idtype, catype, residual); 535 } 536 537 static krb5_error_code 538 pkinit_identity_process_option(krb5_context context, 539 pkinit_plg_crypto_context plg_cryptoctx, 540 pkinit_req_crypto_context req_cryptoctx, 541 pkinit_identity_opts *idopts, 542 pkinit_identity_crypto_context id_cryptoctx, 543 int attr, 544 const char *value) 545 { 546 krb5_error_code retval = 0; 547 548 switch (attr) { 549 case PKINIT_ID_OPT_USER_IDENTITY: 550 retval = process_option_identity(context, plg_cryptoctx, 551 req_cryptoctx, idopts, 552 id_cryptoctx, value); 553 break; 554 case PKINIT_ID_OPT_ANCHOR_CAS: 555 retval = process_option_ca_crl(context, plg_cryptoctx, 556 req_cryptoctx, idopts, 557 id_cryptoctx, value, 558 CATYPE_ANCHORS); 559 break; 560 case PKINIT_ID_OPT_INTERMEDIATE_CAS: 561 retval = process_option_ca_crl(context, plg_cryptoctx, 562 req_cryptoctx, idopts, 563 id_cryptoctx, 564 value, CATYPE_INTERMEDIATES); 565 break; 566 case PKINIT_ID_OPT_CRLS: 567 retval = process_option_ca_crl(context, plg_cryptoctx, 568 req_cryptoctx, idopts, 569 id_cryptoctx, 570 value, CATYPE_CRLS); 571 break; 572 case PKINIT_ID_OPT_OCSP: 573 retval = ENOTSUP; 574 break; 575 default: 576 retval = EINVAL; 577 break; 578 } 579 return retval; 580 } 581 582 krb5_error_code 583 pkinit_identity_initialize(krb5_context context, 584 pkinit_plg_crypto_context plg_cryptoctx, 585 pkinit_req_crypto_context req_cryptoctx, 586 pkinit_identity_opts *idopts, 587 pkinit_identity_crypto_context id_cryptoctx, 588 int do_matching, 589 krb5_principal princ) 590 { 591 krb5_error_code retval = EINVAL; 592 int i; 593 594 pkiDebug("%s: %p %p %p\n", __FUNCTION__, context, idopts, id_cryptoctx); 595 if (idopts == NULL || id_cryptoctx == NULL) 596 goto errout; 597 598 /* 599 * If identity was specified, use that. (For the kdc, this 600 * is specified as pkinit_identity in the kdc.conf. For users, 601 * this is specified on the command line via X509_user_identity.) 602 * If a user did not specify identity on the command line, 603 * then we will try alternatives which may have been specified 604 * in the config file. 605 */ 606 if (idopts->identity != NULL) { 607 retval = pkinit_identity_process_option(context, plg_cryptoctx, 608 req_cryptoctx, idopts, 609 id_cryptoctx, 610 PKINIT_ID_OPT_USER_IDENTITY, 611 idopts->identity); 612 } else if (idopts->identity_alt != NULL) { 613 for (i = 0; retval != 0 && idopts->identity_alt[i] != NULL; i++) 614 retval = pkinit_identity_process_option(context, plg_cryptoctx, 615 req_cryptoctx, idopts, 616 id_cryptoctx, 617 PKINIT_ID_OPT_USER_IDENTITY, 618 idopts->identity_alt[i]); 619 } else { 620 pkiDebug("%s: no user identity options specified\n", __FUNCTION__); 621 goto errout; 622 } 623 if (retval) 624 goto errout; 625 626 retval = crypto_load_certs(context, plg_cryptoctx, req_cryptoctx, 627 idopts, id_cryptoctx, princ, do_matching); 628 if (retval) 629 goto errout; 630 631 if (do_matching) { 632 retval = pkinit_cert_matching(context, plg_cryptoctx, req_cryptoctx, 633 id_cryptoctx, princ, TRUE); 634 if (retval) { 635 pkiDebug("%s: No matching certificate found\n", __FUNCTION__); 636 (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 637 id_cryptoctx); 638 goto errout; 639 } 640 } else { 641 /* Tell crypto code to use the "default" */ 642 retval = crypto_cert_select_default(context, plg_cryptoctx, 643 req_cryptoctx, id_cryptoctx); 644 if (retval) { 645 pkiDebug("%s: Failed while selecting default certificate\n", 646 __FUNCTION__); 647 (void) crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 648 id_cryptoctx); 649 goto errout; 650 } 651 } 652 653 retval = crypto_free_cert_info(context, plg_cryptoctx, req_cryptoctx, 654 id_cryptoctx); 655 if (retval) 656 goto errout; 657 658 for (i = 0; idopts->anchors != NULL && idopts->anchors[i] != NULL; i++) { 659 retval = pkinit_identity_process_option(context, plg_cryptoctx, 660 req_cryptoctx, idopts, 661 id_cryptoctx, 662 PKINIT_ID_OPT_ANCHOR_CAS, 663 idopts->anchors[i]); 664 if (retval) 665 goto errout; 666 } 667 for (i = 0; idopts->intermediates != NULL 668 && idopts->intermediates[i] != NULL; i++) { 669 retval = pkinit_identity_process_option(context, plg_cryptoctx, 670 req_cryptoctx, idopts, 671 id_cryptoctx, 672 PKINIT_ID_OPT_INTERMEDIATE_CAS, 673 idopts->intermediates[i]); 674 if (retval) 675 goto errout; 676 } 677 for (i = 0; idopts->crls != NULL && idopts->crls[i] != NULL; i++) { 678 retval = pkinit_identity_process_option(context, plg_cryptoctx, 679 req_cryptoctx, idopts, 680 id_cryptoctx, 681 PKINIT_ID_OPT_CRLS, 682 idopts->crls[i]); 683 if (retval) 684 goto errout; 685 } 686 if (idopts->ocsp != NULL) { 687 retval = pkinit_identity_process_option(context, plg_cryptoctx, 688 req_cryptoctx, idopts, 689 id_cryptoctx, 690 PKINIT_ID_OPT_OCSP, 691 idopts->ocsp); 692 if (retval) 693 goto errout; 694 } 695 696 errout: 697 return retval; 698 } 699 700