1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * COPYRIGHT (C) 2006,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 "k5-int.h" 33 #include "pkinit.h" 34 #include "k5-json.h" 35 36 #include <sys/stat.h> 37 38 /** 39 * Return true if we should use ContentInfo rather than SignedData. This 40 * happens if we are talking to what might be an old (pre-6112) MIT KDC and 41 * we're using anonymous. 42 */ 43 static int 44 use_content_info(krb5_context context, pkinit_req_context req, 45 krb5_principal client) 46 { 47 if (req->rfc6112_kdc) 48 return 0; 49 if (krb5_principal_compare_any_realm(context, client, 50 krb5_anonymous_principal())) 51 return 1; 52 return 0; 53 } 54 55 static krb5_error_code 56 pkinit_as_req_create(krb5_context context, pkinit_context plgctx, 57 pkinit_req_context reqctx, krb5_timestamp ctsec, 58 krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum, 59 const krb5_pachecksum2 *cksum2, krb5_principal client, 60 krb5_principal server, krb5_data **as_req); 61 62 static krb5_error_code 63 pkinit_as_rep_parse(krb5_context context, pkinit_context plgctx, 64 pkinit_req_context reqctx, krb5_preauthtype pa_type, 65 krb5_kdc_req *request, const krb5_data *as_rep, 66 krb5_keyblock *key_block, krb5_enctype etype, krb5_data *); 67 68 static void pkinit_client_plugin_fini(krb5_context context, 69 krb5_clpreauth_moddata moddata); 70 71 static krb5_error_code 72 pa_pkinit_gen_req(krb5_context context, 73 pkinit_context plgctx, 74 pkinit_req_context reqctx, 75 krb5_clpreauth_callbacks cb, 76 krb5_clpreauth_rock rock, 77 krb5_kdc_req * request, 78 krb5_preauthtype pa_type, 79 krb5_pa_data *** out_padata, 80 krb5_prompter_fct prompter, 81 void *prompter_data, 82 krb5_get_init_creds_opt *gic_opt) 83 { 84 85 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 86 krb5_data *out_data = NULL; 87 krb5_timestamp ctsec = 0; 88 krb5_int32 cusec = 0; 89 krb5_ui_4 nonce = 0; 90 krb5_data cksum = empty_data(); 91 krb5_pachecksum2 *cksum2 = NULL; 92 krb5_data *der_req = NULL; 93 krb5_pa_data **return_pa_data = NULL; 94 95 memset(&cksum, 0, sizeof(cksum)); 96 reqctx->pa_type = pa_type; 97 98 pkiDebug("kdc_options = 0x%x till = %d\n", 99 request->kdc_options, request->till); 100 /* If we don't have a client, we're done */ 101 if (request->client == NULL) { 102 pkiDebug("No request->client; aborting PKINIT\n"); 103 return KRB5KDC_ERR_PREAUTH_FAILED; 104 } 105 106 retval = pkinit_get_kdc_cert(context, plgctx->cryptoctx, reqctx->cryptoctx, 107 reqctx->idctx, request->server); 108 if (retval) { 109 pkiDebug("pkinit_get_kdc_cert returned %d\n", retval); 110 goto cleanup; 111 } 112 113 /* checksum of the encoded KDC-REQ-BODY */ 114 retval = k5int_encode_krb5_kdc_req_body(request, &der_req); 115 if (retval) { 116 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); 117 goto cleanup; 118 } 119 120 retval = crypto_generate_checksums(context, der_req, &cksum, &cksum2); 121 if (retval) 122 goto cleanup; 123 TRACE_PKINIT_CLIENT_REQ_CHECKSUMS(context, &cksum, cksum2); 124 125 retval = cb->get_preauth_time(context, rock, TRUE, &ctsec, &cusec); 126 if (retval) 127 goto cleanup; 128 129 /* XXX PKINIT RFC says that nonce in PKAuthenticator doesn't have be the 130 * same as in the AS_REQ. However, if we pick a different nonce, then we 131 * need to remember that info when AS_REP is returned. I'm choosing to 132 * reuse the AS_REQ nonce. 133 */ 134 nonce = request->nonce; 135 136 retval = pkinit_as_req_create(context, plgctx, reqctx, ctsec, cusec, 137 nonce, &cksum, cksum2, request->client, 138 request->server, &out_data); 139 if (retval) { 140 pkiDebug("error %d on pkinit_as_req_create; aborting PKINIT\n", 141 (int) retval); 142 goto cleanup; 143 } 144 145 return_pa_data = k5calloc(2, sizeof(*return_pa_data), &retval); 146 if (return_pa_data == NULL) 147 goto cleanup; 148 149 return_pa_data[0] = k5alloc(sizeof(*return_pa_data[0]), &retval); 150 if (return_pa_data[0] == NULL) 151 goto cleanup; 152 153 return_pa_data[0]->magic = KV5M_PA_DATA; 154 155 return_pa_data[0]->pa_type = pa_type; 156 return_pa_data[0]->length = out_data->length; 157 return_pa_data[0]->contents = (krb5_octet *) out_data->data; 158 *out_data = empty_data(); 159 160 *out_padata = return_pa_data; 161 return_pa_data = NULL; 162 cb->disable_fallback(context, rock); 163 164 cleanup: 165 krb5_free_data(context, der_req); 166 krb5_free_data_contents(context, &cksum); 167 free_pachecksum2(context, &cksum2); 168 krb5_free_data(context, out_data); 169 krb5_free_pa_data(context, return_pa_data); 170 return retval; 171 } 172 173 static krb5_error_code 174 pkinit_as_req_create(krb5_context context, pkinit_context plgctx, 175 pkinit_req_context reqctx, krb5_timestamp ctsec, 176 krb5_int32 cusec, krb5_ui_4 nonce, const krb5_data *cksum, 177 const krb5_pachecksum2 *cksum2, krb5_principal client, 178 krb5_principal server, krb5_data **as_req) 179 { 180 krb5_error_code retval = ENOMEM; 181 krb5_data spki = empty_data(), *coded_auth_pack = NULL; 182 krb5_auth_pack auth_pack; 183 krb5_pa_pk_as_req *req = NULL; 184 krb5_algorithm_identifier **cmstypes = NULL; 185 186 pkiDebug("pkinit_as_req_create pa_type = %d\n", reqctx->pa_type); 187 188 /* Create the authpack */ 189 memset(&auth_pack, 0, sizeof(auth_pack)); 190 auth_pack.pkAuthenticator.ctime = ctsec; 191 auth_pack.pkAuthenticator.cusec = cusec; 192 auth_pack.pkAuthenticator.nonce = nonce; 193 auth_pack.pkAuthenticator.paChecksum = *cksum; 194 if (!reqctx->opts->disable_freshness) 195 auth_pack.pkAuthenticator.freshnessToken = reqctx->freshness_token; 196 auth_pack.pkAuthenticator.paChecksum2 = (krb5_pachecksum2 *)cksum2; 197 auth_pack.clientDHNonce.length = 0; 198 auth_pack.supportedKDFs = (krb5_data **)supported_kdf_alg_ids; 199 200 /* add List of CMS algorithms */ 201 retval = create_krb5_supportedCMSTypes(context, plgctx->cryptoctx, 202 reqctx->cryptoctx, 203 reqctx->idctx, &cmstypes); 204 auth_pack.supportedCMSTypes = cmstypes; 205 if (retval) 206 goto cleanup; 207 208 TRACE_PKINIT_CLIENT_REQ_DH(context); 209 210 /* create client-side DH keys */ 211 retval = client_create_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, 212 reqctx->idctx, reqctx->opts->dh_size, &spki); 213 auth_pack.clientPublicValue = spki; 214 if (retval != 0) { 215 pkiDebug("failed to create dh parameters\n"); 216 goto cleanup; 217 } 218 219 retval = k5int_encode_krb5_auth_pack(&auth_pack, &coded_auth_pack); 220 if (retval) { 221 pkiDebug("failed to encode the AuthPack %d\n", retval); 222 goto cleanup; 223 } 224 #ifdef DEBUG_ASN1 225 print_buffer_bin((unsigned char *)coded_auth_pack->data, 226 coded_auth_pack->length, 227 "/tmp/client_auth_pack"); 228 #endif 229 230 /* create PKCS7 object from authpack */ 231 init_krb5_pa_pk_as_req(&req); 232 if (req == NULL) { 233 retval = ENOMEM; 234 goto cleanup; 235 } 236 if (use_content_info(context, reqctx, client)) { 237 retval = cms_contentinfo_create(context, plgctx->cryptoctx, 238 reqctx->cryptoctx, reqctx->idctx, 239 CMS_SIGN_CLIENT, 240 (unsigned char *) 241 coded_auth_pack->data, 242 coded_auth_pack->length, 243 (unsigned char **) 244 &req->signedAuthPack.data, 245 &req->signedAuthPack.length); 246 } else { 247 retval = cms_signeddata_create(context, plgctx->cryptoctx, 248 reqctx->cryptoctx, reqctx->idctx, 249 CMS_SIGN_CLIENT, 250 (unsigned char *) 251 coded_auth_pack->data, 252 coded_auth_pack->length, 253 (unsigned char **) 254 &req->signedAuthPack.data, 255 &req->signedAuthPack.length); 256 } 257 258 #ifdef DEBUG_ASN1 259 print_buffer_bin((unsigned char *)req->signedAuthPack.data, 260 req->signedAuthPack.length, 261 "/tmp/client_signed_data"); 262 #endif 263 264 krb5_free_data(context, coded_auth_pack); 265 if (retval) { 266 pkiDebug("failed to create pkcs7 signed data\n"); 267 goto cleanup; 268 } 269 270 /* create a list of trusted CAs */ 271 retval = create_krb5_trustedCertifiers(context, plgctx->cryptoctx, 272 reqctx->cryptoctx, reqctx->idctx, 273 &req->trustedCertifiers); 274 if (retval) 275 goto cleanup; 276 retval = create_issuerAndSerial(context, plgctx->cryptoctx, 277 reqctx->cryptoctx, reqctx->idctx, 278 (unsigned char **)&req->kdcPkId.data, 279 &req->kdcPkId.length); 280 if (retval) 281 goto cleanup; 282 283 /* Encode the as-req */ 284 retval = k5int_encode_krb5_pa_pk_as_req(req, as_req); 285 286 #ifdef DEBUG_ASN1 287 if (!retval) 288 print_buffer_bin((unsigned char *)(*as_req)->data, (*as_req)->length, 289 "/tmp/client_as_req"); 290 #endif 291 292 cleanup: 293 free_krb5_algorithm_identifiers(&cmstypes); 294 free_krb5_pa_pk_as_req(&req); 295 krb5_free_data_contents(context, &spki); 296 297 pkiDebug("pkinit_as_req_create retval=%d\n", (int) retval); 298 299 return retval; 300 } 301 302 static krb5_error_code 303 pa_pkinit_parse_rep(krb5_context context, 304 pkinit_context plgctx, 305 pkinit_req_context reqctx, 306 krb5_kdc_req * request, 307 krb5_pa_data * in_padata, 308 krb5_enctype etype, 309 krb5_keyblock * as_key, 310 krb5_data *encoded_request) 311 { 312 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 313 krb5_data asRep = { 0, 0, NULL}; 314 315 /* 316 * One way or the other - success or failure - no other PA systems can 317 * work if the server sent us a PKINIT reply, since only we know how to 318 * decrypt the key. 319 */ 320 if ((in_padata == NULL) || (in_padata->length == 0)) { 321 pkiDebug("pa_pkinit_parse_rep: no in_padata\n"); 322 return KRB5KDC_ERR_PREAUTH_FAILED; 323 } 324 325 asRep.data = (char *) in_padata->contents; 326 asRep.length = in_padata->length; 327 328 retval = 329 pkinit_as_rep_parse(context, plgctx, reqctx, in_padata->pa_type, 330 request, &asRep, as_key, etype, encoded_request); 331 if (retval) { 332 pkiDebug("pkinit_as_rep_parse returned %d (%s)\n", 333 retval, error_message(retval)); 334 goto cleanup; 335 } 336 337 retval = 0; 338 339 cleanup: 340 341 return retval; 342 } 343 344 static krb5_error_code 345 verify_kdc_san(krb5_context context, 346 pkinit_context plgctx, 347 pkinit_req_context reqctx, 348 krb5_principal kdcprinc, 349 int *valid_san, 350 int *need_eku_checking) 351 { 352 krb5_error_code retval; 353 char **certhosts = NULL, **cfghosts = NULL, **hostptr; 354 krb5_principal *princs = NULL; 355 unsigned char ***get_dns; 356 size_t i, j; 357 358 *valid_san = 0; 359 *need_eku_checking = 1; 360 361 retval = pkinit_libdefault_strings(context, 362 krb5_princ_realm(context, kdcprinc), 363 KRB5_CONF_PKINIT_KDC_HOSTNAME, 364 &cfghosts); 365 if (retval || cfghosts == NULL) { 366 pkiDebug("%s: No pkinit_kdc_hostname values found in config file\n", 367 __FUNCTION__); 368 get_dns = NULL; 369 } else { 370 pkiDebug("%s: pkinit_kdc_hostname values found in config file\n", 371 __FUNCTION__); 372 for (hostptr = cfghosts; *hostptr != NULL; hostptr++) 373 TRACE_PKINIT_CLIENT_SAN_CONFIG_DNSNAME(context, *hostptr); 374 get_dns = (unsigned char ***)&certhosts; 375 } 376 377 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, 378 reqctx->cryptoctx, reqctx->idctx, 379 &princs, NULL, get_dns); 380 if (retval) { 381 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); 382 TRACE_PKINIT_CLIENT_SAN_ERR(context); 383 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; 384 goto out; 385 } 386 for (i = 0; princs != NULL && princs[i] != NULL; i++) 387 TRACE_PKINIT_CLIENT_SAN_KDCCERT_PRINC(context, princs[i]); 388 if (certhosts != NULL) { 389 for (hostptr = certhosts; *hostptr != NULL; hostptr++) 390 TRACE_PKINIT_CLIENT_SAN_KDCCERT_DNSNAME(context, *hostptr); 391 } 392 393 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); 394 for (i = 0; princs != NULL && princs[i] != NULL; i++) { 395 if (krb5_principal_compare(context, princs[i], kdcprinc)) { 396 TRACE_PKINIT_CLIENT_SAN_MATCH_PRINC(context, kdcprinc); 397 pkiDebug("%s: pkinit san match found\n", __FUNCTION__); 398 *valid_san = 1; 399 *need_eku_checking = 0; 400 retval = 0; 401 goto out; 402 } 403 } 404 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); 405 406 if (certhosts == NULL) { 407 pkiDebug("%s: no certhosts (or we wouldn't accept them anyway)\n", 408 __FUNCTION__); 409 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; 410 goto out; 411 } 412 413 for (i = 0; certhosts[i] != NULL; i++) { 414 for (j = 0; cfghosts != NULL && cfghosts[j] != NULL; j++) { 415 pkiDebug("%s: comparing cert name '%s' with config name '%s'\n", 416 __FUNCTION__, certhosts[i], cfghosts[j]); 417 if (strcasecmp(certhosts[i], cfghosts[j]) == 0) { 418 TRACE_PKINIT_CLIENT_SAN_MATCH_DNSNAME(context, certhosts[i]); 419 pkiDebug("%s: we have a dnsName match\n", __FUNCTION__); 420 *valid_san = 1; 421 retval = 0; 422 goto out; 423 } 424 } 425 } 426 TRACE_PKINIT_CLIENT_SAN_MATCH_NONE(context); 427 pkiDebug("%s: no dnsName san match found\n", __FUNCTION__); 428 429 /* We found no match */ 430 retval = 0; 431 432 out: 433 if (princs != NULL) { 434 for (i = 0; princs[i] != NULL; i++) 435 krb5_free_principal(context, princs[i]); 436 free(princs); 437 } 438 if (certhosts != NULL) { 439 for (i = 0; certhosts[i] != NULL; i++) 440 free(certhosts[i]); 441 free(certhosts); 442 } 443 if (cfghosts != NULL) 444 profile_free_list(cfghosts); 445 446 pkiDebug("%s: returning retval %d, valid_san %d, need_eku_checking %d\n", 447 __FUNCTION__, retval, *valid_san, *need_eku_checking); 448 return retval; 449 } 450 451 static krb5_error_code 452 verify_kdc_eku(krb5_context context, 453 pkinit_context plgctx, 454 pkinit_req_context reqctx, 455 int *eku_accepted) 456 { 457 krb5_error_code retval; 458 459 *eku_accepted = 0; 460 461 if (reqctx->opts->require_eku == 0) { 462 TRACE_PKINIT_CLIENT_EKU_SKIP(context); 463 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__); 464 *eku_accepted = 1; 465 retval = 0; 466 goto out; 467 } 468 retval = crypto_check_cert_eku(context, plgctx->cryptoctx, 469 reqctx->cryptoctx, reqctx->idctx, 470 1, /* kdc cert */ 471 reqctx->opts->accept_secondary_eku, 472 eku_accepted); 473 if (retval) { 474 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", 475 __FUNCTION__, retval, error_message(retval)); 476 goto out; 477 } 478 479 out: 480 if (*eku_accepted) 481 TRACE_PKINIT_CLIENT_EKU_ACCEPT(context); 482 else 483 TRACE_PKINIT_CLIENT_EKU_REJECT(context); 484 pkiDebug("%s: returning retval %d, eku_accepted %d\n", 485 __FUNCTION__, retval, *eku_accepted); 486 return retval; 487 } 488 489 /* 490 * Parse PA-PK-AS-REP message. Optionally evaluates the message's 491 * certificate chain. 492 * Optionally returns various components. 493 */ 494 static krb5_error_code 495 pkinit_as_rep_parse(krb5_context context, 496 pkinit_context plgctx, 497 pkinit_req_context reqctx, 498 krb5_preauthtype pa_type, 499 krb5_kdc_req *request, 500 const krb5_data *as_rep, 501 krb5_keyblock *key_block, 502 krb5_enctype etype, 503 krb5_data *encoded_request) 504 { 505 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 506 krb5_principal kdc_princ = NULL; 507 krb5_pa_pk_as_rep *kdc_reply = NULL; 508 krb5_kdc_dh_key_info *kdc_dh = NULL; 509 krb5_reply_key_pack *key_pack = NULL; 510 krb5_data dh_data = { 0, 0, NULL }; 511 unsigned char *client_key = NULL, *kdc_hostname = NULL; 512 unsigned int client_key_len = 0; 513 krb5_checksum cksum = {0, 0, 0, NULL}; 514 krb5_data k5data; 515 krb5_data secret; 516 int valid_san = 0; 517 int valid_eku = 0; 518 int need_eku_checking = 1; 519 520 assert((as_rep != NULL) && (key_block != NULL)); 521 522 #ifdef DEBUG_ASN1 523 print_buffer_bin((unsigned char *)as_rep->data, as_rep->length, 524 "/tmp/client_as_rep"); 525 #endif 526 527 if ((retval = k5int_decode_krb5_pa_pk_as_rep(as_rep, &kdc_reply))) { 528 pkiDebug("decode_krb5_as_rep failed %d\n", retval); 529 return retval; 530 } 531 532 if (kdc_reply->choice != choice_pa_pk_as_rep_dhInfo) { 533 pkiDebug("unknown as_rep type %d\n", kdc_reply->choice); 534 retval = KRB5KDC_ERR_PREAUTH_FAILED; 535 goto cleanup; 536 } 537 538 #ifdef DEBUG_ASN1 539 print_buffer_bin(kdc_reply->u.dh_Info.dhSignedData.data, 540 kdc_reply->u.dh_Info.dhSignedData.length, 541 "/tmp/client_kdc_signeddata"); 542 #endif 543 retval = cms_signeddata_verify(context, plgctx->cryptoctx, 544 reqctx->cryptoctx, reqctx->idctx, 545 CMS_SIGN_SERVER, 546 reqctx->opts->require_crl_checking, 547 (unsigned char *) 548 kdc_reply->u.dh_Info.dhSignedData.data, 549 kdc_reply->u.dh_Info.dhSignedData.length, 550 (unsigned char **)&dh_data.data, 551 &dh_data.length, 552 NULL, NULL, NULL); 553 if (retval) { 554 pkiDebug("failed to verify pkcs7 signed data\n"); 555 TRACE_PKINIT_CLIENT_REP_DH_FAIL(context); 556 goto cleanup; 557 } 558 TRACE_PKINIT_CLIENT_REP_DH(context); 559 560 retval = krb5_build_principal_ext(context, &kdc_princ, 561 request->server->realm.length, 562 request->server->realm.data, 563 strlen(KRB5_TGS_NAME), KRB5_TGS_NAME, 564 request->server->realm.length, 565 request->server->realm.data, 566 0); 567 if (retval) 568 goto cleanup; 569 retval = verify_kdc_san(context, plgctx, reqctx, kdc_princ, 570 &valid_san, &need_eku_checking); 571 if (retval) 572 goto cleanup; 573 if (!valid_san) { 574 pkiDebug("%s: did not find an acceptable SAN in KDC certificate\n", 575 __FUNCTION__); 576 retval = KRB5KDC_ERR_KDC_NAME_MISMATCH; 577 goto cleanup; 578 } 579 580 if (need_eku_checking) { 581 retval = verify_kdc_eku(context, plgctx, reqctx, 582 &valid_eku); 583 if (retval) 584 goto cleanup; 585 if (!valid_eku) { 586 pkiDebug("%s: did not find an acceptable EKU in KDC certificate\n", 587 __FUNCTION__); 588 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; 589 goto cleanup; 590 } 591 } else 592 pkiDebug("%s: skipping EKU check\n", __FUNCTION__); 593 594 OCTETDATA_TO_KRB5DATA(&dh_data, &k5data); 595 596 #ifdef DEBUG_ASN1 597 print_buffer_bin(dh_data.data, dh_data.length, "/tmp/client_dh_key"); 598 #endif 599 retval = k5int_decode_krb5_kdc_dh_key_info(&k5data, &kdc_dh); 600 if (retval) { 601 pkiDebug("failed to decode kdc_dh_key_info\n"); 602 goto cleanup; 603 } 604 605 /* client after KDC reply */ 606 retval = client_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, 607 reqctx->idctx, 608 (unsigned char *)kdc_dh->subjectPublicKey.data, 609 kdc_dh->subjectPublicKey.length, &client_key, 610 &client_key_len); 611 if (retval) { 612 pkiDebug("failed to process dh params\n"); 613 goto cleanup; 614 } 615 616 secret = make_data(client_key, client_key_len); 617 retval = pkinit_kdf(context, &secret, kdc_reply->u.dh_Info.kdfID, 618 request->client, request->server, etype, 619 encoded_request, as_rep, key_block); 620 if (retval) { 621 pkiDebug("pkinit_kdf failed: %s\n", error_message(retval)); 622 goto cleanup; 623 } 624 625 retval = 0; 626 627 cleanup: 628 free(dh_data.data); 629 krb5_free_principal(context, kdc_princ); 630 free(client_key); 631 free_krb5_kdc_dh_key_info(&kdc_dh); 632 free_krb5_pa_pk_as_rep(&kdc_reply); 633 634 if (key_pack != NULL) { 635 free_krb5_reply_key_pack(&key_pack); 636 free(cksum.contents); 637 } 638 639 free(kdc_hostname); 640 641 pkiDebug("pkinit_as_rep_parse returning %d (%s)\n", 642 retval, error_message(retval)); 643 return retval; 644 } 645 646 static void 647 pkinit_client_profile(krb5_context context, 648 pkinit_context plgctx, 649 pkinit_req_context reqctx, 650 krb5_clpreauth_callbacks cb, 651 krb5_clpreauth_rock rock, 652 const krb5_data *realm) 653 { 654 const char *configured_identity; 655 char *eku_string = NULL, *minbits = NULL; 656 657 pkiDebug("pkinit_client_profile %p %p %p %p\n", 658 context, plgctx, reqctx, realm); 659 660 pkinit_libdefault_boolean(context, realm, 661 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING, 662 reqctx->opts->require_crl_checking, 663 &reqctx->opts->require_crl_checking); 664 pkinit_libdefault_string(context, realm, KRB5_CONF_PKINIT_DH_MIN_BITS, 665 &minbits); 666 reqctx->opts->dh_size = parse_dh_min_bits(context, minbits); 667 free(minbits); 668 pkinit_libdefault_string(context, realm, 669 KRB5_CONF_PKINIT_EKU_CHECKING, 670 &eku_string); 671 if (eku_string != NULL) { 672 if (strcasecmp(eku_string, "kpKDC") == 0) { 673 reqctx->opts->require_eku = 1; 674 reqctx->opts->accept_secondary_eku = 0; 675 } else if (strcasecmp(eku_string, "kpServerAuth") == 0) { 676 reqctx->opts->require_eku = 1; 677 reqctx->opts->accept_secondary_eku = 1; 678 } else if (strcasecmp(eku_string, "none") == 0) { 679 reqctx->opts->require_eku = 0; 680 reqctx->opts->accept_secondary_eku = 0; 681 } else { 682 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", 683 __FUNCTION__, eku_string); 684 } 685 free(eku_string); 686 } 687 688 /* Only process anchors here if they were not specified on command line */ 689 if (reqctx->idopts->anchors == NULL) 690 pkinit_libdefault_strings(context, realm, 691 KRB5_CONF_PKINIT_ANCHORS, 692 &reqctx->idopts->anchors); 693 pkinit_libdefault_strings(context, realm, 694 KRB5_CONF_PKINIT_POOL, 695 &reqctx->idopts->intermediates); 696 pkinit_libdefault_strings(context, realm, 697 KRB5_CONF_PKINIT_REVOKE, 698 &reqctx->idopts->crls); 699 pkinit_libdefault_strings(context, realm, 700 KRB5_CONF_PKINIT_IDENTITIES, 701 &reqctx->idopts->identity_alt); 702 reqctx->do_identity_matching = TRUE; 703 704 /* If we had a primary identity in the stored configuration, pick it up. */ 705 configured_identity = cb->get_cc_config(context, rock, 706 "X509_user_identity"); 707 if (configured_identity != NULL) { 708 free(reqctx->idopts->identity); 709 reqctx->idopts->identity = strdup(configured_identity); 710 reqctx->do_identity_matching = FALSE; 711 } 712 } 713 714 /* 715 * Convert a PKCS11 token flags value to the subset that we're interested in 716 * passing along to our API callers. 717 */ 718 static long long 719 pkinit_client_get_token_flags(unsigned long pkcs11_token_flags) 720 { 721 long long flags = 0; 722 723 if (pkcs11_token_flags & CKF_USER_PIN_COUNT_LOW) 724 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_COUNT_LOW; 725 if (pkcs11_token_flags & CKF_USER_PIN_FINAL_TRY) 726 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_FINAL_TRY; 727 if (pkcs11_token_flags & CKF_USER_PIN_LOCKED) 728 flags |= KRB5_RESPONDER_PKINIT_FLAGS_TOKEN_USER_PIN_LOCKED; 729 return flags; 730 } 731 732 /* 733 * Phase one of loading client identity information - call 734 * identity_initialize() to load any identities which we can without requiring 735 * help from the calling user, and use their names of those which we can't load 736 * to construct the challenge for the responder callback. 737 */ 738 static krb5_error_code 739 pkinit_client_prep_questions(krb5_context context, 740 krb5_clpreauth_moddata moddata, 741 krb5_clpreauth_modreq modreq, 742 krb5_get_init_creds_opt *opt, 743 krb5_clpreauth_callbacks cb, 744 krb5_clpreauth_rock rock, 745 krb5_kdc_req *request, 746 krb5_data *encoded_request_body, 747 krb5_data *encoded_previous_request, 748 krb5_pa_data *pa_data) 749 { 750 krb5_error_code retval; 751 pkinit_context plgctx = (pkinit_context)moddata; 752 pkinit_req_context reqctx = (pkinit_req_context)modreq; 753 size_t i, n; 754 const pkinit_deferred_id *deferred_ids; 755 const char *identity; 756 unsigned long ck_flags; 757 char *encoded; 758 k5_json_object jval = NULL; 759 k5_json_number jflag = NULL; 760 761 /* Don't ask questions for the informational padata items or when the 762 * ticket is issued. */ 763 if (pa_data->pa_type != KRB5_PADATA_PK_AS_REQ) 764 return 0; 765 766 if (!reqctx->identity_initialized) { 767 pkinit_client_profile(context, plgctx, reqctx, cb, rock, 768 &request->server->realm); 769 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, 770 reqctx->cryptoctx, reqctx->idopts, 771 reqctx->idctx, cb, rock, 772 request->client); 773 if (retval != 0) { 774 TRACE_PKINIT_CLIENT_NO_IDENTITY(context); 775 pkiDebug("pkinit_identity_initialize returned %d (%s)\n", 776 retval, error_message(retval)); 777 } 778 779 reqctx->identity_initialized = TRUE; 780 if (retval != 0) { 781 pkiDebug("%s: not asking responder question\n", __FUNCTION__); 782 retval = 0; 783 goto cleanup; 784 } 785 } 786 787 deferred_ids = crypto_get_deferred_ids(context, reqctx->idctx); 788 for (i = 0; deferred_ids != NULL && deferred_ids[i] != NULL; i++) 789 continue; 790 n = i; 791 792 /* Make sure we don't just return an empty challenge. */ 793 if (n == 0) { 794 pkiDebug("%s: no questions to ask\n", __FUNCTION__); 795 retval = 0; 796 goto cleanup; 797 } 798 799 /* Create the top-level object. */ 800 retval = k5_json_object_create(&jval); 801 if (retval != 0) 802 goto cleanup; 803 804 for (i = 0; i < n; i++) { 805 /* Add an entry to the top-level object for the identity. */ 806 identity = deferred_ids[i]->identity; 807 ck_flags = deferred_ids[i]->ck_flags; 808 /* Calculate the flags value for the bits that that we care about. */ 809 retval = k5_json_number_create(pkinit_client_get_token_flags(ck_flags), 810 &jflag); 811 if (retval != 0) 812 goto cleanup; 813 retval = k5_json_object_set(jval, identity, jflag); 814 if (retval != 0) 815 goto cleanup; 816 k5_json_release(jflag); 817 jflag = NULL; 818 } 819 820 /* Encode and done. */ 821 retval = k5_json_encode(jval, &encoded); 822 if (retval == 0) { 823 cb->ask_responder_question(context, rock, 824 KRB5_RESPONDER_QUESTION_PKINIT, 825 encoded); 826 pkiDebug("%s: asking question '%s'\n", __FUNCTION__, encoded); 827 free(encoded); 828 } 829 830 cleanup: 831 k5_json_release(jval); 832 k5_json_release(jflag); 833 834 pkiDebug("%s returning %d\n", __FUNCTION__, retval); 835 836 return retval; 837 } 838 839 /* 840 * Parse data supplied by the application's responder callback, saving off any 841 * PINs and passwords for identities which we noted needed them. 842 */ 843 struct save_one_password_data { 844 krb5_context context; 845 krb5_clpreauth_modreq modreq; 846 const char *caller; 847 }; 848 849 static void 850 save_one_password(void *arg, const char *key, k5_json_value val) 851 { 852 struct save_one_password_data *data = arg; 853 pkinit_req_context reqctx = (pkinit_req_context)data->modreq; 854 const char *password; 855 856 if (k5_json_get_tid(val) == K5_JSON_TID_STRING) { 857 password = k5_json_string_utf8(val); 858 pkiDebug("%s: \"%s\": %p\n", data->caller, key, password); 859 crypto_set_deferred_id(data->context, reqctx->idctx, key, password); 860 } 861 } 862 863 static krb5_error_code 864 pkinit_client_parse_answers(krb5_context context, 865 krb5_clpreauth_moddata moddata, 866 krb5_clpreauth_modreq modreq, 867 krb5_clpreauth_callbacks cb, 868 krb5_clpreauth_rock rock) 869 { 870 krb5_error_code retval; 871 const char *encoded; 872 k5_json_value jval; 873 struct save_one_password_data data; 874 875 data.context = context; 876 data.modreq = modreq; 877 data.caller = __FUNCTION__; 878 879 encoded = cb->get_responder_answer(context, rock, 880 KRB5_RESPONDER_QUESTION_PKINIT); 881 if (encoded == NULL) 882 return 0; 883 884 pkiDebug("pkinit_client_parse_answers: %s\n", encoded); 885 886 retval = k5_json_decode(encoded, &jval); 887 if (retval != 0) 888 goto cleanup; 889 890 /* Expect that the top-level answer is an object. */ 891 if (k5_json_get_tid(jval) != K5_JSON_TID_OBJECT) { 892 retval = EINVAL; 893 goto cleanup; 894 } 895 896 /* Store the passed-in per-identity passwords. */ 897 k5_json_object_iterate(jval, &save_one_password, &data); 898 retval = 0; 899 900 cleanup: 901 if (jval != NULL) 902 k5_json_release(jval); 903 return retval; 904 } 905 906 static krb5_error_code 907 pkinit_client_process(krb5_context context, krb5_clpreauth_moddata moddata, 908 krb5_clpreauth_modreq modreq, 909 krb5_get_init_creds_opt *gic_opt, 910 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, 911 krb5_kdc_req *request, krb5_data *encoded_request_body, 912 krb5_data *encoded_previous_request, 913 krb5_pa_data *in_padata, 914 krb5_prompter_fct prompter, void *prompter_data, 915 krb5_pa_data ***out_padata) 916 { 917 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 918 krb5_enctype enctype = -1; 919 int processing_request = 0; 920 pkinit_context plgctx = (pkinit_context)moddata; 921 pkinit_req_context reqctx = (pkinit_req_context)modreq; 922 krb5_keyblock as_key; 923 krb5_data d; 924 925 pkiDebug("pkinit_client_process %p %p %p %p\n", 926 context, plgctx, reqctx, request); 927 928 929 if (plgctx == NULL || reqctx == NULL) 930 return EINVAL; 931 932 switch ((int) in_padata->pa_type) { 933 case KRB5_PADATA_PKINIT_KX: 934 reqctx->rfc6112_kdc = 1; 935 return 0; 936 case KRB5_PADATA_AS_FRESHNESS: 937 TRACE_PKINIT_CLIENT_FRESHNESS_TOKEN(context); 938 krb5_free_data(context, reqctx->freshness_token); 939 reqctx->freshness_token = NULL; 940 d = make_data(in_padata->contents, in_padata->length); 941 return krb5_copy_data(context, &d, &reqctx->freshness_token); 942 case KRB5_PADATA_PK_AS_REQ: 943 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); 944 processing_request = 1; 945 break; 946 947 case KRB5_PADATA_PK_AS_REP: 948 pkiDebug("processing KRB5_PADATA_PK_AS_REP\n"); 949 break; 950 default: 951 pkiDebug("unrecognized patype = %d for PKINIT\n", 952 in_padata->pa_type); 953 return EINVAL; 954 } 955 956 if (processing_request) { 957 if (reqctx->idopts->anchors == NULL) { 958 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 959 _("No pkinit_anchors supplied")); 960 return KRB5_PREAUTH_FAILED; 961 } 962 /* Pull in PINs and passwords for identities which we deferred 963 * loading earlier. */ 964 retval = pkinit_client_parse_answers(context, moddata, modreq, 965 cb, rock); 966 if (retval) { 967 if (retval == KRB5KRB_ERR_GENERIC) 968 pkiDebug("pkinit responder answers were invalid\n"); 969 return retval; 970 } 971 if (!reqctx->identity_prompted) { 972 reqctx->identity_prompted = TRUE; 973 /* 974 * Load identities (again, potentially), prompting, if we can, for 975 * anything for which we didn't get an answer from the responder 976 * callback. 977 */ 978 pkinit_identity_set_prompter(reqctx->idctx, prompter, 979 prompter_data); 980 retval = pkinit_identity_prompt(context, plgctx->cryptoctx, 981 reqctx->cryptoctx, reqctx->idopts, 982 reqctx->idctx, cb, rock, 983 reqctx->do_identity_matching, 984 request->client); 985 pkinit_identity_set_prompter(reqctx->idctx, NULL, NULL); 986 reqctx->identity_prompt_retval = retval; 987 if (retval) { 988 TRACE_PKINIT_CLIENT_NO_IDENTITY(context); 989 pkiDebug("pkinit_identity_prompt returned %d (%s)\n", 990 retval, error_message(retval)); 991 return retval; 992 } 993 } else if (reqctx->identity_prompt_retval) { 994 retval = reqctx->identity_prompt_retval; 995 TRACE_PKINIT_CLIENT_NO_IDENTITY(context); 996 pkiDebug("pkinit_identity_prompt previously returned %d (%s)\n", 997 retval, error_message(retval)); 998 return retval; 999 } 1000 retval = pa_pkinit_gen_req(context, plgctx, reqctx, cb, rock, request, 1001 in_padata->pa_type, out_padata, prompter, 1002 prompter_data, gic_opt); 1003 } else { 1004 /* 1005 * Get the enctype of the reply. 1006 */ 1007 enctype = cb->get_etype(context, rock); 1008 retval = pa_pkinit_parse_rep(context, plgctx, reqctx, request, 1009 in_padata, enctype, &as_key, 1010 encoded_previous_request); 1011 if (retval == 0) { 1012 retval = cb->set_as_key(context, rock, &as_key); 1013 krb5_free_keyblock_contents(context, &as_key); 1014 } 1015 } 1016 1017 pkiDebug("pkinit_client_process: returning %d (%s)\n", 1018 retval, error_message(retval)); 1019 return retval; 1020 } 1021 1022 static krb5_error_code 1023 pkinit_client_tryagain(krb5_context context, krb5_clpreauth_moddata moddata, 1024 krb5_clpreauth_modreq modreq, 1025 krb5_get_init_creds_opt *gic_opt, 1026 krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock, 1027 krb5_kdc_req *request, krb5_data *encoded_request_body, 1028 krb5_data *encoded_previous_request, 1029 krb5_preauthtype pa_type, krb5_error *err_reply, 1030 krb5_pa_data **err_padata, krb5_prompter_fct prompter, 1031 void *prompter_data, krb5_pa_data ***out_padata) 1032 { 1033 krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED; 1034 pkinit_context plgctx = (pkinit_context)moddata; 1035 pkinit_req_context reqctx = (pkinit_req_context)modreq; 1036 krb5_pa_data *pa; 1037 krb5_data scratch; 1038 krb5_external_principal_identifier **certifiers = NULL; 1039 krb5_algorithm_identifier **algId = NULL; 1040 int do_again = 0; 1041 1042 pkiDebug("pkinit_client_tryagain %p %p %p %p\n", 1043 context, plgctx, reqctx, request); 1044 1045 if (reqctx->pa_type != pa_type || err_padata == NULL) 1046 return retval; 1047 1048 for (; *err_padata != NULL && !do_again; err_padata++) { 1049 pa = *err_padata; 1050 PADATA_TO_KRB5DATA(pa, &scratch); 1051 switch (pa->pa_type) { 1052 case TD_TRUSTED_CERTIFIERS: 1053 case TD_INVALID_CERTIFICATES: 1054 retval = k5int_decode_krb5_td_trusted_certifiers(&scratch, 1055 &certifiers); 1056 if (retval) { 1057 pkiDebug("failed to decode sequence of trusted certifiers\n"); 1058 goto cleanup; 1059 } 1060 retval = pkinit_process_td_trusted_certifiers(context, 1061 plgctx->cryptoctx, 1062 reqctx->cryptoctx, 1063 reqctx->idctx, 1064 certifiers, 1065 pa->pa_type); 1066 if (!retval) 1067 do_again = 1; 1068 break; 1069 case TD_DH_PARAMETERS: 1070 retval = k5int_decode_krb5_td_dh_parameters(&scratch, &algId); 1071 if (retval) { 1072 pkiDebug("failed to decode td_dh_parameters\n"); 1073 goto cleanup; 1074 } 1075 retval = pkinit_process_td_dh_params(context, plgctx->cryptoctx, 1076 reqctx->cryptoctx, 1077 reqctx->idctx, algId, 1078 &reqctx->opts->dh_size); 1079 if (!retval) 1080 do_again = 1; 1081 break; 1082 default: 1083 break; 1084 } 1085 } 1086 1087 if (do_again) { 1088 TRACE_PKINIT_CLIENT_TRYAGAIN(context); 1089 retval = pa_pkinit_gen_req(context, plgctx, reqctx, cb, rock, request, 1090 pa_type, out_padata, prompter, 1091 prompter_data, gic_opt); 1092 if (retval) 1093 goto cleanup; 1094 } 1095 1096 retval = 0; 1097 cleanup: 1098 if (certifiers != NULL) 1099 free_krb5_external_principal_identifier(&certifiers); 1100 1101 if (algId != NULL) 1102 free_krb5_algorithm_identifiers(&algId); 1103 1104 pkiDebug("pkinit_client_tryagain: returning %d (%s)\n", 1105 retval, error_message(retval)); 1106 return retval; 1107 } 1108 1109 static int 1110 pkinit_client_get_flags(krb5_context kcontext, krb5_preauthtype patype) 1111 { 1112 if (patype == KRB5_PADATA_PKINIT_KX || patype == KRB5_PADATA_AS_FRESHNESS) 1113 return PA_INFO; 1114 return PA_REAL; 1115 } 1116 1117 /* 1118 * We want to be notified about KRB5_PADATA_PKINIT_KX in addition to the actual 1119 * pkinit patypes because RFC 6112 requires anonymous KDCs to send it. We use 1120 * that to determine whether to use the broken MIT 1.9 behavior of sending 1121 * ContentInfo rather than SignedData or the RFC 6112 behavior 1122 */ 1123 static krb5_preauthtype supported_client_pa_types[] = { 1124 KRB5_PADATA_PK_AS_REP, 1125 KRB5_PADATA_PK_AS_REQ, 1126 KRB5_PADATA_PKINIT_KX, 1127 KRB5_PADATA_AS_FRESHNESS, 1128 0 1129 }; 1130 1131 static void 1132 pkinit_client_req_init(krb5_context context, 1133 krb5_clpreauth_moddata moddata, 1134 krb5_clpreauth_modreq *modreq_out) 1135 { 1136 krb5_error_code retval = ENOMEM; 1137 pkinit_req_context reqctx = NULL; 1138 pkinit_context plgctx = (pkinit_context)moddata; 1139 1140 *modreq_out = NULL; 1141 1142 reqctx = malloc(sizeof(*reqctx)); 1143 if (reqctx == NULL) 1144 return; 1145 memset(reqctx, 0, sizeof(*reqctx)); 1146 1147 reqctx->magic = PKINIT_REQ_CTX_MAGIC; 1148 reqctx->cryptoctx = NULL; 1149 reqctx->opts = NULL; 1150 reqctx->idctx = NULL; 1151 reqctx->idopts = NULL; 1152 reqctx->freshness_token = NULL; 1153 1154 retval = pkinit_init_req_opts(&reqctx->opts); 1155 if (retval) 1156 goto cleanup; 1157 1158 reqctx->opts->require_eku = plgctx->opts->require_eku; 1159 reqctx->opts->accept_secondary_eku = plgctx->opts->accept_secondary_eku; 1160 reqctx->opts->allow_upn = plgctx->opts->allow_upn; 1161 reqctx->opts->require_crl_checking = plgctx->opts->require_crl_checking; 1162 reqctx->opts->disable_freshness = plgctx->opts->disable_freshness; 1163 1164 retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 1165 if (retval) 1166 goto cleanup; 1167 1168 retval = pkinit_init_identity_crypto(&reqctx->idctx); 1169 if (retval) 1170 goto cleanup; 1171 1172 retval = pkinit_dup_identity_opts(plgctx->idopts, &reqctx->idopts); 1173 if (retval) 1174 goto cleanup; 1175 1176 *modreq_out = (krb5_clpreauth_modreq)reqctx; 1177 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); 1178 1179 cleanup: 1180 if (retval) { 1181 if (reqctx->idctx != NULL) 1182 pkinit_fini_identity_crypto(reqctx->idctx); 1183 if (reqctx->cryptoctx != NULL) 1184 pkinit_fini_req_crypto(reqctx->cryptoctx); 1185 if (reqctx->opts != NULL) 1186 pkinit_fini_req_opts(reqctx->opts); 1187 if (reqctx->idopts != NULL) 1188 pkinit_fini_identity_opts(reqctx->idopts); 1189 free(reqctx); 1190 } 1191 1192 return; 1193 } 1194 1195 static void 1196 pkinit_client_req_fini(krb5_context context, krb5_clpreauth_moddata moddata, 1197 krb5_clpreauth_modreq modreq) 1198 { 1199 pkinit_req_context reqctx = (pkinit_req_context)modreq; 1200 1201 pkiDebug("%s: received reqctx at %p\n", __FUNCTION__, reqctx); 1202 if (reqctx == NULL) 1203 return; 1204 if (reqctx->magic != PKINIT_REQ_CTX_MAGIC) { 1205 pkiDebug("%s: Bad magic value (%x) in req ctx\n", 1206 __FUNCTION__, reqctx->magic); 1207 return; 1208 } 1209 if (reqctx->opts != NULL) 1210 pkinit_fini_req_opts(reqctx->opts); 1211 1212 if (reqctx->cryptoctx != NULL) 1213 pkinit_fini_req_crypto(reqctx->cryptoctx); 1214 1215 if (reqctx->idctx != NULL) 1216 pkinit_fini_identity_crypto(reqctx->idctx); 1217 1218 if (reqctx->idopts != NULL) 1219 pkinit_fini_identity_opts(reqctx->idopts); 1220 1221 krb5_free_data(context, reqctx->freshness_token); 1222 1223 free(reqctx); 1224 return; 1225 } 1226 1227 static int 1228 pkinit_client_plugin_init(krb5_context context, 1229 krb5_clpreauth_moddata *moddata_out) 1230 { 1231 krb5_error_code retval = ENOMEM; 1232 pkinit_context ctx = NULL; 1233 1234 ctx = calloc(1, sizeof(*ctx)); 1235 if (ctx == NULL) 1236 return ENOMEM; 1237 memset(ctx, 0, sizeof(*ctx)); 1238 ctx->magic = PKINIT_CTX_MAGIC; 1239 ctx->opts = NULL; 1240 ctx->cryptoctx = NULL; 1241 ctx->idopts = NULL; 1242 1243 retval = pkinit_accessor_init(); 1244 if (retval) 1245 goto errout; 1246 1247 retval = pkinit_init_plg_opts(&ctx->opts); 1248 if (retval) 1249 goto errout; 1250 1251 retval = pkinit_init_plg_crypto(context, &ctx->cryptoctx); 1252 if (retval) 1253 goto errout; 1254 1255 retval = pkinit_init_identity_opts(&ctx->idopts); 1256 if (retval) 1257 goto errout; 1258 1259 *moddata_out = (krb5_clpreauth_moddata)ctx; 1260 1261 pkiDebug("%s: returning plgctx at %p\n", __FUNCTION__, ctx); 1262 1263 errout: 1264 if (retval) 1265 pkinit_client_plugin_fini(context, (krb5_clpreauth_moddata)ctx); 1266 1267 return retval; 1268 } 1269 1270 static void 1271 pkinit_client_plugin_fini(krb5_context context, krb5_clpreauth_moddata moddata) 1272 { 1273 pkinit_context ctx = (pkinit_context)moddata; 1274 1275 if (ctx == NULL || ctx->magic != PKINIT_CTX_MAGIC) { 1276 pkiDebug("pkinit_lib_fini: got bad plgctx (%p)!\n", ctx); 1277 return; 1278 } 1279 pkiDebug("%s: got plgctx at %p\n", __FUNCTION__, ctx); 1280 1281 pkinit_fini_identity_opts(ctx->idopts); 1282 pkinit_fini_plg_crypto(ctx->cryptoctx); 1283 pkinit_fini_plg_opts(ctx->opts); 1284 free(ctx); 1285 1286 } 1287 1288 static krb5_error_code 1289 add_string_to_array(krb5_context context, char ***array, const char *addition) 1290 { 1291 char **a = *array; 1292 size_t len; 1293 1294 for (len = 0; a != NULL && a[len] != NULL; len++); 1295 a = realloc(a, (len + 2) * sizeof(char *)); 1296 if (a == NULL) 1297 return ENOMEM; 1298 *array = a; 1299 a[len] = strdup(addition); 1300 if (a[len] == NULL) 1301 return ENOMEM; 1302 a[len + 1] = NULL; 1303 return 0; 1304 } 1305 1306 static krb5_error_code 1307 handle_gic_opt(krb5_context context, 1308 pkinit_context plgctx, 1309 const char *attr, 1310 const char *value) 1311 { 1312 krb5_error_code retval; 1313 1314 if (strcmp(attr, "X509_user_identity") == 0) { 1315 if (plgctx->idopts->identity != NULL) { 1316 krb5_set_error_message(context, KRB5_PREAUTH_FAILED, 1317 "X509_user_identity can not be given twice\n"); 1318 return KRB5_PREAUTH_FAILED; 1319 } 1320 plgctx->idopts->identity = strdup(value); 1321 if (plgctx->idopts->identity == NULL) { 1322 krb5_set_error_message(context, ENOMEM, 1323 "Could not duplicate X509_user_identity value\n"); 1324 return ENOMEM; 1325 } 1326 } else if (strcmp(attr, "X509_anchors") == 0) { 1327 retval = add_string_to_array(context, &plgctx->idopts->anchors, value); 1328 if (retval) 1329 return retval; 1330 } else if (strcmp(attr, "disable_freshness") == 0) { 1331 if (strcmp(value, "yes") == 0) 1332 plgctx->opts->disable_freshness = 1; 1333 } 1334 return 0; 1335 } 1336 1337 static krb5_error_code 1338 pkinit_client_gic_opt(krb5_context context, krb5_clpreauth_moddata moddata, 1339 krb5_get_init_creds_opt *gic_opt, 1340 const char *attr, 1341 const char *value) 1342 { 1343 krb5_error_code retval; 1344 pkinit_context plgctx = (pkinit_context)moddata; 1345 1346 pkiDebug("(pkinit) received '%s' = '%s'\n", attr, value); 1347 retval = handle_gic_opt(context, plgctx, attr, value); 1348 if (retval) 1349 return retval; 1350 1351 return 0; 1352 } 1353 1354 krb5_error_code 1355 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1356 krb5_plugin_vtable vtable); 1357 1358 krb5_error_code 1359 clpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1360 krb5_plugin_vtable vtable) 1361 { 1362 krb5_clpreauth_vtable vt; 1363 1364 if (maj_ver != 1) 1365 return KRB5_PLUGIN_VER_NOTSUPP; 1366 vt = (krb5_clpreauth_vtable)vtable; 1367 vt->name = "pkinit"; 1368 vt->pa_type_list = supported_client_pa_types; 1369 vt->init = pkinit_client_plugin_init; 1370 vt->fini = pkinit_client_plugin_fini; 1371 vt->flags = pkinit_client_get_flags; 1372 vt->request_init = pkinit_client_req_init; 1373 vt->prep_questions = pkinit_client_prep_questions; 1374 vt->request_fini = pkinit_client_req_fini; 1375 vt->process = pkinit_client_process; 1376 vt->tryagain = pkinit_client_tryagain; 1377 vt->gic_opts = pkinit_client_gic_opt; 1378 return 0; 1379 } 1380