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