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