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 "krb5/certauth_plugin.h" 35 36 /* Aliases used by the built-in certauth modules */ 37 struct certauth_req_opts { 38 krb5_kdcpreauth_callbacks cb; 39 krb5_kdcpreauth_rock rock; 40 pkinit_kdc_context plgctx; 41 pkinit_kdc_req_context reqctx; 42 }; 43 44 typedef struct certauth_module_handle_st { 45 struct krb5_certauth_vtable_st vt; 46 krb5_certauth_moddata moddata; 47 } *certauth_handle; 48 49 struct krb5_kdcpreauth_moddata_st { 50 pkinit_kdc_context *realm_contexts; 51 certauth_handle *certauth_modules; 52 }; 53 54 static krb5_error_code 55 pkinit_init_kdc_req_context(krb5_context, pkinit_kdc_req_context *blob); 56 57 static void 58 pkinit_fini_kdc_req_context(krb5_context context, void *blob); 59 60 static void 61 pkinit_server_plugin_fini_realm(krb5_context context, 62 pkinit_kdc_context plgctx); 63 64 static void 65 pkinit_server_plugin_fini(krb5_context context, 66 krb5_kdcpreauth_moddata moddata); 67 68 static pkinit_kdc_context 69 pkinit_find_realm_context(krb5_context context, 70 krb5_kdcpreauth_moddata moddata, 71 krb5_principal princ); 72 73 static void 74 free_realm_contexts(krb5_context context, pkinit_kdc_context *realm_contexts) 75 { 76 int i; 77 78 if (realm_contexts == NULL) 79 return; 80 for (i = 0; realm_contexts[i] != NULL; i++) 81 pkinit_server_plugin_fini_realm(context, realm_contexts[i]); 82 pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); 83 free(realm_contexts); 84 } 85 86 static void 87 free_certauth_handles(krb5_context context, certauth_handle *list) 88 { 89 int i; 90 91 if (list == NULL) 92 return; 93 for (i = 0; list[i] != NULL; i++) { 94 if (list[i]->vt.fini != NULL) 95 list[i]->vt.fini(context, list[i]->moddata); 96 free(list[i]); 97 } 98 free(list); 99 } 100 101 static krb5_error_code 102 pkinit_create_edata(krb5_context context, 103 pkinit_plg_crypto_context plg_cryptoctx, 104 pkinit_req_crypto_context req_cryptoctx, 105 pkinit_identity_crypto_context id_cryptoctx, 106 pkinit_plg_opts *opts, 107 krb5_error_code err_code, 108 krb5_pa_data ***e_data_out) 109 { 110 krb5_error_code retval = KRB5KRB_ERR_GENERIC; 111 112 pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n", 113 err_code, error_message(err_code)); 114 switch(err_code) { 115 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE: 116 retval = pkinit_create_td_trusted_certifiers(context, 117 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out); 118 break; 119 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED: 120 retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx, 121 req_cryptoctx, id_cryptoctx, opts, e_data_out); 122 break; 123 case KRB5KDC_ERR_INVALID_CERTIFICATE: 124 case KRB5KDC_ERR_REVOKED_CERTIFICATE: 125 retval = pkinit_create_td_invalid_certificate(context, 126 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data_out); 127 break; 128 default: 129 pkiDebug("no edata needed for error %d (%s)\n", 130 err_code, error_message(err_code)); 131 retval = 0; 132 goto cleanup; 133 } 134 135 cleanup: 136 137 return retval; 138 } 139 140 static void 141 pkinit_server_get_edata(krb5_context context, 142 krb5_kdc_req *request, 143 krb5_kdcpreauth_callbacks cb, 144 krb5_kdcpreauth_rock rock, 145 krb5_kdcpreauth_moddata moddata, 146 krb5_preauthtype pa_type, 147 krb5_kdcpreauth_edata_respond_fn respond, 148 void *arg) 149 { 150 krb5_error_code retval = 0; 151 pkinit_kdc_context plgctx = NULL; 152 153 pkiDebug("pkinit_server_get_edata: entered!\n"); 154 155 156 /* 157 * If we don't have a realm context for the given realm, 158 * don't tell the client that we support pkinit! 159 */ 160 plgctx = pkinit_find_realm_context(context, moddata, request->server); 161 if (plgctx == NULL) 162 retval = EINVAL; 163 164 /* Send a freshness token if the client requested one. */ 165 if (!retval) 166 cb->send_freshness_token(context, rock); 167 168 (*respond)(arg, retval, NULL); 169 } 170 171 static krb5_error_code 172 verify_client_san(krb5_context context, 173 pkinit_kdc_context plgctx, 174 pkinit_kdc_req_context reqctx, 175 krb5_kdcpreauth_callbacks cb, 176 krb5_kdcpreauth_rock rock, 177 krb5_const_principal client, 178 int *valid_san) 179 { 180 krb5_error_code retval; 181 krb5_principal *princs = NULL, upn; 182 krb5_boolean match; 183 char **upns = NULL; 184 int i; 185 #ifdef DEBUG_SAN_INFO 186 char *client_string = NULL, *san_string; 187 #endif 188 189 *valid_san = 0; 190 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, 191 reqctx->cryptoctx, plgctx->idctx, 192 &princs, 193 plgctx->opts->allow_upn ? &upns : NULL, 194 NULL); 195 if (retval) { 196 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); 197 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 198 goto out; 199 } 200 201 if (princs == NULL && upns == NULL) { 202 TRACE_PKINIT_SERVER_NO_SAN(context); 203 retval = ENOENT; 204 goto out; 205 } 206 207 #ifdef DEBUG_SAN_INFO 208 krb5_unparse_name(context, client, &client_string); 209 #endif 210 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); 211 for (i = 0; princs != NULL && princs[i] != NULL; i++) { 212 #ifdef DEBUG_SAN_INFO 213 krb5_unparse_name(context, princs[i], &san_string); 214 pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n", 215 __FUNCTION__, client_string, san_string); 216 krb5_free_unparsed_name(context, san_string); 217 #endif 218 if (cb->match_client(context, rock, princs[i])) { 219 TRACE_PKINIT_SERVER_MATCHING_SAN_FOUND(context); 220 *valid_san = 1; 221 retval = 0; 222 goto out; 223 } 224 } 225 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); 226 /* 227 * XXX if cert has names but none match, should we 228 * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here? 229 */ 230 231 if (upns == NULL) { 232 pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n", 233 __FUNCTION__); 234 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 235 goto out; 236 } 237 238 pkiDebug("%s: Checking upn sans\n", __FUNCTION__); 239 for (i = 0; upns[i] != NULL; i++) { 240 #ifdef DEBUG_SAN_INFO 241 pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n", 242 __FUNCTION__, client_string, upns[i]); 243 #endif 244 retval = krb5_parse_name_flags(context, upns[i], 245 KRB5_PRINCIPAL_PARSE_ENTERPRISE, &upn); 246 if (retval) { 247 TRACE_PKINIT_SERVER_UPN_PARSE_FAIL(context, upns[i], retval); 248 continue; 249 } 250 match = cb->match_client(context, rock, upn); 251 krb5_free_principal(context, upn); 252 if (match) { 253 TRACE_PKINIT_SERVER_MATCHING_UPN_FOUND(context); 254 *valid_san = 1; 255 retval = 0; 256 goto out; 257 } 258 } 259 pkiDebug("%s: no upn san match found\n", __FUNCTION__); 260 261 retval = 0; 262 out: 263 if (princs != NULL) { 264 for (i = 0; princs[i] != NULL; i++) 265 krb5_free_principal(context, princs[i]); 266 free(princs); 267 } 268 if (upns != NULL) { 269 for (i = 0; upns[i] != NULL; i++) 270 free(upns[i]); 271 free(upns); 272 } 273 #ifdef DEBUG_SAN_INFO 274 if (client_string != NULL) 275 krb5_free_unparsed_name(context, client_string); 276 #endif 277 pkiDebug("%s: returning retval %d, valid_san %d\n", 278 __FUNCTION__, retval, *valid_san); 279 return retval; 280 } 281 282 static krb5_error_code 283 verify_client_eku(krb5_context context, 284 pkinit_kdc_context plgctx, 285 pkinit_kdc_req_context reqctx, 286 int *eku_accepted) 287 { 288 krb5_error_code retval; 289 290 *eku_accepted = 0; 291 292 if (plgctx->opts->require_eku == 0) { 293 TRACE_PKINIT_SERVER_EKU_SKIP(context); 294 *eku_accepted = 1; 295 retval = 0; 296 goto out; 297 } 298 299 retval = crypto_check_cert_eku(context, plgctx->cryptoctx, 300 reqctx->cryptoctx, plgctx->idctx, 301 0, /* kdc cert */ 302 plgctx->opts->accept_secondary_eku, 303 eku_accepted); 304 if (retval) { 305 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", 306 __FUNCTION__, retval, error_message(retval)); 307 goto out; 308 } 309 310 out: 311 pkiDebug("%s: returning retval %d, eku_accepted %d\n", 312 __FUNCTION__, retval, *eku_accepted); 313 return retval; 314 } 315 316 317 /* Run the received, verified certificate through certauth modules, to verify 318 * that it is authorized to authenticate as client. */ 319 static krb5_error_code 320 authorize_cert(krb5_context context, certauth_handle *certauth_modules, 321 pkinit_kdc_context plgctx, pkinit_kdc_req_context reqctx, 322 krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock, 323 krb5_principal client, krb5_boolean *hwauth_out) 324 { 325 krb5_error_code ret; 326 certauth_handle h; 327 struct certauth_req_opts opts; 328 krb5_boolean accepted = FALSE, hwauth = FALSE; 329 uint8_t *cert; 330 size_t i, cert_len; 331 void *db_ent = NULL; 332 char **ais = NULL, **ai = NULL; 333 334 /* Re-encode the received certificate into DER, which is extra work, but 335 * avoids creating an X.509 library dependency in the interface. */ 336 ret = crypto_encode_der_cert(context, reqctx->cryptoctx, &cert, &cert_len); 337 if (ret) 338 goto cleanup; 339 340 /* Set options for the builtin module. */ 341 opts.plgctx = plgctx; 342 opts.reqctx = reqctx; 343 opts.cb = cb; 344 opts.rock = rock; 345 346 db_ent = cb->client_entry(context, rock); 347 348 /* 349 * Check the certificate against each certauth module. For the certificate 350 * to be authorized at least one module must return 0 or 351 * KRB5_CERTAUTH_HWAUTH, and no module can return an error code other than 352 * KRB5_PLUGIN_NO_HANDLE (pass) or KRB5_CERTAUTH_HWAUTH_PASS (pass but 353 * set hw-authent). Add indicators from all modules. 354 */ 355 ret = KRB5_PLUGIN_NO_HANDLE; 356 for (i = 0; certauth_modules != NULL && certauth_modules[i] != NULL; i++) { 357 h = certauth_modules[i]; 358 TRACE_PKINIT_SERVER_CERT_AUTH(context, h->vt.name); 359 ret = h->vt.authorize(context, h->moddata, cert, cert_len, client, 360 &opts, db_ent, &ais); 361 if (ret == 0) 362 accepted = TRUE; 363 else if (ret == KRB5_CERTAUTH_HWAUTH) 364 accepted = hwauth = TRUE; 365 else if (ret == KRB5_CERTAUTH_HWAUTH_PASS) 366 hwauth = TRUE; 367 else if (ret != KRB5_PLUGIN_NO_HANDLE) 368 goto cleanup; 369 370 if (ais != NULL) { 371 /* Assert authentication indicators from the module. */ 372 for (ai = ais; *ai != NULL; ai++) { 373 ret = cb->add_auth_indicator(context, rock, *ai); 374 if (ret) 375 goto cleanup; 376 } 377 h->vt.free_ind(context, h->moddata, ais); 378 ais = NULL; 379 } 380 } 381 382 *hwauth_out = hwauth; 383 ret = accepted ? 0 : KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 384 385 cleanup: 386 free(cert); 387 return ret; 388 } 389 390 /* Return an error if freshness tokens are required and one was not received. 391 * Log an appropriate message indicating whether a valid token was received. */ 392 static krb5_error_code 393 check_log_freshness(krb5_context context, pkinit_kdc_context plgctx, 394 krb5_kdc_req *request, krb5_boolean valid_freshness_token) 395 { 396 krb5_error_code ret; 397 char *name = NULL; 398 399 ret = krb5_unparse_name(context, request->client, &name); 400 if (ret) 401 return ret; 402 if (plgctx->opts->require_freshness && !valid_freshness_token) { 403 com_err("", 0, _("PKINIT: no freshness token, rejecting auth from %s"), 404 name); 405 ret = KRB5KDC_ERR_PREAUTH_FAILED; 406 } else if (valid_freshness_token) { 407 com_err("", 0, _("PKINIT: freshness token received from %s"), name); 408 } else { 409 com_err("", 0, _("PKINIT: no freshness token received from %s"), name); 410 } 411 krb5_free_unparsed_name(context, name); 412 return ret; 413 } 414 415 static void 416 pkinit_server_verify_padata(krb5_context context, 417 krb5_data *req_pkt, 418 krb5_kdc_req * request, 419 krb5_enc_tkt_part * enc_tkt_reply, 420 krb5_pa_data * data, 421 krb5_kdcpreauth_callbacks cb, 422 krb5_kdcpreauth_rock rock, 423 krb5_kdcpreauth_moddata moddata, 424 krb5_kdcpreauth_verify_respond_fn respond, 425 void *arg) 426 { 427 krb5_error_code retval = 0; 428 krb5_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; 429 krb5_pa_pk_as_req *reqp = NULL; 430 krb5_auth_pack *auth_pack = NULL; 431 pkinit_kdc_context plgctx = NULL; 432 pkinit_kdc_req_context reqctx = NULL; 433 krb5_checksum cksum = {0, 0, 0, NULL}; 434 krb5_data *der_req = NULL; 435 krb5_data k5data, *ftoken; 436 int is_signed = 1; 437 krb5_pa_data **e_data = NULL; 438 krb5_kdcpreauth_modreq modreq = NULL; 439 krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE; 440 char **sp; 441 442 pkiDebug("pkinit_verify_padata: entered!\n"); 443 if (data == NULL || data->length <= 0 || data->contents == NULL) { 444 (*respond)(arg, EINVAL, NULL, NULL, NULL); 445 return; 446 } 447 448 449 if (moddata == NULL) { 450 (*respond)(arg, EINVAL, NULL, NULL, NULL); 451 return; 452 } 453 454 plgctx = pkinit_find_realm_context(context, moddata, request->server); 455 if (plgctx == NULL) { 456 (*respond)(arg, EINVAL, NULL, NULL, NULL); 457 return; 458 } 459 460 #ifdef DEBUG_ASN1 461 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); 462 #endif 463 /* create a per-request context */ 464 retval = pkinit_init_kdc_req_context(context, &reqctx); 465 if (retval) 466 goto cleanup; 467 reqctx->pa_type = data->pa_type; 468 469 PADATA_TO_KRB5DATA(data, &k5data); 470 471 if (data->pa_type != KRB5_PADATA_PK_AS_REQ) { 472 pkiDebug("unrecognized pa_type = %d\n", data->pa_type); 473 retval = EINVAL; 474 goto cleanup; 475 } 476 477 TRACE_PKINIT_SERVER_PADATA_VERIFY(context); 478 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); 479 if (retval) { 480 pkiDebug("decode_krb5_pa_pk_as_req failed\n"); 481 goto cleanup; 482 } 483 #ifdef DEBUG_ASN1 484 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, 485 "/tmp/kdc_signed_data"); 486 #endif 487 retval = cms_signeddata_verify(context, plgctx->cryptoctx, 488 reqctx->cryptoctx, plgctx->idctx, 489 CMS_SIGN_CLIENT, 490 plgctx->opts->require_crl_checking, 491 (unsigned char *)reqp->signedAuthPack.data, 492 reqp->signedAuthPack.length, 493 (unsigned char **)&authp_data.data, 494 &authp_data.length, 495 (unsigned char **)&krb5_authz.data, 496 &krb5_authz.length, &is_signed); 497 if (retval) { 498 TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context); 499 goto cleanup; 500 } 501 if (is_signed) { 502 retval = authorize_cert(context, moddata->certauth_modules, plgctx, 503 reqctx, cb, rock, request->client, &hwauth); 504 if (retval) 505 goto cleanup; 506 507 } else { /* !is_signed */ 508 if (!krb5_principal_compare(context, request->client, 509 krb5_anonymous_principal())) { 510 retval = KRB5KDC_ERR_PREAUTH_FAILED; 511 krb5_set_error_message(context, retval, 512 _("Pkinit request not signed, but client " 513 "not anonymous.")); 514 goto cleanup; 515 } 516 } 517 #ifdef DEBUG_ASN1 518 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); 519 #endif 520 521 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); 522 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); 523 if (retval) { 524 pkiDebug("failed to decode krb5_auth_pack\n"); 525 goto cleanup; 526 } 527 528 retval = krb5_check_clockskew(context, auth_pack->pkAuthenticator.ctime); 529 if (retval) 530 goto cleanup; 531 532 /* check dh parameters */ 533 if (auth_pack->clientPublicValue.length > 0) { 534 retval = server_check_dh(context, plgctx->cryptoctx, 535 reqctx->cryptoctx, plgctx->idctx, 536 &auth_pack->clientPublicValue, 537 plgctx->opts->dh_min_bits); 538 if (retval) { 539 pkiDebug("bad dh parameters\n"); 540 goto cleanup; 541 } 542 } else if (!is_signed) { 543 /*Anonymous pkinit requires DH*/ 544 retval = KRB5KDC_ERR_PREAUTH_FAILED; 545 krb5_set_error_message(context, retval, 546 _("Anonymous pkinit without DH public " 547 "value not supported.")); 548 goto cleanup; 549 } 550 der_req = cb->request_body(context, rock); 551 retval = krb5_c_make_checksum(context, CKSUMTYPE_SHA1, NULL, 0, der_req, 552 &cksum); 553 if (retval) { 554 pkiDebug("unable to calculate AS REQ checksum\n"); 555 goto cleanup; 556 } 557 if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || 558 k5_bcmp(cksum.contents, auth_pack->pkAuthenticator.paChecksum.contents, 559 cksum.length) != 0) { 560 pkiDebug("failed to match the checksum\n"); 561 #ifdef DEBUG_CKSUM 562 pkiDebug("calculating checksum on buf size (%d)\n", req_pkt->length); 563 print_buffer(req_pkt->data, req_pkt->length); 564 pkiDebug("received checksum type=%d size=%d ", 565 auth_pack->pkAuthenticator.paChecksum.checksum_type, 566 auth_pack->pkAuthenticator.paChecksum.length); 567 print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, 568 auth_pack->pkAuthenticator.paChecksum.length); 569 pkiDebug("expected checksum type=%d size=%d ", 570 cksum.checksum_type, cksum.length); 571 print_buffer(cksum.contents, cksum.length); 572 #endif 573 574 retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; 575 goto cleanup; 576 } 577 578 ftoken = auth_pack->pkAuthenticator.freshnessToken; 579 if (ftoken != NULL) { 580 retval = cb->check_freshness_token(context, rock, ftoken); 581 if (retval) 582 goto cleanup; 583 valid_freshness_token = TRUE; 584 } 585 586 /* check if kdcPkId present and match KDC's subjectIdentifier */ 587 if (reqp->kdcPkId.data != NULL) { 588 int valid_kdcPkId = 0; 589 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, 590 reqctx->cryptoctx, plgctx->idctx, 591 (unsigned char *)reqp->kdcPkId.data, 592 reqp->kdcPkId.length, &valid_kdcPkId); 593 if (retval) 594 goto cleanup; 595 if (!valid_kdcPkId) { 596 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; " 597 "RFC says to ignore and proceed\n"); 598 } 599 } 600 /* remember the decoded auth_pack for verify_padata routine */ 601 reqctx->rcv_auth_pack = auth_pack; 602 auth_pack = NULL; 603 604 if (is_signed) { 605 retval = check_log_freshness(context, plgctx, request, 606 valid_freshness_token); 607 if (retval) 608 goto cleanup; 609 } 610 611 if (is_signed && plgctx->auth_indicators != NULL) { 612 /* Assert configured authentication indicators. */ 613 for (sp = plgctx->auth_indicators; *sp != NULL; sp++) { 614 retval = cb->add_auth_indicator(context, rock, *sp); 615 if (retval) 616 goto cleanup; 617 } 618 } 619 620 /* remember to set the PREAUTH flag in the reply */ 621 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 622 if (hwauth) 623 enc_tkt_reply->flags |= TKT_FLG_HW_AUTH; 624 modreq = (krb5_kdcpreauth_modreq)reqctx; 625 reqctx = NULL; 626 627 cleanup: 628 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { 629 pkiDebug("pkinit_verify_padata failed: creating e-data\n"); 630 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, 631 plgctx->idctx, plgctx->opts, retval, &e_data)) 632 pkiDebug("pkinit_create_edata failed\n"); 633 } 634 635 free_krb5_pa_pk_as_req(&reqp); 636 free(cksum.contents); 637 free(authp_data.data); 638 free(krb5_authz.data); 639 if (reqctx != NULL) 640 pkinit_fini_kdc_req_context(context, reqctx); 641 free_krb5_auth_pack(&auth_pack); 642 643 (*respond)(arg, retval, modreq, e_data, NULL); 644 } 645 static krb5_error_code 646 return_pkinit_kx(krb5_context context, krb5_kdc_req *request, 647 krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, 648 krb5_pa_data **out_padata) 649 { 650 krb5_error_code ret = 0; 651 krb5_keyblock *session = reply->ticket->enc_part2->session; 652 krb5_keyblock *new_session = NULL; 653 krb5_pa_data *pa = NULL; 654 krb5_enc_data enc; 655 krb5_data *scratch = NULL; 656 657 *out_padata = NULL; 658 enc.ciphertext.data = NULL; 659 if (!krb5_principal_compare(context, request->client, 660 krb5_anonymous_principal())) 661 return 0; 662 /* 663 * The KDC contribution key needs to be a fresh key of an enctype supported 664 * by the client and server. The existing session key meets these 665 * requirements so we use it. 666 */ 667 ret = krb5_c_fx_cf2_simple(context, session, "PKINIT", 668 encrypting_key, "KEYEXCHANGE", 669 &new_session); 670 if (ret) 671 goto cleanup; 672 ret = encode_krb5_encryption_key( session, &scratch); 673 if (ret) 674 goto cleanup; 675 ret = krb5_encrypt_helper(context, encrypting_key, 676 KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc); 677 if (ret) 678 goto cleanup; 679 memset(scratch->data, 0, scratch->length); 680 krb5_free_data(context, scratch); 681 scratch = NULL; 682 ret = encode_krb5_enc_data(&enc, &scratch); 683 if (ret) 684 goto cleanup; 685 pa = malloc(sizeof(krb5_pa_data)); 686 if (pa == NULL) { 687 ret = ENOMEM; 688 goto cleanup; 689 } 690 pa->pa_type = KRB5_PADATA_PKINIT_KX; 691 pa->length = scratch->length; 692 pa->contents = (krb5_octet *) scratch->data; 693 *out_padata = pa; 694 scratch->data = NULL; 695 memset(session->contents, 0, session->length); 696 krb5_free_keyblock_contents(context, session); 697 *session = *new_session; 698 new_session->contents = NULL; 699 cleanup: 700 krb5_free_data_contents(context, &enc.ciphertext); 701 krb5_free_keyblock(context, new_session); 702 krb5_free_data(context, scratch); 703 return ret; 704 } 705 706 static krb5_error_code 707 pkinit_pick_kdf_alg(krb5_context context, krb5_data **kdf_list, 708 krb5_data **alg_oid) 709 { 710 krb5_error_code retval = 0; 711 krb5_data *req_oid = NULL; 712 const krb5_data *supp_oid = NULL; 713 krb5_data *tmp_oid = NULL; 714 int i, j = 0; 715 716 /* if we don't find a match, return NULL value */ 717 *alg_oid = NULL; 718 719 /* for each of the OIDs that the server supports... */ 720 for (i = 0; NULL != (supp_oid = supported_kdf_alg_ids[i]); i++) { 721 /* if the requested OID is in the client's list, use it. */ 722 for (j = 0; NULL != (req_oid = kdf_list[j]); j++) { 723 if ((req_oid->length == supp_oid->length) && 724 (0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) { 725 tmp_oid = k5alloc(sizeof(krb5_data), &retval); 726 if (retval) 727 goto cleanup; 728 tmp_oid->data = k5memdup(supp_oid->data, supp_oid->length, 729 &retval); 730 if (retval) 731 goto cleanup; 732 tmp_oid->length = supp_oid->length; 733 *alg_oid = tmp_oid; 734 /* don't free the OID in clean-up if we are returning it */ 735 tmp_oid = NULL; 736 goto cleanup; 737 } 738 } 739 } 740 cleanup: 741 if (tmp_oid) 742 krb5_free_data(context, tmp_oid); 743 return retval; 744 } 745 746 static krb5_error_code 747 pkinit_server_return_padata(krb5_context context, 748 krb5_pa_data * padata, 749 krb5_data *req_pkt, 750 krb5_kdc_req * request, 751 krb5_kdc_rep * reply, 752 krb5_keyblock * encrypting_key, 753 krb5_pa_data ** send_pa, 754 krb5_kdcpreauth_callbacks cb, 755 krb5_kdcpreauth_rock rock, 756 krb5_kdcpreauth_moddata moddata, 757 krb5_kdcpreauth_modreq modreq) 758 { 759 krb5_error_code retval = 0; 760 krb5_data scratch = {0, 0, NULL}; 761 krb5_pa_pk_as_req *reqp = NULL; 762 int i = 0; 763 764 unsigned char *dh_pubkey = NULL, *server_key = NULL; 765 unsigned int server_key_len = 0, dh_pubkey_len = 0; 766 krb5_keyblock reply_key = { 0 }; 767 768 krb5_kdc_dh_key_info dhkey_info; 769 krb5_data *encoded_dhkey_info = NULL; 770 krb5_pa_pk_as_rep *rep = NULL; 771 krb5_data *out_data = NULL; 772 krb5_data secret; 773 774 krb5_enctype enctype = -1; 775 776 krb5_reply_key_pack *key_pack = NULL; 777 krb5_data *encoded_key_pack = NULL; 778 779 pkinit_kdc_context plgctx; 780 pkinit_kdc_req_context reqctx; 781 782 *send_pa = NULL; 783 if (padata->pa_type == KRB5_PADATA_PKINIT_KX) { 784 return return_pkinit_kx(context, request, reply, 785 encrypting_key, send_pa); 786 } 787 if (padata->length <= 0 || padata->contents == NULL) 788 return 0; 789 790 if (modreq == NULL) { 791 pkiDebug("missing request context \n"); 792 return EINVAL; 793 } 794 795 plgctx = pkinit_find_realm_context(context, moddata, request->server); 796 if (plgctx == NULL) { 797 pkiDebug("Unable to locate correct realm context\n"); 798 return ENOENT; 799 } 800 801 TRACE_PKINIT_SERVER_RETURN_PADATA(context); 802 reqctx = (pkinit_kdc_req_context)modreq; 803 804 for(i = 0; i < request->nktypes; i++) { 805 enctype = request->ktype[i]; 806 if (!krb5_c_valid_enctype(enctype)) 807 continue; 808 else { 809 pkiDebug("KDC picked etype = %d\n", enctype); 810 break; 811 } 812 } 813 814 if (i == request->nktypes) { 815 retval = KRB5KDC_ERR_ETYPE_NOSUPP; 816 goto cleanup; 817 } 818 819 init_krb5_pa_pk_as_rep(&rep); 820 if (rep == NULL) { 821 retval = ENOMEM; 822 goto cleanup; 823 } 824 /* let's assume it's RSA. we'll reset it to DH if needed */ 825 rep->choice = choice_pa_pk_as_rep_encKeyPack; 826 827 if (reqctx->rcv_auth_pack != NULL && 828 reqctx->rcv_auth_pack->clientPublicValue.length > 0) { 829 rep->choice = choice_pa_pk_as_rep_dhInfo; 830 831 pkiDebug("received DH key delivery AS REQ\n"); 832 retval = server_process_dh(context, plgctx->cryptoctx, 833 reqctx->cryptoctx, plgctx->idctx, 834 &dh_pubkey, &dh_pubkey_len, 835 &server_key, &server_key_len); 836 if (retval) { 837 pkiDebug("failed to process/create dh parameters\n"); 838 goto cleanup; 839 } 840 841 /* 842 * This is DH, so don't generate the key until after we 843 * encode the reply, because the encoded reply is needed 844 * to generate the key in some cases. 845 */ 846 847 dhkey_info.subjectPublicKey.length = dh_pubkey_len; 848 dhkey_info.subjectPublicKey.data = (char *)dh_pubkey; 849 dhkey_info.nonce = request->nonce; 850 dhkey_info.dhKeyExpiration = 0; 851 852 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, 853 &encoded_dhkey_info); 854 if (retval) { 855 pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); 856 goto cleanup; 857 } 858 #ifdef DEBUG_ASN1 859 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, 860 encoded_dhkey_info->length, 861 "/tmp/kdc_dh_key_info"); 862 #endif 863 864 retval = cms_signeddata_create(context, plgctx->cryptoctx, 865 reqctx->cryptoctx, plgctx->idctx, 866 CMS_SIGN_SERVER, 867 (unsigned char *) 868 encoded_dhkey_info->data, 869 encoded_dhkey_info->length, 870 (unsigned char **) 871 &rep->u.dh_Info.dhSignedData.data, 872 &rep->u.dh_Info.dhSignedData.length); 873 if (retval) { 874 pkiDebug("failed to create pkcs7 signed data\n"); 875 goto cleanup; 876 } 877 878 } else { 879 pkiDebug("received RSA key delivery AS REQ\n"); 880 881 init_krb5_reply_key_pack(&key_pack); 882 if (key_pack == NULL) { 883 retval = ENOMEM; 884 goto cleanup; 885 } 886 887 retval = krb5_c_make_random_key(context, enctype, &key_pack->replyKey); 888 if (retval) { 889 pkiDebug("unable to make a session key\n"); 890 goto cleanup; 891 } 892 893 retval = krb5_c_make_checksum(context, 0, &key_pack->replyKey, 894 KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 895 req_pkt, &key_pack->asChecksum); 896 if (retval) { 897 pkiDebug("unable to calculate AS REQ checksum\n"); 898 goto cleanup; 899 } 900 #ifdef DEBUG_CKSUM 901 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); 902 print_buffer(req_pkt->data, req_pkt->length); 903 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); 904 print_buffer(key_pack->asChecksum.contents, 905 key_pack->asChecksum.length); 906 pkiDebug("encrypting key (%d)\n", key_pack->replyKey.length); 907 print_buffer(key_pack->replyKey.contents, key_pack->replyKey.length); 908 #endif 909 910 retval = k5int_encode_krb5_reply_key_pack(key_pack, 911 &encoded_key_pack); 912 if (retval) { 913 pkiDebug("failed to encode reply_key_pack\n"); 914 goto cleanup; 915 } 916 917 rep->choice = choice_pa_pk_as_rep_encKeyPack; 918 retval = cms_envelopeddata_create(context, plgctx->cryptoctx, 919 reqctx->cryptoctx, plgctx->idctx, 920 padata->pa_type, 921 (unsigned char *) 922 encoded_key_pack->data, 923 encoded_key_pack->length, 924 (unsigned char **) 925 &rep->u.encKeyPack.data, 926 &rep->u.encKeyPack.length); 927 if (retval) { 928 pkiDebug("failed to create pkcs7 enveloped data: %s\n", 929 error_message(retval)); 930 goto cleanup; 931 } 932 #ifdef DEBUG_ASN1 933 print_buffer_bin((unsigned char *)encoded_key_pack->data, 934 encoded_key_pack->length, 935 "/tmp/kdc_key_pack"); 936 print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length, 937 "/tmp/kdc_enc_key_pack"); 938 #endif 939 940 retval = cb->replace_reply_key(context, rock, &key_pack->replyKey, 941 FALSE); 942 if (retval) 943 goto cleanup; 944 } 945 946 if (rep->choice == choice_pa_pk_as_rep_dhInfo && 947 ((reqctx->rcv_auth_pack != NULL && 948 reqctx->rcv_auth_pack->supportedKDFs != NULL))) { 949 950 /* If using the alg-agility KDF, put the algorithm in the reply 951 * before encoding it. 952 */ 953 if (reqctx->rcv_auth_pack != NULL && 954 reqctx->rcv_auth_pack->supportedKDFs != NULL) { 955 retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs, 956 &(rep->u.dh_Info.kdfID)); 957 if (retval) { 958 pkiDebug("pkinit_pick_kdf_alg failed: %s\n", 959 error_message(retval)); 960 goto cleanup; 961 } 962 } 963 } 964 965 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); 966 if (retval) { 967 pkiDebug("failed to encode AS_REP\n"); 968 goto cleanup; 969 } 970 #ifdef DEBUG_ASN1 971 if (out_data != NULL) 972 print_buffer_bin((unsigned char *)out_data->data, out_data->length, 973 "/tmp/kdc_as_rep"); 974 #endif 975 976 /* If this is DH, we haven't computed the key yet, so do it now. */ 977 if (rep->choice == choice_pa_pk_as_rep_dhInfo) { 978 979 /* If mutually supported KDFs were found, use the algorithm agility 980 * KDF. */ 981 if (rep->u.dh_Info.kdfID) { 982 secret.data = (char *)server_key; 983 secret.length = server_key_len; 984 985 retval = pkinit_alg_agility_kdf(context, &secret, 986 rep->u.dh_Info.kdfID, 987 request->client, request->server, 988 enctype, req_pkt, out_data, 989 &reply_key); 990 if (retval) { 991 pkiDebug("pkinit_alg_agility_kdf failed: %s\n", 992 error_message(retval)); 993 goto cleanup; 994 } 995 996 /* Otherwise, use the older octetstring2key() function */ 997 } else { 998 retval = pkinit_octetstring2key(context, enctype, server_key, 999 server_key_len, &reply_key); 1000 if (retval) { 1001 pkiDebug("pkinit_octetstring2key failed: %s\n", 1002 error_message(retval)); 1003 goto cleanup; 1004 } 1005 } 1006 retval = cb->replace_reply_key(context, rock, &reply_key, FALSE); 1007 if (retval) 1008 goto cleanup; 1009 } 1010 1011 *send_pa = malloc(sizeof(krb5_pa_data)); 1012 if (*send_pa == NULL) { 1013 retval = ENOMEM; 1014 free(out_data->data); 1015 free(out_data); 1016 out_data = NULL; 1017 goto cleanup; 1018 } 1019 (*send_pa)->magic = KV5M_PA_DATA; 1020 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; 1021 (*send_pa)->length = out_data->length; 1022 (*send_pa)->contents = (krb5_octet *) out_data->data; 1023 1024 cleanup: 1025 free(scratch.data); 1026 free(out_data); 1027 if (encoded_dhkey_info != NULL) 1028 krb5_free_data(context, encoded_dhkey_info); 1029 if (encoded_key_pack != NULL) 1030 krb5_free_data(context, encoded_key_pack); 1031 free(dh_pubkey); 1032 free(server_key); 1033 free_krb5_pa_pk_as_req(&reqp); 1034 free_krb5_pa_pk_as_rep(&rep); 1035 free_krb5_reply_key_pack(&key_pack); 1036 krb5_free_keyblock_contents(context, &reply_key); 1037 1038 if (retval) 1039 pkiDebug("pkinit_verify_padata failure"); 1040 1041 return retval; 1042 } 1043 1044 static int 1045 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) 1046 { 1047 if (patype == KRB5_PADATA_PKINIT_KX) 1048 return PA_INFO; 1049 /* PKINIT does not normally set the hw-authent ticket flag, but a 1050 * certauth module can cause it to do so. */ 1051 return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE; 1052 } 1053 1054 static krb5_preauthtype supported_server_pa_types[] = { 1055 KRB5_PADATA_PK_AS_REQ, 1056 KRB5_PADATA_PKINIT_KX, 1057 0 1058 }; 1059 1060 static void 1061 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1062 { 1063 /* 1064 * There is nothing currently allocated by pkinit_init_kdc_profile() 1065 * which needs to be freed here. 1066 */ 1067 } 1068 1069 static krb5_error_code 1070 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1071 { 1072 krb5_error_code retval; 1073 char *eku_string = NULL, *ocsp_check = NULL; 1074 1075 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname); 1076 retval = pkinit_kdcdefault_string(context, plgctx->realmname, 1077 KRB5_CONF_PKINIT_IDENTITY, 1078 &plgctx->idopts->identity); 1079 if (retval != 0 || NULL == plgctx->idopts->identity) { 1080 retval = EINVAL; 1081 krb5_set_error_message(context, retval, 1082 _("No pkinit_identity supplied for realm %s"), 1083 plgctx->realmname); 1084 goto errout; 1085 } 1086 1087 retval = pkinit_kdcdefault_strings(context, plgctx->realmname, 1088 KRB5_CONF_PKINIT_ANCHORS, 1089 &plgctx->idopts->anchors); 1090 if (retval != 0 || NULL == plgctx->idopts->anchors) { 1091 retval = EINVAL; 1092 krb5_set_error_message(context, retval, 1093 _("No pkinit_anchors supplied for realm %s"), 1094 plgctx->realmname); 1095 goto errout; 1096 } 1097 1098 pkinit_kdcdefault_strings(context, plgctx->realmname, 1099 KRB5_CONF_PKINIT_POOL, 1100 &plgctx->idopts->intermediates); 1101 1102 pkinit_kdcdefault_strings(context, plgctx->realmname, 1103 KRB5_CONF_PKINIT_REVOKE, 1104 &plgctx->idopts->crls); 1105 1106 pkinit_kdcdefault_string(context, plgctx->realmname, 1107 KRB5_CONF_PKINIT_KDC_OCSP, 1108 &ocsp_check); 1109 if (ocsp_check != NULL) { 1110 free(ocsp_check); 1111 retval = ENOTSUP; 1112 krb5_set_error_message(context, retval, 1113 _("OCSP is not supported: (realm: %s)"), 1114 plgctx->realmname); 1115 goto errout; 1116 } 1117 1118 pkinit_kdcdefault_integer(context, plgctx->realmname, 1119 KRB5_CONF_PKINIT_DH_MIN_BITS, 1120 PKINIT_DEFAULT_DH_MIN_BITS, 1121 &plgctx->opts->dh_min_bits); 1122 if (plgctx->opts->dh_min_bits < PKINIT_DH_MIN_CONFIG_BITS) { 1123 pkiDebug("%s: invalid value (%d < %d) for pkinit_dh_min_bits, " 1124 "using default value (%d) instead\n", __FUNCTION__, 1125 plgctx->opts->dh_min_bits, PKINIT_DH_MIN_CONFIG_BITS, 1126 PKINIT_DEFAULT_DH_MIN_BITS); 1127 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; 1128 } 1129 1130 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1131 KRB5_CONF_PKINIT_ALLOW_UPN, 1132 0, &plgctx->opts->allow_upn); 1133 1134 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1135 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING, 1136 0, &plgctx->opts->require_crl_checking); 1137 1138 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1139 KRB5_CONF_PKINIT_REQUIRE_FRESHNESS, 1140 0, &plgctx->opts->require_freshness); 1141 1142 pkinit_kdcdefault_string(context, plgctx->realmname, 1143 KRB5_CONF_PKINIT_EKU_CHECKING, 1144 &eku_string); 1145 if (eku_string != NULL) { 1146 if (strcasecmp(eku_string, "kpClientAuth") == 0) { 1147 plgctx->opts->require_eku = 1; 1148 plgctx->opts->accept_secondary_eku = 0; 1149 } else if (strcasecmp(eku_string, "scLogin") == 0) { 1150 plgctx->opts->require_eku = 1; 1151 plgctx->opts->accept_secondary_eku = 1; 1152 } else if (strcasecmp(eku_string, "none") == 0) { 1153 plgctx->opts->require_eku = 0; 1154 plgctx->opts->accept_secondary_eku = 0; 1155 } else { 1156 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", 1157 __FUNCTION__, eku_string); 1158 } 1159 free(eku_string); 1160 } 1161 1162 pkinit_kdcdefault_strings(context, plgctx->realmname, 1163 KRB5_CONF_PKINIT_INDICATOR, 1164 &plgctx->auth_indicators); 1165 1166 return 0; 1167 errout: 1168 pkinit_fini_kdc_profile(context, plgctx); 1169 return retval; 1170 } 1171 1172 static pkinit_kdc_context 1173 pkinit_find_realm_context(krb5_context context, 1174 krb5_kdcpreauth_moddata moddata, 1175 krb5_principal princ) 1176 { 1177 int i; 1178 pkinit_kdc_context *realm_contexts; 1179 1180 if (moddata == NULL) 1181 return NULL; 1182 1183 realm_contexts = moddata->realm_contexts; 1184 if (realm_contexts == NULL) 1185 return NULL; 1186 1187 for (i = 0; realm_contexts[i] != NULL; i++) { 1188 pkinit_kdc_context p = realm_contexts[i]; 1189 1190 if ((p->realmname_len == princ->realm.length) && 1191 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) { 1192 pkiDebug("%s: returning context at %p for realm '%s'\n", 1193 __FUNCTION__, p, p->realmname); 1194 return p; 1195 } 1196 } 1197 pkiDebug("%s: unable to find realm context for realm '%.*s'\n", 1198 __FUNCTION__, princ->realm.length, princ->realm.data); 1199 return NULL; 1200 } 1201 1202 static int 1203 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname, 1204 pkinit_kdc_context *pplgctx) 1205 { 1206 krb5_error_code retval = ENOMEM; 1207 pkinit_kdc_context plgctx = NULL; 1208 1209 *pplgctx = NULL; 1210 1211 plgctx = calloc(1, sizeof(*plgctx)); 1212 if (plgctx == NULL) 1213 goto errout; 1214 1215 pkiDebug("%s: initializing context at %p for realm '%s'\n", 1216 __FUNCTION__, plgctx, realmname); 1217 memset(plgctx, 0, sizeof(*plgctx)); 1218 plgctx->magic = PKINIT_CTX_MAGIC; 1219 1220 plgctx->realmname = strdup(realmname); 1221 if (plgctx->realmname == NULL) 1222 goto errout; 1223 plgctx->realmname_len = strlen(plgctx->realmname); 1224 1225 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx); 1226 if (retval) 1227 goto errout; 1228 1229 retval = pkinit_init_plg_opts(&plgctx->opts); 1230 if (retval) 1231 goto errout; 1232 1233 retval = pkinit_init_identity_crypto(&plgctx->idctx); 1234 if (retval) 1235 goto errout; 1236 1237 retval = pkinit_init_identity_opts(&plgctx->idopts); 1238 if (retval) 1239 goto errout; 1240 1241 retval = pkinit_init_kdc_profile(context, plgctx); 1242 if (retval) 1243 goto errout; 1244 1245 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL, 1246 plgctx->idopts, plgctx->idctx, 1247 NULL, NULL, NULL); 1248 if (retval) 1249 goto errout; 1250 retval = pkinit_identity_prompt(context, plgctx->cryptoctx, NULL, 1251 plgctx->idopts, plgctx->idctx, 1252 NULL, NULL, 0, NULL); 1253 if (retval) 1254 goto errout; 1255 1256 pkiDebug("%s: returning context at %p for realm '%s'\n", 1257 __FUNCTION__, plgctx, realmname); 1258 *pplgctx = plgctx; 1259 retval = 0; 1260 1261 errout: 1262 if (retval) 1263 pkinit_server_plugin_fini_realm(context, plgctx); 1264 1265 return retval; 1266 } 1267 1268 static krb5_error_code 1269 pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata, 1270 const uint8_t *cert, size_t cert_len, 1271 krb5_const_principal princ, const void *opts, 1272 const struct _krb5_db_entry_new *db_entry, 1273 char ***authinds_out) 1274 { 1275 krb5_error_code ret; 1276 int valid_san; 1277 const struct certauth_req_opts *req_opts = opts; 1278 1279 *authinds_out = NULL; 1280 1281 ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx, 1282 req_opts->cb, req_opts->rock, princ, &valid_san); 1283 if (ret == ENOENT) 1284 return KRB5_PLUGIN_NO_HANDLE; 1285 else if (ret) 1286 return ret; 1287 1288 if (!valid_san) { 1289 TRACE_PKINIT_SERVER_SAN_REJECT(context); 1290 return KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 1291 } 1292 1293 return 0; 1294 } 1295 1296 static krb5_error_code 1297 pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata, 1298 const uint8_t *cert, size_t cert_len, 1299 krb5_const_principal princ, const void *opts, 1300 const struct _krb5_db_entry_new *db_entry, 1301 char ***authinds_out) 1302 { 1303 krb5_error_code ret; 1304 int valid_eku; 1305 const struct certauth_req_opts *req_opts = opts; 1306 1307 *authinds_out = NULL; 1308 1309 /* Verify the client EKU. */ 1310 ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx, 1311 &valid_eku); 1312 if (ret) 1313 return ret; 1314 1315 if (!valid_eku) { 1316 TRACE_PKINIT_SERVER_EKU_REJECT(context); 1317 return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; 1318 } 1319 1320 return KRB5_PLUGIN_NO_HANDLE; 1321 } 1322 1323 static krb5_error_code 1324 certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver, 1325 krb5_plugin_vtable vtable) 1326 { 1327 krb5_certauth_vtable vt; 1328 1329 if (maj_ver != 1) 1330 return KRB5_PLUGIN_VER_NOTSUPP; 1331 vt = (krb5_certauth_vtable)vtable; 1332 vt->name = "pkinit_san"; 1333 vt->authorize = pkinit_san_authorize; 1334 return 0; 1335 } 1336 1337 static krb5_error_code 1338 certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver, 1339 krb5_plugin_vtable vtable) 1340 { 1341 krb5_certauth_vtable vt; 1342 1343 if (maj_ver != 1) 1344 return KRB5_PLUGIN_VER_NOTSUPP; 1345 vt = (krb5_certauth_vtable)vtable; 1346 vt->name = "pkinit_eku"; 1347 vt->authorize = pkinit_eku_authorize; 1348 return 0; 1349 } 1350 1351 /* 1352 * Do certificate auth based on a match expression in the pkinit_cert_match 1353 * attribute string. An expression should be in the same form as those used 1354 * for the pkinit_cert_match configuration option. 1355 */ 1356 static krb5_error_code 1357 dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata, 1358 const uint8_t *cert, size_t cert_len, 1359 krb5_const_principal princ, const void *opts, 1360 const struct _krb5_db_entry_new *db_entry, 1361 char ***authinds_out) 1362 { 1363 krb5_error_code ret; 1364 const struct certauth_req_opts *req_opts = opts; 1365 char *pattern; 1366 krb5_boolean matched; 1367 1368 *authinds_out = NULL; 1369 1370 /* Fetch the matching pattern. Pass if it isn't specified. */ 1371 ret = req_opts->cb->get_string(context, req_opts->rock, 1372 "pkinit_cert_match", &pattern); 1373 if (ret) 1374 return ret; 1375 if (pattern == NULL) 1376 return KRB5_PLUGIN_NO_HANDLE; 1377 1378 /* Check the certificate against the match expression. */ 1379 ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx, 1380 req_opts->reqctx->cryptoctx, pattern, 1381 &matched); 1382 req_opts->cb->free_string(context, req_opts->rock, pattern); 1383 if (ret) 1384 return ret; 1385 return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH; 1386 } 1387 1388 static krb5_error_code 1389 certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver, 1390 krb5_plugin_vtable vtable) 1391 { 1392 krb5_certauth_vtable vt; 1393 1394 if (maj_ver != 1) 1395 return KRB5_PLUGIN_VER_NOTSUPP; 1396 vt = (krb5_certauth_vtable)vtable; 1397 vt->name = "dbmatch"; 1398 vt->authorize = dbmatch_authorize; 1399 return 0; 1400 } 1401 1402 static krb5_error_code 1403 load_certauth_plugins(krb5_context context, const char *const *realmnames, 1404 certauth_handle **handle_out) 1405 { 1406 krb5_error_code ret; 1407 krb5_plugin_initvt_fn *modules = NULL, *mod; 1408 certauth_handle *list = NULL, h; 1409 size_t count; 1410 1411 /* Register the builtin modules. */ 1412 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, 1413 "pkinit_san", certauth_pkinit_san_initvt); 1414 if (ret) 1415 goto cleanup; 1416 1417 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, 1418 "pkinit_eku", certauth_pkinit_eku_initvt); 1419 if (ret) 1420 goto cleanup; 1421 1422 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch", 1423 certauth_dbmatch_initvt); 1424 if (ret) 1425 goto cleanup; 1426 1427 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules); 1428 if (ret) 1429 goto cleanup; 1430 1431 /* Allocate handle list. */ 1432 for (count = 0; modules[count]; count++); 1433 list = k5calloc(count + 1, sizeof(*list), &ret); 1434 if (list == NULL) 1435 goto cleanup; 1436 1437 /* Initialize each module, ignoring ones that fail. */ 1438 count = 0; 1439 for (mod = modules; *mod != NULL; mod++) { 1440 h = k5calloc(1, sizeof(*h), &ret); 1441 if (h == NULL) 1442 goto cleanup; 1443 1444 ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt); 1445 if (ret) { 1446 TRACE_CERTAUTH_VTINIT_FAIL(context, ret); 1447 free(h); 1448 continue; 1449 } 1450 h->moddata = NULL; 1451 if (h->vt.init_ex != NULL) 1452 ret = h->vt.init_ex(context, realmnames, &h->moddata); 1453 else if (h->vt.init != NULL) 1454 ret = h->vt.init(context, &h->moddata); 1455 if (ret) { 1456 TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret); 1457 free(h); 1458 continue; 1459 } 1460 list[count++] = h; 1461 list[count] = NULL; 1462 } 1463 list[count] = NULL; 1464 1465 ret = 0; 1466 *handle_out = list; 1467 list = NULL; 1468 1469 cleanup: 1470 k5_plugin_free_modules(context, modules); 1471 free_certauth_handles(context, list); 1472 return ret; 1473 } 1474 1475 static int 1476 pkinit_server_plugin_init(krb5_context context, 1477 krb5_kdcpreauth_moddata *moddata_out, 1478 const char **realmnames) 1479 { 1480 krb5_error_code retval = ENOMEM; 1481 pkinit_kdc_context plgctx, *realm_contexts = NULL; 1482 certauth_handle *certauth_modules = NULL; 1483 krb5_kdcpreauth_moddata moddata; 1484 size_t i, j; 1485 size_t numrealms; 1486 1487 retval = pkinit_accessor_init(); 1488 if (retval) 1489 return retval; 1490 1491 /* Determine how many realms we may need to support */ 1492 for (i = 0; realmnames[i] != NULL; i++) {}; 1493 numrealms = i; 1494 1495 realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context)); 1496 if (realm_contexts == NULL) 1497 return ENOMEM; 1498 1499 for (i = 0, j = 0; i < numrealms; i++) { 1500 TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]); 1501 krb5_clear_error_message(context); 1502 retval = pkinit_server_plugin_init_realm(context, realmnames[i], 1503 &plgctx); 1504 if (retval) 1505 TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval); 1506 else 1507 realm_contexts[j++] = plgctx; 1508 } 1509 1510 if (j == 0) { 1511 if (numrealms == 1) { 1512 k5_prependmsg(context, retval, "PKINIT initialization failed"); 1513 } else { 1514 retval = EINVAL; 1515 k5_setmsg(context, retval, 1516 _("No realms configured correctly for pkinit support")); 1517 } 1518 goto errout; 1519 } 1520 1521 retval = load_certauth_plugins(context, realmnames, &certauth_modules); 1522 if (retval) 1523 goto errout; 1524 1525 moddata = k5calloc(1, sizeof(*moddata), &retval); 1526 if (moddata == NULL) 1527 goto errout; 1528 moddata->realm_contexts = realm_contexts; 1529 moddata->certauth_modules = certauth_modules; 1530 *moddata_out = moddata; 1531 pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata); 1532 return 0; 1533 1534 errout: 1535 free_realm_contexts(context, realm_contexts); 1536 free_certauth_handles(context, certauth_modules); 1537 return retval; 1538 } 1539 1540 static void 1541 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx) 1542 { 1543 char **sp; 1544 1545 if (plgctx == NULL) 1546 return; 1547 1548 pkinit_fini_kdc_profile(context, plgctx); 1549 pkinit_fini_identity_opts(plgctx->idopts); 1550 pkinit_fini_identity_crypto(plgctx->idctx); 1551 pkinit_fini_plg_crypto(plgctx->cryptoctx); 1552 pkinit_fini_plg_opts(plgctx->opts); 1553 for (sp = plgctx->auth_indicators; sp != NULL && *sp != NULL; sp++) 1554 free(*sp); 1555 free(plgctx->auth_indicators); 1556 free(plgctx->realmname); 1557 free(plgctx); 1558 } 1559 1560 static void 1561 pkinit_server_plugin_fini(krb5_context context, 1562 krb5_kdcpreauth_moddata moddata) 1563 { 1564 if (moddata == NULL) 1565 return; 1566 free_realm_contexts(context, moddata->realm_contexts); 1567 free_certauth_handles(context, moddata->certauth_modules); 1568 free(moddata); 1569 } 1570 1571 static krb5_error_code 1572 pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx) 1573 { 1574 krb5_error_code retval = ENOMEM; 1575 pkinit_kdc_req_context reqctx = NULL; 1576 1577 reqctx = malloc(sizeof(*reqctx)); 1578 if (reqctx == NULL) 1579 return retval; 1580 memset(reqctx, 0, sizeof(*reqctx)); 1581 reqctx->magic = PKINIT_CTX_MAGIC; 1582 1583 retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 1584 if (retval) 1585 goto cleanup; 1586 reqctx->rcv_auth_pack = NULL; 1587 1588 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); 1589 *ctx = reqctx; 1590 retval = 0; 1591 cleanup: 1592 if (retval) 1593 pkinit_fini_kdc_req_context(context, reqctx); 1594 1595 return retval; 1596 } 1597 1598 static void 1599 pkinit_fini_kdc_req_context(krb5_context context, void *ctx) 1600 { 1601 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx; 1602 1603 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) { 1604 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx); 1605 return; 1606 } 1607 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx); 1608 1609 pkinit_fini_req_crypto(reqctx->cryptoctx); 1610 if (reqctx->rcv_auth_pack != NULL) 1611 free_krb5_auth_pack(&reqctx->rcv_auth_pack); 1612 1613 free(reqctx); 1614 } 1615 1616 static void 1617 pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata, 1618 krb5_kdcpreauth_modreq modreq) 1619 { 1620 pkinit_fini_kdc_req_context(context, modreq); 1621 } 1622 1623 krb5_error_code 1624 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1625 krb5_plugin_vtable vtable); 1626 1627 krb5_error_code 1628 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1629 krb5_plugin_vtable vtable) 1630 { 1631 krb5_kdcpreauth_vtable vt; 1632 1633 if (maj_ver != 1) 1634 return KRB5_PLUGIN_VER_NOTSUPP; 1635 vt = (krb5_kdcpreauth_vtable)vtable; 1636 vt->name = "pkinit"; 1637 vt->pa_type_list = supported_server_pa_types; 1638 vt->init = pkinit_server_plugin_init; 1639 vt->fini = pkinit_server_plugin_fini; 1640 vt->flags = pkinit_server_get_flags; 1641 vt->edata = pkinit_server_get_edata; 1642 vt->verify = pkinit_server_verify_padata; 1643 vt->return_padata = pkinit_server_return_padata; 1644 vt->free_modreq = pkinit_free_modreq; 1645 return 0; 1646 } 1647