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 size_t 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 size_t 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 size_t 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 krb5_pk_authenticator *pka; 432 pkinit_kdc_context plgctx = NULL; 433 pkinit_kdc_req_context reqctx = NULL; 434 krb5_checksum cksum = {0, 0, 0, NULL}; 435 krb5_data *der_req = NULL; 436 krb5_data k5data; 437 int is_signed = 1; 438 krb5_pa_data **e_data = NULL; 439 krb5_kdcpreauth_modreq modreq = NULL; 440 krb5_boolean valid_freshness_token = FALSE, hwauth = FALSE; 441 char **sp; 442 443 pkiDebug("pkinit_verify_padata: entered!\n"); 444 if (data == NULL || data->length <= 0 || data->contents == NULL) { 445 (*respond)(arg, EINVAL, NULL, NULL, NULL); 446 return; 447 } 448 449 450 if (moddata == NULL) { 451 (*respond)(arg, EINVAL, NULL, NULL, NULL); 452 return; 453 } 454 455 plgctx = pkinit_find_realm_context(context, moddata, request->server); 456 if (plgctx == NULL) { 457 (*respond)(arg, EINVAL, NULL, NULL, NULL); 458 return; 459 } 460 461 #ifdef DEBUG_ASN1 462 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); 463 #endif 464 /* create a per-request context */ 465 retval = pkinit_init_kdc_req_context(context, &reqctx); 466 if (retval) 467 goto cleanup; 468 reqctx->pa_type = data->pa_type; 469 470 PADATA_TO_KRB5DATA(data, &k5data); 471 472 if (data->pa_type != KRB5_PADATA_PK_AS_REQ) { 473 pkiDebug("unrecognized pa_type = %d\n", data->pa_type); 474 retval = EINVAL; 475 goto cleanup; 476 } 477 478 TRACE_PKINIT_SERVER_PADATA_VERIFY(context); 479 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); 480 if (retval) { 481 pkiDebug("decode_krb5_pa_pk_as_req failed\n"); 482 goto cleanup; 483 } 484 #ifdef DEBUG_ASN1 485 print_buffer_bin(reqp->signedAuthPack.data, reqp->signedAuthPack.length, 486 "/tmp/kdc_signed_data"); 487 #endif 488 retval = cms_signeddata_verify(context, plgctx->cryptoctx, 489 reqctx->cryptoctx, plgctx->idctx, 490 CMS_SIGN_CLIENT, 491 plgctx->opts->require_crl_checking, 492 (unsigned char *)reqp->signedAuthPack.data, 493 reqp->signedAuthPack.length, 494 (unsigned char **)&authp_data.data, 495 &authp_data.length, 496 (unsigned char **)&krb5_authz.data, 497 &krb5_authz.length, &is_signed); 498 if (retval) { 499 TRACE_PKINIT_SERVER_PADATA_VERIFY_FAIL(context); 500 goto cleanup; 501 } 502 if (is_signed) { 503 retval = authorize_cert(context, moddata->certauth_modules, plgctx, 504 reqctx, cb, rock, request->client, &hwauth); 505 if (retval) 506 goto cleanup; 507 508 } else { /* !is_signed */ 509 if (!krb5_principal_compare(context, request->client, 510 krb5_anonymous_principal())) { 511 retval = KRB5KDC_ERR_PREAUTH_FAILED; 512 krb5_set_error_message(context, retval, 513 _("Pkinit request not signed, but client " 514 "not anonymous.")); 515 goto cleanup; 516 } 517 } 518 #ifdef DEBUG_ASN1 519 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); 520 #endif 521 522 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); 523 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); 524 if (retval) { 525 pkiDebug("failed to decode krb5_auth_pack\n"); 526 goto cleanup; 527 } 528 pka = &auth_pack->pkAuthenticator; 529 530 retval = krb5_check_clockskew(context, pka->ctime); 531 if (retval) 532 goto cleanup; 533 534 /* check dh parameters */ 535 if (auth_pack->clientPublicValue.length > 0) { 536 retval = server_check_dh(context, plgctx->cryptoctx, 537 reqctx->cryptoctx, plgctx->idctx, 538 &auth_pack->clientPublicValue, 539 plgctx->opts->dh_min_bits); 540 if (retval) { 541 pkiDebug("bad dh parameters\n"); 542 goto cleanup; 543 } 544 } else if (!is_signed) { 545 /*Anonymous pkinit requires DH*/ 546 retval = KRB5KDC_ERR_PREAUTH_FAILED; 547 krb5_set_error_message(context, retval, 548 _("Anonymous pkinit without DH public " 549 "value not supported.")); 550 goto cleanup; 551 } 552 der_req = cb->request_body(context, rock); 553 554 retval = crypto_verify_checksums(context, der_req, &pka->paChecksum, 555 pka->paChecksum2); 556 if (retval) 557 goto cleanup; 558 559 if (pka->freshnessToken != NULL) { 560 retval = cb->check_freshness_token(context, rock, pka->freshnessToken); 561 if (retval) 562 goto cleanup; 563 valid_freshness_token = TRUE; 564 } 565 566 /* check if kdcPkId present and match KDC's subjectIdentifier */ 567 if (reqp->kdcPkId.data != NULL) { 568 int valid_kdcPkId = 0; 569 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, 570 reqctx->cryptoctx, plgctx->idctx, 571 (unsigned char *)reqp->kdcPkId.data, 572 reqp->kdcPkId.length, &valid_kdcPkId); 573 if (retval) 574 goto cleanup; 575 if (!valid_kdcPkId) { 576 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert; " 577 "RFC says to ignore and proceed\n"); 578 } 579 } 580 /* remember the decoded auth_pack for verify_padata routine */ 581 reqctx->rcv_auth_pack = auth_pack; 582 auth_pack = NULL; 583 584 if (is_signed) { 585 retval = check_log_freshness(context, plgctx, request, 586 valid_freshness_token); 587 if (retval) 588 goto cleanup; 589 } 590 591 if (is_signed && plgctx->auth_indicators != NULL) { 592 /* Assert configured authentication indicators. */ 593 for (sp = plgctx->auth_indicators; *sp != NULL; sp++) { 594 retval = cb->add_auth_indicator(context, rock, *sp); 595 if (retval) 596 goto cleanup; 597 } 598 } 599 600 /* remember to set the PREAUTH flag in the reply */ 601 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 602 if (hwauth) 603 enc_tkt_reply->flags |= TKT_FLG_HW_AUTH; 604 modreq = (krb5_kdcpreauth_modreq)reqctx; 605 reqctx = NULL; 606 607 cleanup: 608 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { 609 pkiDebug("pkinit_verify_padata failed: creating e-data\n"); 610 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, 611 plgctx->idctx, plgctx->opts, retval, &e_data)) 612 pkiDebug("pkinit_create_edata failed\n"); 613 } 614 615 free_krb5_pa_pk_as_req(&reqp); 616 free(cksum.contents); 617 free(authp_data.data); 618 free(krb5_authz.data); 619 if (reqctx != NULL) 620 pkinit_fini_kdc_req_context(context, reqctx); 621 free_krb5_auth_pack(&auth_pack); 622 623 (*respond)(arg, retval, modreq, e_data, NULL); 624 } 625 static krb5_error_code 626 return_pkinit_kx(krb5_context context, krb5_kdc_req *request, 627 krb5_kdc_rep *reply, krb5_keyblock *encrypting_key, 628 krb5_pa_data **out_padata) 629 { 630 krb5_error_code ret = 0; 631 krb5_keyblock *session = reply->ticket->enc_part2->session; 632 krb5_keyblock *new_session = NULL; 633 krb5_pa_data *pa = NULL; 634 krb5_enc_data enc; 635 krb5_data *scratch = NULL; 636 637 *out_padata = NULL; 638 enc.ciphertext.data = NULL; 639 if (!krb5_principal_compare(context, request->client, 640 krb5_anonymous_principal())) 641 return 0; 642 /* 643 * The KDC contribution key needs to be a fresh key of an enctype supported 644 * by the client and server. The existing session key meets these 645 * requirements so we use it. 646 */ 647 ret = krb5_c_fx_cf2_simple(context, session, "PKINIT", 648 encrypting_key, "KEYEXCHANGE", 649 &new_session); 650 if (ret) 651 goto cleanup; 652 ret = encode_krb5_encryption_key( session, &scratch); 653 if (ret) 654 goto cleanup; 655 ret = krb5_encrypt_helper(context, encrypting_key, 656 KRB5_KEYUSAGE_PA_PKINIT_KX, scratch, &enc); 657 if (ret) 658 goto cleanup; 659 memset(scratch->data, 0, scratch->length); 660 krb5_free_data(context, scratch); 661 scratch = NULL; 662 ret = encode_krb5_enc_data(&enc, &scratch); 663 if (ret) 664 goto cleanup; 665 pa = malloc(sizeof(krb5_pa_data)); 666 if (pa == NULL) { 667 ret = ENOMEM; 668 goto cleanup; 669 } 670 pa->pa_type = KRB5_PADATA_PKINIT_KX; 671 pa->length = scratch->length; 672 pa->contents = (krb5_octet *) scratch->data; 673 *out_padata = pa; 674 scratch->data = NULL; 675 memset(session->contents, 0, session->length); 676 krb5_free_keyblock_contents(context, session); 677 *session = *new_session; 678 new_session->contents = NULL; 679 cleanup: 680 krb5_free_data_contents(context, &enc.ciphertext); 681 krb5_free_keyblock(context, new_session); 682 krb5_free_data(context, scratch); 683 return ret; 684 } 685 686 static krb5_error_code 687 pkinit_pick_kdf_alg(krb5_context context, krb5_data **kdf_list, 688 krb5_data **alg_oid) 689 { 690 krb5_error_code retval = 0; 691 krb5_data *req_oid = NULL; 692 const krb5_data *supp_oid = NULL; 693 krb5_data *tmp_oid = NULL; 694 size_t i, j = 0; 695 696 /* if we don't find a match, return NULL value */ 697 *alg_oid = NULL; 698 699 /* for each of the OIDs that the server supports... */ 700 for (i = 0; NULL != (supp_oid = supported_kdf_alg_ids[i]); i++) { 701 /* if the requested OID is in the client's list, use it. */ 702 for (j = 0; NULL != (req_oid = kdf_list[j]); j++) { 703 if ((req_oid->length == supp_oid->length) && 704 (0 == memcmp(req_oid->data, supp_oid->data, req_oid->length))) { 705 tmp_oid = k5alloc(sizeof(krb5_data), &retval); 706 if (retval) 707 goto cleanup; 708 tmp_oid->data = k5memdup(supp_oid->data, supp_oid->length, 709 &retval); 710 if (retval) 711 goto cleanup; 712 tmp_oid->length = supp_oid->length; 713 *alg_oid = tmp_oid; 714 /* don't free the OID in clean-up if we are returning it */ 715 tmp_oid = NULL; 716 goto cleanup; 717 } 718 } 719 } 720 cleanup: 721 if (tmp_oid) 722 krb5_free_data(context, tmp_oid); 723 return retval; 724 } 725 726 static krb5_error_code 727 pkinit_server_return_padata(krb5_context context, 728 krb5_pa_data * padata, 729 krb5_data *req_pkt, 730 krb5_kdc_req * request, 731 krb5_kdc_rep * reply, 732 krb5_keyblock * encrypting_key, 733 krb5_pa_data ** send_pa, 734 krb5_kdcpreauth_callbacks cb, 735 krb5_kdcpreauth_rock rock, 736 krb5_kdcpreauth_moddata moddata, 737 krb5_kdcpreauth_modreq modreq) 738 { 739 krb5_error_code retval = 0; 740 krb5_data scratch = {0, 0, NULL}; 741 krb5_pa_pk_as_req *reqp = NULL; 742 int i = 0; 743 744 unsigned char *dh_pubkey = NULL, *server_key = NULL; 745 unsigned int server_key_len = 0, dh_pubkey_len = 0; 746 krb5_keyblock reply_key = { 0 }; 747 748 krb5_kdc_dh_key_info dhkey_info; 749 krb5_data *encoded_dhkey_info = NULL; 750 krb5_pa_pk_as_rep *rep = NULL; 751 krb5_data *out_data = NULL; 752 krb5_data secret; 753 754 krb5_enctype enctype = -1; 755 756 krb5_reply_key_pack *key_pack = NULL; 757 krb5_data *encoded_key_pack = NULL; 758 759 pkinit_kdc_context plgctx; 760 pkinit_kdc_req_context reqctx; 761 762 *send_pa = NULL; 763 if (padata->pa_type == KRB5_PADATA_PKINIT_KX) { 764 return return_pkinit_kx(context, request, reply, 765 encrypting_key, send_pa); 766 } 767 if (padata->length <= 0 || padata->contents == NULL) 768 return 0; 769 770 if (modreq == NULL) { 771 pkiDebug("missing request context \n"); 772 return EINVAL; 773 } 774 775 plgctx = pkinit_find_realm_context(context, moddata, request->server); 776 if (plgctx == NULL) { 777 pkiDebug("Unable to locate correct realm context\n"); 778 return ENOENT; 779 } 780 781 TRACE_PKINIT_SERVER_RETURN_PADATA(context); 782 reqctx = (pkinit_kdc_req_context)modreq; 783 784 for(i = 0; i < request->nktypes; i++) { 785 enctype = request->ktype[i]; 786 if (!krb5_c_valid_enctype(enctype)) 787 continue; 788 else { 789 pkiDebug("KDC picked etype = %d\n", enctype); 790 break; 791 } 792 } 793 794 if (i == request->nktypes) { 795 retval = KRB5KDC_ERR_ETYPE_NOSUPP; 796 goto cleanup; 797 } 798 799 init_krb5_pa_pk_as_rep(&rep); 800 if (rep == NULL) { 801 retval = ENOMEM; 802 goto cleanup; 803 } 804 805 if (reqctx->rcv_auth_pack == NULL || 806 reqctx->rcv_auth_pack->clientPublicValue.length == 0) { 807 retval = KRB5KDC_ERR_PREAUTH_FAILED; 808 k5_setmsg(context, retval, _("Unsupported PKINIT RSA request")); 809 goto cleanup; 810 } 811 812 rep->choice = choice_pa_pk_as_rep_dhInfo; 813 814 retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, 815 plgctx->idctx, &dh_pubkey, &dh_pubkey_len, 816 &server_key, &server_key_len); 817 if (retval) { 818 pkiDebug("failed to process/create dh parameters\n"); 819 goto cleanup; 820 } 821 822 dhkey_info.subjectPublicKey.length = dh_pubkey_len; 823 dhkey_info.subjectPublicKey.data = (char *)dh_pubkey; 824 dhkey_info.nonce = request->nonce; 825 dhkey_info.dhKeyExpiration = 0; 826 827 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, 828 &encoded_dhkey_info); 829 if (retval) { 830 pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); 831 goto cleanup; 832 } 833 #ifdef DEBUG_ASN1 834 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, 835 encoded_dhkey_info->length, "/tmp/kdc_dh_key_info"); 836 #endif 837 838 retval = cms_signeddata_create(context, plgctx->cryptoctx, 839 reqctx->cryptoctx, plgctx->idctx, 840 CMS_SIGN_SERVER, 841 (unsigned char *)encoded_dhkey_info->data, 842 encoded_dhkey_info->length, 843 (unsigned char **) 844 &rep->u.dh_Info.dhSignedData.data, 845 &rep->u.dh_Info.dhSignedData.length); 846 if (retval) { 847 pkiDebug("failed to create pkcs7 signed data\n"); 848 goto cleanup; 849 } 850 851 if (reqctx->rcv_auth_pack != NULL && 852 reqctx->rcv_auth_pack->supportedKDFs != NULL) { 853 /* If using the alg-agility KDF, put the algorithm in the reply 854 * before encoding it. 855 */ 856 if (reqctx->rcv_auth_pack != NULL && 857 reqctx->rcv_auth_pack->supportedKDFs != NULL) { 858 retval = pkinit_pick_kdf_alg(context, reqctx->rcv_auth_pack->supportedKDFs, 859 &(rep->u.dh_Info.kdfID)); 860 if (retval) { 861 pkiDebug("pkinit_pick_kdf_alg failed: %s\n", 862 error_message(retval)); 863 goto cleanup; 864 } 865 } 866 } 867 868 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); 869 if (retval) { 870 pkiDebug("failed to encode AS_REP\n"); 871 goto cleanup; 872 } 873 #ifdef DEBUG_ASN1 874 if (out_data != NULL) 875 print_buffer_bin((unsigned char *)out_data->data, out_data->length, 876 "/tmp/kdc_as_rep"); 877 #endif 878 879 secret = make_data(server_key, server_key_len); 880 retval = pkinit_kdf(context, &secret, rep->u.dh_Info.kdfID, 881 request->client, request->server, enctype, req_pkt, 882 out_data, &reply_key); 883 if (retval) { 884 pkiDebug("pkinit_kdf failed: %s\n", error_message(retval)); 885 goto cleanup; 886 } 887 888 retval = cb->replace_reply_key(context, rock, &reply_key, FALSE); 889 if (retval) 890 goto cleanup; 891 892 *send_pa = malloc(sizeof(krb5_pa_data)); 893 if (*send_pa == NULL) { 894 retval = ENOMEM; 895 free(out_data->data); 896 free(out_data); 897 out_data = NULL; 898 goto cleanup; 899 } 900 (*send_pa)->magic = KV5M_PA_DATA; 901 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; 902 (*send_pa)->length = out_data->length; 903 (*send_pa)->contents = (krb5_octet *) out_data->data; 904 905 cleanup: 906 free(scratch.data); 907 free(out_data); 908 if (encoded_dhkey_info != NULL) 909 krb5_free_data(context, encoded_dhkey_info); 910 if (encoded_key_pack != NULL) 911 krb5_free_data(context, encoded_key_pack); 912 free(dh_pubkey); 913 free(server_key); 914 free_krb5_pa_pk_as_req(&reqp); 915 free_krb5_pa_pk_as_rep(&rep); 916 free_krb5_reply_key_pack(&key_pack); 917 krb5_free_keyblock_contents(context, &reply_key); 918 919 if (retval) 920 pkiDebug("pkinit_verify_padata failure"); 921 922 return retval; 923 } 924 925 static int 926 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) 927 { 928 if (patype == KRB5_PADATA_PKINIT_KX) 929 return PA_INFO; 930 /* PKINIT does not normally set the hw-authent ticket flag, but a 931 * certauth module can cause it to do so. */ 932 return PA_SUFFICIENT | PA_REPLACES_KEY | PA_TYPED_E_DATA | PA_HARDWARE; 933 } 934 935 static krb5_preauthtype supported_server_pa_types[] = { 936 KRB5_PADATA_PK_AS_REQ, 937 KRB5_PADATA_PKINIT_KX, 938 0 939 }; 940 941 static void 942 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 943 { 944 /* 945 * There is nothing currently allocated by pkinit_init_kdc_profile() 946 * which needs to be freed here. 947 */ 948 } 949 950 static krb5_error_code 951 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 952 { 953 krb5_error_code retval; 954 char *eku_string = NULL, *ocsp_check = NULL, *minbits = NULL; 955 956 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname); 957 retval = pkinit_kdcdefault_string(context, plgctx->realmname, 958 KRB5_CONF_PKINIT_IDENTITY, 959 &plgctx->idopts->identity); 960 if (retval != 0 || NULL == plgctx->idopts->identity) { 961 retval = EINVAL; 962 krb5_set_error_message(context, retval, 963 _("No pkinit_identity supplied for realm %s"), 964 plgctx->realmname); 965 goto errout; 966 } 967 968 retval = pkinit_kdcdefault_strings(context, plgctx->realmname, 969 KRB5_CONF_PKINIT_ANCHORS, 970 &plgctx->idopts->anchors); 971 if (retval != 0 || NULL == plgctx->idopts->anchors) { 972 retval = EINVAL; 973 krb5_set_error_message(context, retval, 974 _("No pkinit_anchors supplied for realm %s"), 975 plgctx->realmname); 976 goto errout; 977 } 978 979 pkinit_kdcdefault_strings(context, plgctx->realmname, 980 KRB5_CONF_PKINIT_POOL, 981 &plgctx->idopts->intermediates); 982 983 pkinit_kdcdefault_strings(context, plgctx->realmname, 984 KRB5_CONF_PKINIT_REVOKE, 985 &plgctx->idopts->crls); 986 987 pkinit_kdcdefault_string(context, plgctx->realmname, 988 KRB5_CONF_PKINIT_KDC_OCSP, 989 &ocsp_check); 990 if (ocsp_check != NULL) { 991 free(ocsp_check); 992 retval = ENOTSUP; 993 krb5_set_error_message(context, retval, 994 _("OCSP is not supported: (realm: %s)"), 995 plgctx->realmname); 996 goto errout; 997 } 998 999 pkinit_kdcdefault_string(context, plgctx->realmname, 1000 KRB5_CONF_PKINIT_DH_MIN_BITS, &minbits); 1001 plgctx->opts->dh_min_bits = parse_dh_min_bits(context, minbits); 1002 free(minbits); 1003 1004 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1005 KRB5_CONF_PKINIT_ALLOW_UPN, 1006 0, &plgctx->opts->allow_upn); 1007 1008 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1009 KRB5_CONF_PKINIT_REQUIRE_CRL_CHECKING, 1010 0, &plgctx->opts->require_crl_checking); 1011 1012 pkinit_kdcdefault_boolean(context, plgctx->realmname, 1013 KRB5_CONF_PKINIT_REQUIRE_FRESHNESS, 1014 0, &plgctx->opts->require_freshness); 1015 1016 pkinit_kdcdefault_string(context, plgctx->realmname, 1017 KRB5_CONF_PKINIT_EKU_CHECKING, 1018 &eku_string); 1019 if (eku_string != NULL) { 1020 if (strcasecmp(eku_string, "kpClientAuth") == 0) { 1021 plgctx->opts->require_eku = 1; 1022 plgctx->opts->accept_secondary_eku = 0; 1023 } else if (strcasecmp(eku_string, "scLogin") == 0) { 1024 plgctx->opts->require_eku = 1; 1025 plgctx->opts->accept_secondary_eku = 1; 1026 } else if (strcasecmp(eku_string, "none") == 0) { 1027 plgctx->opts->require_eku = 0; 1028 plgctx->opts->accept_secondary_eku = 0; 1029 } else { 1030 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", 1031 __FUNCTION__, eku_string); 1032 } 1033 free(eku_string); 1034 } 1035 1036 pkinit_kdcdefault_strings(context, plgctx->realmname, 1037 KRB5_CONF_PKINIT_INDICATOR, 1038 &plgctx->auth_indicators); 1039 1040 return 0; 1041 errout: 1042 pkinit_fini_kdc_profile(context, plgctx); 1043 return retval; 1044 } 1045 1046 static pkinit_kdc_context 1047 pkinit_find_realm_context(krb5_context context, 1048 krb5_kdcpreauth_moddata moddata, 1049 krb5_principal princ) 1050 { 1051 size_t i; 1052 pkinit_kdc_context *realm_contexts; 1053 1054 if (moddata == NULL) 1055 return NULL; 1056 1057 realm_contexts = moddata->realm_contexts; 1058 if (realm_contexts == NULL) 1059 return NULL; 1060 1061 for (i = 0; realm_contexts[i] != NULL; i++) { 1062 pkinit_kdc_context p = realm_contexts[i]; 1063 1064 if ((p->realmname_len == princ->realm.length) && 1065 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) { 1066 pkiDebug("%s: returning context at %p for realm '%s'\n", 1067 __FUNCTION__, p, p->realmname); 1068 return p; 1069 } 1070 } 1071 pkiDebug("%s: unable to find realm context for realm '%.*s'\n", 1072 __FUNCTION__, princ->realm.length, princ->realm.data); 1073 return NULL; 1074 } 1075 1076 static int 1077 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname, 1078 pkinit_kdc_context *pplgctx) 1079 { 1080 krb5_error_code retval = ENOMEM; 1081 pkinit_kdc_context plgctx = NULL; 1082 1083 *pplgctx = NULL; 1084 1085 plgctx = calloc(1, sizeof(*plgctx)); 1086 if (plgctx == NULL) 1087 goto errout; 1088 1089 pkiDebug("%s: initializing context at %p for realm '%s'\n", 1090 __FUNCTION__, plgctx, realmname); 1091 memset(plgctx, 0, sizeof(*plgctx)); 1092 plgctx->magic = PKINIT_CTX_MAGIC; 1093 1094 plgctx->realmname = strdup(realmname); 1095 if (plgctx->realmname == NULL) 1096 goto errout; 1097 plgctx->realmname_len = strlen(plgctx->realmname); 1098 1099 retval = pkinit_init_plg_crypto(context, &plgctx->cryptoctx); 1100 if (retval) 1101 goto errout; 1102 1103 retval = pkinit_init_plg_opts(&plgctx->opts); 1104 if (retval) 1105 goto errout; 1106 1107 retval = pkinit_init_identity_crypto(&plgctx->idctx); 1108 if (retval) 1109 goto errout; 1110 1111 retval = pkinit_init_identity_opts(&plgctx->idopts); 1112 if (retval) 1113 goto errout; 1114 1115 retval = pkinit_init_kdc_profile(context, plgctx); 1116 if (retval) 1117 goto errout; 1118 1119 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL, 1120 plgctx->idopts, plgctx->idctx, 1121 NULL, NULL, NULL); 1122 if (retval) 1123 goto errout; 1124 retval = pkinit_identity_prompt(context, plgctx->cryptoctx, NULL, 1125 plgctx->idopts, plgctx->idctx, 1126 NULL, NULL, 0, NULL); 1127 if (retval) 1128 goto errout; 1129 1130 pkiDebug("%s: returning context at %p for realm '%s'\n", 1131 __FUNCTION__, plgctx, realmname); 1132 *pplgctx = plgctx; 1133 retval = 0; 1134 1135 errout: 1136 if (retval) 1137 pkinit_server_plugin_fini_realm(context, plgctx); 1138 1139 return retval; 1140 } 1141 1142 static krb5_error_code 1143 pkinit_san_authorize(krb5_context context, krb5_certauth_moddata moddata, 1144 const uint8_t *cert, size_t cert_len, 1145 krb5_const_principal princ, const void *opts, 1146 const struct _krb5_db_entry_new *db_entry, 1147 char ***authinds_out) 1148 { 1149 krb5_error_code ret; 1150 int valid_san; 1151 const struct certauth_req_opts *req_opts = opts; 1152 1153 *authinds_out = NULL; 1154 1155 ret = verify_client_san(context, req_opts->plgctx, req_opts->reqctx, 1156 req_opts->cb, req_opts->rock, princ, &valid_san); 1157 if (ret == ENOENT) 1158 return KRB5_PLUGIN_NO_HANDLE; 1159 else if (ret) 1160 return ret; 1161 1162 if (!valid_san) { 1163 TRACE_PKINIT_SERVER_SAN_REJECT(context); 1164 return KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 1165 } 1166 1167 return 0; 1168 } 1169 1170 static krb5_error_code 1171 pkinit_eku_authorize(krb5_context context, krb5_certauth_moddata moddata, 1172 const uint8_t *cert, size_t cert_len, 1173 krb5_const_principal princ, const void *opts, 1174 const struct _krb5_db_entry_new *db_entry, 1175 char ***authinds_out) 1176 { 1177 krb5_error_code ret; 1178 int valid_eku; 1179 const struct certauth_req_opts *req_opts = opts; 1180 1181 *authinds_out = NULL; 1182 1183 /* Verify the client EKU. */ 1184 ret = verify_client_eku(context, req_opts->plgctx, req_opts->reqctx, 1185 &valid_eku); 1186 if (ret) 1187 return ret; 1188 1189 if (!valid_eku) { 1190 TRACE_PKINIT_SERVER_EKU_REJECT(context); 1191 return KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; 1192 } 1193 1194 return KRB5_PLUGIN_NO_HANDLE; 1195 } 1196 1197 static krb5_error_code 1198 certauth_pkinit_san_initvt(krb5_context context, int maj_ver, int min_ver, 1199 krb5_plugin_vtable vtable) 1200 { 1201 krb5_certauth_vtable vt; 1202 1203 if (maj_ver != 1) 1204 return KRB5_PLUGIN_VER_NOTSUPP; 1205 vt = (krb5_certauth_vtable)vtable; 1206 vt->name = "pkinit_san"; 1207 vt->authorize = pkinit_san_authorize; 1208 return 0; 1209 } 1210 1211 static krb5_error_code 1212 certauth_pkinit_eku_initvt(krb5_context context, int maj_ver, int min_ver, 1213 krb5_plugin_vtable vtable) 1214 { 1215 krb5_certauth_vtable vt; 1216 1217 if (maj_ver != 1) 1218 return KRB5_PLUGIN_VER_NOTSUPP; 1219 vt = (krb5_certauth_vtable)vtable; 1220 vt->name = "pkinit_eku"; 1221 vt->authorize = pkinit_eku_authorize; 1222 return 0; 1223 } 1224 1225 /* 1226 * Do certificate auth based on a match expression in the pkinit_cert_match 1227 * attribute string. An expression should be in the same form as those used 1228 * for the pkinit_cert_match configuration option. 1229 */ 1230 static krb5_error_code 1231 dbmatch_authorize(krb5_context context, krb5_certauth_moddata moddata, 1232 const uint8_t *cert, size_t cert_len, 1233 krb5_const_principal princ, const void *opts, 1234 const struct _krb5_db_entry_new *db_entry, 1235 char ***authinds_out) 1236 { 1237 krb5_error_code ret; 1238 const struct certauth_req_opts *req_opts = opts; 1239 char *pattern; 1240 krb5_boolean matched; 1241 1242 *authinds_out = NULL; 1243 1244 /* Fetch the matching pattern. Pass if it isn't specified. */ 1245 ret = req_opts->cb->get_string(context, req_opts->rock, 1246 "pkinit_cert_match", &pattern); 1247 if (ret) 1248 return ret; 1249 if (pattern == NULL) 1250 return KRB5_PLUGIN_NO_HANDLE; 1251 1252 /* Check the certificate against the match expression. */ 1253 ret = pkinit_client_cert_match(context, req_opts->plgctx->cryptoctx, 1254 req_opts->reqctx->cryptoctx, pattern, 1255 &matched); 1256 req_opts->cb->free_string(context, req_opts->rock, pattern); 1257 if (ret) 1258 return ret; 1259 return matched ? 0 : KRB5KDC_ERR_CERTIFICATE_MISMATCH; 1260 } 1261 1262 static krb5_error_code 1263 certauth_dbmatch_initvt(krb5_context context, int maj_ver, int min_ver, 1264 krb5_plugin_vtable vtable) 1265 { 1266 krb5_certauth_vtable vt; 1267 1268 if (maj_ver != 1) 1269 return KRB5_PLUGIN_VER_NOTSUPP; 1270 vt = (krb5_certauth_vtable)vtable; 1271 vt->name = "dbmatch"; 1272 vt->authorize = dbmatch_authorize; 1273 return 0; 1274 } 1275 1276 static krb5_error_code 1277 load_certauth_plugins(krb5_context context, const char *const *realmnames, 1278 certauth_handle **handle_out) 1279 { 1280 krb5_error_code ret; 1281 krb5_plugin_initvt_fn *modules = NULL, *mod; 1282 certauth_handle *list = NULL, h; 1283 size_t count; 1284 1285 /* Register the builtin modules. */ 1286 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, 1287 "pkinit_san", certauth_pkinit_san_initvt); 1288 if (ret) 1289 goto cleanup; 1290 1291 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, 1292 "pkinit_eku", certauth_pkinit_eku_initvt); 1293 if (ret) 1294 goto cleanup; 1295 1296 ret = k5_plugin_register(context, PLUGIN_INTERFACE_CERTAUTH, "dbmatch", 1297 certauth_dbmatch_initvt); 1298 if (ret) 1299 goto cleanup; 1300 1301 ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_CERTAUTH, &modules); 1302 if (ret) 1303 goto cleanup; 1304 1305 /* Allocate handle list. */ 1306 for (count = 0; modules[count]; count++); 1307 list = k5calloc(count + 1, sizeof(*list), &ret); 1308 if (list == NULL) 1309 goto cleanup; 1310 1311 /* Initialize each module, ignoring ones that fail. */ 1312 count = 0; 1313 for (mod = modules; *mod != NULL; mod++) { 1314 h = k5calloc(1, sizeof(*h), &ret); 1315 if (h == NULL) 1316 goto cleanup; 1317 1318 ret = (*mod)(context, 1, 2, (krb5_plugin_vtable)&h->vt); 1319 if (ret) { 1320 TRACE_CERTAUTH_VTINIT_FAIL(context, ret); 1321 free(h); 1322 continue; 1323 } 1324 h->moddata = NULL; 1325 if (h->vt.init_ex != NULL) 1326 ret = h->vt.init_ex(context, realmnames, &h->moddata); 1327 else if (h->vt.init != NULL) 1328 ret = h->vt.init(context, &h->moddata); 1329 if (ret) { 1330 TRACE_CERTAUTH_INIT_FAIL(context, h->vt.name, ret); 1331 free(h); 1332 continue; 1333 } 1334 list[count++] = h; 1335 list[count] = NULL; 1336 } 1337 list[count] = NULL; 1338 1339 ret = 0; 1340 *handle_out = list; 1341 list = NULL; 1342 1343 cleanup: 1344 k5_plugin_free_modules(context, modules); 1345 free_certauth_handles(context, list); 1346 return ret; 1347 } 1348 1349 static int 1350 pkinit_server_plugin_init(krb5_context context, 1351 krb5_kdcpreauth_moddata *moddata_out, 1352 const char **realmnames) 1353 { 1354 krb5_error_code retval = ENOMEM; 1355 pkinit_kdc_context plgctx, *realm_contexts = NULL; 1356 certauth_handle *certauth_modules = NULL; 1357 krb5_kdcpreauth_moddata moddata; 1358 size_t i, j; 1359 size_t numrealms; 1360 1361 retval = pkinit_accessor_init(); 1362 if (retval) 1363 return retval; 1364 1365 /* Determine how many realms we may need to support */ 1366 for (i = 0; realmnames[i] != NULL; i++) {}; 1367 numrealms = i; 1368 1369 realm_contexts = calloc(numrealms+1, sizeof(pkinit_kdc_context)); 1370 if (realm_contexts == NULL) 1371 return ENOMEM; 1372 1373 for (i = 0, j = 0; i < numrealms; i++) { 1374 TRACE_PKINIT_SERVER_INIT_REALM(context, realmnames[i]); 1375 krb5_clear_error_message(context); 1376 retval = pkinit_server_plugin_init_realm(context, realmnames[i], 1377 &plgctx); 1378 if (retval) 1379 TRACE_PKINIT_SERVER_INIT_FAIL(context, realmnames[i], retval); 1380 else 1381 realm_contexts[j++] = plgctx; 1382 } 1383 1384 if (j == 0) { 1385 if (numrealms == 1) { 1386 k5_prependmsg(context, retval, "PKINIT initialization failed"); 1387 } else { 1388 retval = EINVAL; 1389 k5_setmsg(context, retval, 1390 _("No realms configured correctly for pkinit support")); 1391 } 1392 goto errout; 1393 } 1394 1395 retval = load_certauth_plugins(context, realmnames, &certauth_modules); 1396 if (retval) 1397 goto errout; 1398 1399 moddata = k5calloc(1, sizeof(*moddata), &retval); 1400 if (moddata == NULL) 1401 goto errout; 1402 moddata->realm_contexts = realm_contexts; 1403 moddata->certauth_modules = certauth_modules; 1404 *moddata_out = moddata; 1405 pkiDebug("%s: returning context at %p\n", __FUNCTION__, moddata); 1406 return 0; 1407 1408 errout: 1409 free_realm_contexts(context, realm_contexts); 1410 free_certauth_handles(context, certauth_modules); 1411 return retval; 1412 } 1413 1414 static void 1415 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx) 1416 { 1417 char **sp; 1418 1419 if (plgctx == NULL) 1420 return; 1421 1422 pkinit_fini_kdc_profile(context, plgctx); 1423 pkinit_fini_identity_opts(plgctx->idopts); 1424 pkinit_fini_identity_crypto(plgctx->idctx); 1425 pkinit_fini_plg_crypto(plgctx->cryptoctx); 1426 pkinit_fini_plg_opts(plgctx->opts); 1427 for (sp = plgctx->auth_indicators; sp != NULL && *sp != NULL; sp++) 1428 free(*sp); 1429 free(plgctx->auth_indicators); 1430 free(plgctx->realmname); 1431 free(plgctx); 1432 } 1433 1434 static void 1435 pkinit_server_plugin_fini(krb5_context context, 1436 krb5_kdcpreauth_moddata moddata) 1437 { 1438 if (moddata == NULL) 1439 return; 1440 free_realm_contexts(context, moddata->realm_contexts); 1441 free_certauth_handles(context, moddata->certauth_modules); 1442 free(moddata); 1443 } 1444 1445 static krb5_error_code 1446 pkinit_init_kdc_req_context(krb5_context context, pkinit_kdc_req_context *ctx) 1447 { 1448 krb5_error_code retval = ENOMEM; 1449 pkinit_kdc_req_context reqctx = NULL; 1450 1451 reqctx = malloc(sizeof(*reqctx)); 1452 if (reqctx == NULL) 1453 return retval; 1454 memset(reqctx, 0, sizeof(*reqctx)); 1455 reqctx->magic = PKINIT_CTX_MAGIC; 1456 1457 retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 1458 if (retval) 1459 goto cleanup; 1460 reqctx->rcv_auth_pack = NULL; 1461 1462 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); 1463 *ctx = reqctx; 1464 retval = 0; 1465 cleanup: 1466 if (retval) 1467 pkinit_fini_kdc_req_context(context, reqctx); 1468 1469 return retval; 1470 } 1471 1472 static void 1473 pkinit_fini_kdc_req_context(krb5_context context, void *ctx) 1474 { 1475 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx; 1476 1477 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) { 1478 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx); 1479 return; 1480 } 1481 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx); 1482 1483 pkinit_fini_req_crypto(reqctx->cryptoctx); 1484 if (reqctx->rcv_auth_pack != NULL) 1485 free_krb5_auth_pack(&reqctx->rcv_auth_pack); 1486 1487 free(reqctx); 1488 } 1489 1490 static void 1491 pkinit_free_modreq(krb5_context context, krb5_kdcpreauth_moddata moddata, 1492 krb5_kdcpreauth_modreq modreq) 1493 { 1494 pkinit_fini_kdc_req_context(context, modreq); 1495 } 1496 1497 krb5_error_code 1498 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1499 krb5_plugin_vtable vtable); 1500 1501 krb5_error_code 1502 kdcpreauth_pkinit_initvt(krb5_context context, int maj_ver, int min_ver, 1503 krb5_plugin_vtable vtable) 1504 { 1505 krb5_kdcpreauth_vtable vt; 1506 1507 if (maj_ver != 1) 1508 return KRB5_PLUGIN_VER_NOTSUPP; 1509 vt = (krb5_kdcpreauth_vtable)vtable; 1510 vt->name = "pkinit"; 1511 vt->pa_type_list = supported_server_pa_types; 1512 vt->init = pkinit_server_plugin_init; 1513 vt->fini = pkinit_server_plugin_fini; 1514 vt->flags = pkinit_server_get_flags; 1515 vt->edata = pkinit_server_get_edata; 1516 vt->verify = pkinit_server_verify_padata; 1517 vt->return_padata = pkinit_server_return_padata; 1518 vt->free_modreq = pkinit_free_modreq; 1519 return 0; 1520 } 1521