1 /* 2 * COPYRIGHT (C) 2006,2007 3 * THE REGENTS OF THE UNIVERSITY OF MICHIGAN 4 * ALL RIGHTS RESERVED 5 * 6 * Permission is granted to use, copy, create derivative works 7 * and redistribute this software and such derivative works 8 * for any purpose, so long as the name of The University of 9 * Michigan is not used in any advertising or publicity 10 * pertaining to the use of distribution of this software 11 * without specific, written prior authorization. If the 12 * above copyright notice or any other identification of the 13 * University of Michigan is included in any copy of any 14 * portion of this software, then the disclaimer below must 15 * also be included. 16 * 17 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 18 * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY 19 * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF 20 * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING 21 * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 23 * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE 24 * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 25 * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING 26 * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN 27 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGES. 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <string.h> 35 36 #include "pkinit.h" 37 38 static krb5_error_code 39 pkinit_server_get_edata(krb5_context context, 40 krb5_kdc_req * request, 41 struct _krb5_db_entry_new * client, 42 struct _krb5_db_entry_new * server, 43 preauth_get_entry_data_proc server_get_entry_data, 44 void *pa_plugin_context, 45 krb5_pa_data * data); 46 47 static krb5_error_code 48 pkinit_server_verify_padata(krb5_context context, 49 struct _krb5_db_entry_new * client, 50 krb5_data *req_pkt, 51 krb5_kdc_req * request, 52 krb5_enc_tkt_part * enc_tkt_reply, 53 krb5_pa_data * data, 54 preauth_get_entry_data_proc server_get_entry_data, 55 void *pa_plugin_context, 56 void **pa_request_context, 57 krb5_data **e_data, 58 krb5_authdata ***authz_data); 59 60 static krb5_error_code 61 pkinit_server_return_padata(krb5_context context, 62 krb5_pa_data * padata, 63 struct _krb5_db_entry_new * client, 64 krb5_data *req_pkt, 65 krb5_kdc_req * request, 66 krb5_kdc_rep * reply, 67 struct _krb5_key_data * client_key, 68 krb5_keyblock * encrypting_key, 69 krb5_pa_data ** send_pa, 70 preauth_get_entry_data_proc server_get_entry_data, 71 void *pa_plugin_context, 72 void **pa_request_context); 73 74 static int pkinit_server_get_flags 75 (krb5_context kcontext, krb5_preauthtype patype); 76 77 static krb5_error_code pkinit_init_kdc_req_context 78 (krb5_context, void **blob); 79 80 static void pkinit_fini_kdc_req_context 81 (krb5_context context, void *blob); 82 83 static int pkinit_server_plugin_init_realm 84 (krb5_context context, const char *realmname, 85 pkinit_kdc_context *pplgctx); 86 87 static void pkinit_server_plugin_fini_realm 88 (krb5_context context, pkinit_kdc_context plgctx); 89 90 static int pkinit_server_plugin_init 91 (krb5_context context, void **blob, const char **realmnames); 92 93 static void pkinit_server_plugin_fini 94 (krb5_context context, void *blob); 95 96 static pkinit_kdc_context pkinit_find_realm_context 97 (krb5_context context, void *pa_plugin_context, krb5_principal princ); 98 99 static krb5_error_code 100 pkinit_create_edata(krb5_context context, 101 pkinit_plg_crypto_context plg_cryptoctx, 102 pkinit_req_crypto_context req_cryptoctx, 103 pkinit_identity_crypto_context id_cryptoctx, 104 pkinit_plg_opts *opts, 105 krb5_error_code err_code, 106 krb5_data **e_data) 107 { 108 krb5_error_code retval = KRB5KRB_ERR_GENERIC; 109 110 pkiDebug("pkinit_create_edata: creating edata for error %d (%s)\n", 111 err_code, error_message(err_code)); 112 switch(err_code) { 113 case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE: 114 retval = pkinit_create_td_trusted_certifiers(context, 115 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data); 116 break; 117 case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED: 118 retval = pkinit_create_td_dh_parameters(context, plg_cryptoctx, 119 req_cryptoctx, id_cryptoctx, opts, e_data); 120 break; 121 case KRB5KDC_ERR_INVALID_CERTIFICATE: 122 case KRB5KDC_ERR_REVOKED_CERTIFICATE: 123 retval = pkinit_create_td_invalid_certificate(context, 124 plg_cryptoctx, req_cryptoctx, id_cryptoctx, e_data); 125 break; 126 default: 127 pkiDebug("no edata needed for error %d (%s)\n", 128 err_code, error_message(err_code)); 129 retval = 0; 130 goto cleanup; 131 } 132 133 cleanup: 134 135 return retval; 136 } 137 138 /* ARGSUSED */ 139 static krb5_error_code 140 pkinit_server_get_edata(krb5_context context, 141 krb5_kdc_req * request, 142 struct _krb5_db_entry_new * client, 143 struct _krb5_db_entry_new * server, 144 preauth_get_entry_data_proc server_get_entry_data, 145 void *pa_plugin_context, 146 krb5_pa_data * data) 147 { 148 krb5_error_code retval = 0; 149 pkinit_kdc_context plgctx = NULL; 150 151 pkiDebug("pkinit_server_get_edata: entered!\n"); 152 153 /* 154 * If we don't have a realm context for the given realm, 155 * don't tell the client that we support pkinit! 156 */ 157 plgctx = pkinit_find_realm_context(context, pa_plugin_context, 158 request->server); 159 if (plgctx == NULL) 160 retval = EINVAL; 161 162 return retval; 163 } 164 165 static krb5_error_code 166 verify_client_san(krb5_context context, 167 pkinit_kdc_context plgctx, 168 pkinit_kdc_req_context reqctx, 169 krb5_principal client, 170 int *valid_san) 171 { 172 krb5_error_code retval; 173 krb5_principal *princs = NULL; 174 krb5_principal *upns = NULL; 175 int i; 176 #ifdef DEBUG_SAN_INFO 177 char *client_string = NULL, *san_string; 178 #endif 179 180 retval = crypto_retrieve_cert_sans(context, plgctx->cryptoctx, 181 reqctx->cryptoctx, plgctx->idctx, 182 &princs, 183 plgctx->opts->allow_upn ? &upns : NULL, 184 NULL); 185 if (retval) { 186 pkiDebug("%s: error from retrieve_certificate_sans()\n", __FUNCTION__); 187 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 188 goto out; 189 } 190 /* XXX Verify this is consistent with client side XXX */ 191 #if 0 192 retval = call_san_checking_plugins(context, plgctx, reqctx, princs, 193 upns, NULL, &plugin_decision, &ignore); 194 pkiDebug("%s: call_san_checking_plugins() returned retval %d\n", 195 __FUNCTION__); 196 if (retval) { 197 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 198 goto cleanup; 199 } 200 pkiDebug("%s: call_san_checking_plugins() returned decision %d\n", 201 __FUNCTION__, plugin_decision); 202 if (plugin_decision != NO_DECISION) { 203 retval = plugin_decision; 204 goto out; 205 } 206 #endif 207 208 #ifdef DEBUG_SAN_INFO 209 krb5_unparse_name(context, client, &client_string); 210 #endif 211 pkiDebug("%s: Checking pkinit sans\n", __FUNCTION__); 212 for (i = 0; princs != NULL && princs[i] != NULL; i++) { 213 #ifdef DEBUG_SAN_INFO 214 krb5_unparse_name(context, princs[i], &san_string); 215 pkiDebug("%s: Comparing client '%s' to pkinit san value '%s'\n", 216 __FUNCTION__, client_string, san_string); 217 krb5_free_unparsed_name(context, san_string); 218 #endif 219 if (krb5_principal_compare(context, princs[i], client)) { 220 pkiDebug("%s: pkinit san match found\n", __FUNCTION__); 221 *valid_san = 1; 222 retval = 0; 223 goto out; 224 } 225 } 226 pkiDebug("%s: no pkinit san match found\n", __FUNCTION__); 227 /* 228 * XXX if cert has names but none match, should we 229 * be returning KRB5KDC_ERR_CLIENT_NAME_MISMATCH here? 230 */ 231 232 if (upns == NULL) { 233 pkiDebug("%s: no upn sans (or we wouldn't accept them anyway)\n", 234 __FUNCTION__); 235 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 236 goto out; 237 } 238 239 pkiDebug("%s: Checking upn sans\n", __FUNCTION__); 240 for (i = 0; upns[i] != NULL; i++) { 241 #ifdef DEBUG_SAN_INFO 242 krb5_unparse_name(context, upns[i], &san_string); 243 pkiDebug("%s: Comparing client '%s' to upn san value '%s'\n", 244 __FUNCTION__, client_string, san_string); 245 krb5_free_unparsed_name(context, san_string); 246 #endif 247 if (krb5_principal_compare(context, upns[i], client)) { 248 pkiDebug("%s: upn san match found\n", __FUNCTION__); 249 *valid_san = 1; 250 retval = 0; 251 goto out; 252 } 253 } 254 pkiDebug("%s: no upn san match found\n", __FUNCTION__); 255 256 /* We found no match */ 257 if (princs != NULL || upns != NULL) { 258 *valid_san = 0; 259 /* XXX ??? If there was one or more name in the cert, but 260 * none matched the client name, then return mismatch? */ 261 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 262 } 263 retval = 0; 264 265 out: 266 if (princs != NULL) { 267 for (i = 0; princs[i] != NULL; i++) 268 krb5_free_principal(context, princs[i]); 269 free(princs); 270 } 271 if (upns != NULL) { 272 for (i = 0; upns[i] != NULL; i++) 273 krb5_free_principal(context, upns[i]); 274 free(upns); 275 } 276 #ifdef DEBUG_SAN_INFO 277 if (client_string != NULL) 278 krb5_free_unparsed_name(context, client_string); 279 #endif 280 pkiDebug("%s: returning retval %d, valid_san %d\n", 281 __FUNCTION__, retval, *valid_san); 282 return retval; 283 } 284 285 static krb5_error_code 286 verify_client_eku(krb5_context context, 287 pkinit_kdc_context plgctx, 288 pkinit_kdc_req_context reqctx, 289 int *eku_accepted) 290 { 291 krb5_error_code retval; 292 293 *eku_accepted = 0; 294 295 if (plgctx->opts->require_eku == 0) { 296 pkiDebug("%s: configuration requests no EKU checking\n", __FUNCTION__); 297 *eku_accepted = 1; 298 retval = 0; 299 goto out; 300 } 301 302 retval = crypto_check_cert_eku(context, plgctx->cryptoctx, 303 reqctx->cryptoctx, plgctx->idctx, 304 0, /* kdc cert */ 305 plgctx->opts->accept_secondary_eku, 306 eku_accepted); 307 if (retval) { 308 pkiDebug("%s: Error from crypto_check_cert_eku %d (%s)\n", 309 __FUNCTION__, retval, error_message(retval)); 310 goto out; 311 } 312 313 out: 314 pkiDebug("%s: returning retval %d, eku_accepted %d\n", 315 __FUNCTION__, retval, *eku_accepted); 316 return retval; 317 } 318 319 /* ARGSUSED */ 320 static krb5_error_code 321 pkinit_server_verify_padata(krb5_context context, 322 struct _krb5_db_entry_new * client, 323 krb5_data *req_pkt, 324 krb5_kdc_req * request, 325 krb5_enc_tkt_part * enc_tkt_reply, 326 krb5_pa_data * data, 327 preauth_get_entry_data_proc server_get_entry_data, 328 void *pa_plugin_context, 329 void **pa_request_context, 330 krb5_data **e_data, 331 krb5_authdata ***authz_data) 332 { 333 krb5_error_code retval = 0; 334 krb5_octet_data authp_data = {0, 0, NULL}, krb5_authz = {0, 0, NULL}; 335 krb5_data *encoded_pkinit_authz_data = NULL; 336 krb5_pa_pk_as_req *reqp = NULL; 337 krb5_pa_pk_as_req_draft9 *reqp9 = NULL; 338 krb5_auth_pack *auth_pack = NULL; 339 krb5_auth_pack_draft9 *auth_pack9 = NULL; 340 pkinit_kdc_context plgctx = NULL; 341 pkinit_kdc_req_context reqctx; 342 /* Solaris Kerberos: set but not used */ 343 #if 0 344 krb5_preauthtype pa_type; 345 #endif 346 krb5_checksum cksum = {0, 0, 0, NULL}; 347 krb5_data *der_req = NULL; 348 int valid_eku = 0, valid_san = 0; 349 krb5_authdata **my_authz_data = NULL, *pkinit_authz_data = NULL; 350 krb5_kdc_req *tmp_as_req = NULL; 351 krb5_data k5data; 352 353 pkiDebug("pkinit_verify_padata: entered!\n"); 354 /* Solaris Kerberos */ 355 if (data == NULL || data->length == 0 || data->contents == NULL) 356 return 0; 357 358 if (pa_plugin_context == NULL || e_data == NULL) 359 return EINVAL; 360 361 plgctx = pkinit_find_realm_context(context, pa_plugin_context, 362 request->server); 363 if (plgctx == NULL) 364 return 0; 365 366 #ifdef DEBUG_ASN1 367 print_buffer_bin(data->contents, data->length, "/tmp/kdc_as_req"); 368 #endif 369 /* create a per-request context */ 370 retval = pkinit_init_kdc_req_context(context, (void **)&reqctx); 371 if (retval) 372 goto cleanup; 373 reqctx->pa_type = data->pa_type; 374 375 PADATA_TO_KRB5DATA(data, &k5data); 376 377 switch ((int)data->pa_type) { 378 case KRB5_PADATA_PK_AS_REQ: 379 pkiDebug("processing KRB5_PADATA_PK_AS_REQ\n"); 380 retval = k5int_decode_krb5_pa_pk_as_req(&k5data, &reqp); 381 if (retval) { 382 pkiDebug("decode_krb5_pa_pk_as_req failed\n"); 383 goto cleanup; 384 } 385 #ifdef DEBUG_ASN1 386 print_buffer_bin(reqp->signedAuthPack.data, 387 reqp->signedAuthPack.length, 388 "/tmp/kdc_signed_data"); 389 #endif 390 retval = cms_signeddata_verify(context, plgctx->cryptoctx, 391 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_CLIENT, 392 plgctx->opts->require_crl_checking, 393 reqp->signedAuthPack.data, reqp->signedAuthPack.length, 394 &authp_data.data, &authp_data.length, &krb5_authz.data, 395 &krb5_authz.length); 396 break; 397 case KRB5_PADATA_PK_AS_REP_OLD: 398 case KRB5_PADATA_PK_AS_REQ_OLD: 399 pkiDebug("processing KRB5_PADATA_PK_AS_REQ_OLD\n"); 400 retval = k5int_decode_krb5_pa_pk_as_req_draft9(&k5data, &reqp9); 401 if (retval) { 402 pkiDebug("decode_krb5_pa_pk_as_req_draft9 failed\n"); 403 goto cleanup; 404 } 405 #ifdef DEBUG_ASN1 406 print_buffer_bin(reqp9->signedAuthPack.data, 407 reqp9->signedAuthPack.length, 408 "/tmp/kdc_signed_data_draft9"); 409 #endif 410 411 retval = cms_signeddata_verify(context, plgctx->cryptoctx, 412 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 413 plgctx->opts->require_crl_checking, 414 reqp9->signedAuthPack.data, reqp9->signedAuthPack.length, 415 &authp_data.data, &authp_data.length, &krb5_authz.data, 416 &krb5_authz.length); 417 break; 418 default: 419 pkiDebug("unrecognized pa_type = %d\n", data->pa_type); 420 retval = EINVAL; 421 goto cleanup; 422 } 423 if (retval) { 424 pkiDebug("pkcs7_signeddata_verify failed\n"); 425 goto cleanup; 426 } 427 428 retval = verify_client_san(context, plgctx, reqctx, request->client, 429 &valid_san); 430 if (retval) 431 goto cleanup; 432 if (!valid_san) { 433 pkiDebug("%s: did not find an acceptable SAN in user certificate\n", 434 __FUNCTION__); 435 retval = KRB5KDC_ERR_CLIENT_NAME_MISMATCH; 436 goto cleanup; 437 } 438 retval = verify_client_eku(context, plgctx, reqctx, &valid_eku); 439 if (retval) 440 goto cleanup; 441 442 if (!valid_eku) { 443 pkiDebug("%s: did not find an acceptable EKU in user certificate\n", 444 __FUNCTION__); 445 retval = KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE; 446 goto cleanup; 447 } 448 449 #ifdef DEBUG_ASN1 450 print_buffer_bin(authp_data.data, authp_data.length, "/tmp/kdc_auth_pack"); 451 #endif 452 453 OCTETDATA_TO_KRB5DATA(&authp_data, &k5data); 454 switch ((int)data->pa_type) { 455 case KRB5_PADATA_PK_AS_REQ: 456 retval = k5int_decode_krb5_auth_pack(&k5data, &auth_pack); 457 if (retval) { 458 pkiDebug("failed to decode krb5_auth_pack\n"); 459 goto cleanup; 460 } 461 462 /* check dh parameters */ 463 if (auth_pack->clientPublicValue != NULL) { 464 retval = server_check_dh(context, plgctx->cryptoctx, 465 reqctx->cryptoctx, plgctx->idctx, 466 &auth_pack->clientPublicValue->algorithm.parameters, 467 plgctx->opts->dh_min_bits); 468 469 if (retval) { 470 pkiDebug("bad dh parameters\n"); 471 goto cleanup; 472 } 473 } 474 /* 475 * The KDC may have modified the request after decoding it. 476 * We need to compute the checksum on the data that 477 * came from the client. Therefore, we use the original 478 * packet contents. 479 */ 480 retval = k5int_decode_krb5_as_req(req_pkt, &tmp_as_req); 481 if (retval) { 482 pkiDebug("decode_krb5_as_req returned %d\n", (int)retval); 483 goto cleanup; 484 } 485 486 retval = k5int_encode_krb5_kdc_req_body(tmp_as_req, &der_req); 487 if (retval) { 488 pkiDebug("encode_krb5_kdc_req_body returned %d\n", (int) retval); 489 goto cleanup; 490 } 491 retval = krb5_c_make_checksum(context, CKSUMTYPE_NIST_SHA, NULL, 492 0, der_req, &cksum); 493 if (retval) { 494 pkiDebug("unable to calculate AS REQ checksum\n"); 495 goto cleanup; 496 } 497 if (cksum.length != auth_pack->pkAuthenticator.paChecksum.length || 498 memcmp(cksum.contents, 499 auth_pack->pkAuthenticator.paChecksum.contents, 500 cksum.length)) { 501 pkiDebug("failed to match the checksum\n"); 502 #ifdef DEBUG_CKSUM 503 pkiDebug("calculating checksum on buf size (%d)\n", 504 req_pkt->length); 505 print_buffer(req_pkt->data, req_pkt->length); 506 pkiDebug("received checksum type=%d size=%d ", 507 auth_pack->pkAuthenticator.paChecksum.checksum_type, 508 auth_pack->pkAuthenticator.paChecksum.length); 509 print_buffer(auth_pack->pkAuthenticator.paChecksum.contents, 510 auth_pack->pkAuthenticator.paChecksum.length); 511 pkiDebug("expected checksum type=%d size=%d ", 512 cksum.checksum_type, cksum.length); 513 print_buffer(cksum.contents, cksum.length); 514 #endif 515 516 retval = KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; 517 goto cleanup; 518 } 519 520 /* check if kdcPkId present and match KDC's subjectIdentifier */ 521 if (reqp->kdcPkId.data != NULL) { 522 int valid_kdcPkId = 0; 523 retval = pkinit_check_kdc_pkid(context, plgctx->cryptoctx, 524 reqctx->cryptoctx, plgctx->idctx, 525 reqp->kdcPkId.data, reqp->kdcPkId.length, &valid_kdcPkId); 526 if (retval) 527 goto cleanup; 528 if (!valid_kdcPkId) 529 pkiDebug("kdcPkId in AS_REQ does not match KDC's cert" 530 "RFC says to ignore and proceed\n"); 531 532 } 533 /* remember the decoded auth_pack for verify_padata routine */ 534 reqctx->rcv_auth_pack = auth_pack; 535 auth_pack = NULL; 536 break; 537 case KRB5_PADATA_PK_AS_REP_OLD: 538 case KRB5_PADATA_PK_AS_REQ_OLD: 539 retval = k5int_decode_krb5_auth_pack_draft9(&k5data, &auth_pack9); 540 if (retval) { 541 pkiDebug("failed to decode krb5_auth_pack_draft9\n"); 542 goto cleanup; 543 } 544 if (auth_pack9->clientPublicValue != NULL) { 545 retval = server_check_dh(context, plgctx->cryptoctx, 546 reqctx->cryptoctx, plgctx->idctx, 547 &auth_pack9->clientPublicValue->algorithm.parameters, 548 plgctx->opts->dh_min_bits); 549 550 if (retval) { 551 pkiDebug("bad dh parameters\n"); 552 goto cleanup; 553 } 554 } 555 /* remember the decoded auth_pack for verify_padata routine */ 556 reqctx->rcv_auth_pack9 = auth_pack9; 557 auth_pack9 = NULL; 558 break; 559 } 560 561 /* return authorization data to be included in the ticket */ 562 switch ((int)data->pa_type) { 563 case KRB5_PADATA_PK_AS_REQ: 564 my_authz_data = malloc(2 * sizeof(*my_authz_data)); 565 if (my_authz_data == NULL) { 566 retval = ENOMEM; 567 pkiDebug("Couldn't allocate krb5_authdata ptr array\n"); 568 goto cleanup; 569 } 570 my_authz_data[1] = NULL; 571 my_authz_data[0] = malloc(sizeof(krb5_authdata)); 572 if (my_authz_data[0] == NULL) { 573 retval = ENOMEM; 574 pkiDebug("Couldn't allocate krb5_authdata\n"); 575 free(my_authz_data); 576 goto cleanup; 577 } 578 /* AD-INITIAL-VERIFIED-CAS must be wrapped in AD-IF-RELEVANT */ 579 my_authz_data[0]->magic = KV5M_AUTHDATA; 580 my_authz_data[0]->ad_type = KRB5_AUTHDATA_IF_RELEVANT; 581 582 /* create an internal AD-INITIAL-VERIFIED-CAS data */ 583 pkinit_authz_data = malloc(sizeof(krb5_authdata)); 584 if (pkinit_authz_data == NULL) { 585 retval = ENOMEM; 586 pkiDebug("Couldn't allocate krb5_authdata\n"); 587 free(my_authz_data[0]); 588 free(my_authz_data); 589 goto cleanup; 590 } 591 pkinit_authz_data->ad_type = KRB5_AUTHDATA_INITIAL_VERIFIED_CAS; 592 /* content of this ad-type contains the certification 593 path with which the client certificate was validated 594 */ 595 pkinit_authz_data->contents = krb5_authz.data; 596 pkinit_authz_data->length = krb5_authz.length; 597 retval = k5int_encode_krb5_authdata_elt(pkinit_authz_data, 598 &encoded_pkinit_authz_data); 599 #ifdef DEBUG_ASN1 600 print_buffer_bin((unsigned char *)encoded_pkinit_authz_data->data, 601 encoded_pkinit_authz_data->length, 602 "/tmp/kdc_pkinit_authz_data"); 603 #endif 604 free(pkinit_authz_data); 605 if (retval) { 606 pkiDebug("k5int_encode_krb5_authdata_elt failed\n"); 607 free(my_authz_data[0]); 608 free(my_authz_data); 609 goto cleanup; 610 } 611 612 my_authz_data[0]->contents = 613 (krb5_octet *) encoded_pkinit_authz_data->data; 614 my_authz_data[0]->length = encoded_pkinit_authz_data->length; 615 *authz_data = my_authz_data; 616 pkiDebug("Returning %d bytes of authorization data\n", 617 krb5_authz.length); 618 encoded_pkinit_authz_data->data = NULL; /* Don't free during cleanup*/ 619 free(encoded_pkinit_authz_data); 620 break; 621 default: 622 *authz_data = NULL; 623 } 624 /* remember to set the PREAUTH flag in the reply */ 625 enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; 626 *pa_request_context = reqctx; 627 reqctx = NULL; 628 629 cleanup: 630 if (retval && data->pa_type == KRB5_PADATA_PK_AS_REQ) { 631 pkiDebug("pkinit_verify_padata failed: creating e-data\n"); 632 if (pkinit_create_edata(context, plgctx->cryptoctx, reqctx->cryptoctx, 633 plgctx->idctx, plgctx->opts, retval, e_data)) 634 pkiDebug("pkinit_create_edata failed\n"); 635 } 636 637 switch ((int)data->pa_type) { 638 case KRB5_PADATA_PK_AS_REQ: 639 free_krb5_pa_pk_as_req(&reqp); 640 if (cksum.contents != NULL) 641 free(cksum.contents); 642 if (der_req != NULL) 643 krb5_free_data(context, der_req); 644 break; 645 case KRB5_PADATA_PK_AS_REP_OLD: 646 case KRB5_PADATA_PK_AS_REQ_OLD: 647 free_krb5_pa_pk_as_req_draft9(&reqp9); 648 } 649 if (tmp_as_req != NULL) 650 k5int_krb5_free_kdc_req(context, tmp_as_req); 651 if (authp_data.data != NULL) 652 free(authp_data.data); 653 if (krb5_authz.data != NULL) 654 free(krb5_authz.data); 655 if (reqctx != NULL) 656 pkinit_fini_kdc_req_context(context, reqctx); 657 if (auth_pack != NULL) 658 free_krb5_auth_pack(&auth_pack); 659 if (auth_pack9 != NULL) 660 free_krb5_auth_pack_draft9(context, &auth_pack9); 661 662 return retval; 663 } 664 665 /* ARGSUSED */ 666 static krb5_error_code 667 pkinit_server_return_padata(krb5_context context, 668 krb5_pa_data * padata, 669 struct _krb5_db_entry_new * client, 670 krb5_data *req_pkt, 671 krb5_kdc_req * request, 672 krb5_kdc_rep * reply, 673 struct _krb5_key_data * client_key, 674 krb5_keyblock * encrypting_key, 675 krb5_pa_data ** send_pa, 676 preauth_get_entry_data_proc server_get_entry_data, 677 void *pa_plugin_context, 678 void **pa_request_context) 679 { 680 krb5_error_code retval = 0; 681 krb5_data scratch = {0, 0, NULL}; 682 krb5_pa_pk_as_req *reqp = NULL; 683 krb5_pa_pk_as_req_draft9 *reqp9 = NULL; 684 int i = 0; 685 686 unsigned char *subjectPublicKey = NULL; 687 unsigned char *dh_pubkey = NULL, *server_key = NULL; 688 unsigned int subjectPublicKey_len = 0; 689 unsigned int server_key_len = 0, dh_pubkey_len = 0; 690 691 krb5_kdc_dh_key_info dhkey_info; 692 krb5_data *encoded_dhkey_info = NULL; 693 krb5_pa_pk_as_rep *rep = NULL; 694 krb5_pa_pk_as_rep_draft9 *rep9 = NULL; 695 krb5_data *out_data = NULL; 696 697 krb5_enctype enctype = -1; 698 699 krb5_reply_key_pack *key_pack = NULL; 700 krb5_reply_key_pack_draft9 *key_pack9 = NULL; 701 krb5_data *encoded_key_pack = NULL; 702 unsigned int num_types; 703 krb5_cksumtype *cksum_types = NULL; 704 705 pkinit_kdc_context plgctx; 706 pkinit_kdc_req_context reqctx; 707 708 int fixed_keypack = 0; 709 710 *send_pa = NULL; 711 /* Solaris Kerberos */ 712 if (padata == NULL || padata->length == 0 || padata->contents == NULL) 713 return 0; 714 715 if (pa_request_context == NULL || *pa_request_context == NULL) { 716 pkiDebug("missing request context \n"); 717 return EINVAL; 718 } 719 720 plgctx = pkinit_find_realm_context(context, pa_plugin_context, 721 request->server); 722 if (plgctx == NULL) { 723 pkiDebug("Unable to locate correct realm context\n"); 724 return ENOENT; 725 } 726 727 pkiDebug("pkinit_return_padata: entered!\n"); 728 reqctx = (pkinit_kdc_req_context)*pa_request_context; 729 730 if (encrypting_key->contents) { 731 free(encrypting_key->contents); 732 encrypting_key->length = 0; 733 encrypting_key->contents = NULL; 734 } 735 736 for(i = 0; i < request->nktypes; i++) { 737 enctype = request->ktype[i]; 738 if (!krb5_c_valid_enctype(enctype)) 739 continue; 740 else { 741 pkiDebug("KDC picked etype = %d\n", enctype); 742 break; 743 } 744 } 745 746 if (i == request->nktypes) { 747 retval = KRB5KDC_ERR_ETYPE_NOSUPP; 748 goto cleanup; 749 } 750 751 switch((int)reqctx->pa_type) { 752 case KRB5_PADATA_PK_AS_REQ: 753 init_krb5_pa_pk_as_rep(&rep); 754 if (rep == NULL) { 755 retval = ENOMEM; 756 goto cleanup; 757 } 758 /* let's assume it's RSA. we'll reset it to DH if needed */ 759 rep->choice = choice_pa_pk_as_rep_encKeyPack; 760 break; 761 case KRB5_PADATA_PK_AS_REP_OLD: 762 case KRB5_PADATA_PK_AS_REQ_OLD: 763 init_krb5_pa_pk_as_rep_draft9(&rep9); 764 if (rep9 == NULL) { 765 retval = ENOMEM; 766 goto cleanup; 767 } 768 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; 769 break; 770 default: 771 retval = KRB5KDC_ERR_PREAUTH_FAILED; 772 goto cleanup; 773 } 774 775 if (reqctx->rcv_auth_pack != NULL && 776 reqctx->rcv_auth_pack->clientPublicValue != NULL) { 777 subjectPublicKey = 778 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data; 779 subjectPublicKey_len = 780 reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length; 781 rep->choice = choice_pa_pk_as_rep_dhInfo; 782 } else if (reqctx->rcv_auth_pack9 != NULL && 783 reqctx->rcv_auth_pack9->clientPublicValue != NULL) { 784 subjectPublicKey = 785 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data; 786 subjectPublicKey_len = 787 reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length; 788 rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData; 789 } 790 791 /* if this DH, then process finish computing DH key */ 792 if (((rep != NULL) && (rep->choice == choice_pa_pk_as_rep_dhInfo)) || 793 ((rep9 != NULL) && rep9->choice == 794 choice_pa_pk_as_rep_draft9_dhSignedData)) { 795 pkiDebug("received DH key delivery AS REQ\n"); 796 retval = server_process_dh(context, plgctx->cryptoctx, 797 reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, 798 subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, 799 &server_key, &server_key_len); 800 if (retval) { 801 pkiDebug("failed to process/create dh paramters\n"); 802 goto cleanup; 803 } 804 } 805 806 if ((rep9 != NULL && 807 rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || 808 (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { 809 retval = pkinit_octetstring2key(context, enctype, server_key, 810 server_key_len, encrypting_key); 811 if (retval) { 812 pkiDebug("pkinit_octetstring2key failed: %s\n", 813 error_message(retval)); 814 goto cleanup; 815 } 816 817 dhkey_info.subjectPublicKey.length = dh_pubkey_len; 818 dhkey_info.subjectPublicKey.data = dh_pubkey; 819 dhkey_info.nonce = request->nonce; 820 dhkey_info.dhKeyExpiration = 0; 821 822 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, 823 &encoded_dhkey_info); 824 if (retval) { 825 pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); 826 goto cleanup; 827 } 828 #ifdef DEBUG_ASN1 829 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, 830 encoded_dhkey_info->length, 831 "/tmp/kdc_dh_key_info"); 832 #endif 833 834 switch ((int)padata->pa_type) { 835 case KRB5_PADATA_PK_AS_REQ: 836 retval = cms_signeddata_create(context, plgctx->cryptoctx, 837 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, 838 (unsigned char *)encoded_dhkey_info->data, 839 encoded_dhkey_info->length, 840 &rep->u.dh_Info.dhSignedData.data, 841 &rep->u.dh_Info.dhSignedData.length); 842 if (retval) { 843 pkiDebug("failed to create pkcs7 signed data\n"); 844 goto cleanup; 845 } 846 break; 847 case KRB5_PADATA_PK_AS_REP_OLD: 848 case KRB5_PADATA_PK_AS_REQ_OLD: 849 retval = cms_signeddata_create(context, plgctx->cryptoctx, 850 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, 851 (unsigned char *)encoded_dhkey_info->data, 852 encoded_dhkey_info->length, 853 &rep9->u.dhSignedData.data, 854 &rep9->u.dhSignedData.length); 855 if (retval) { 856 pkiDebug("failed to create pkcs7 signed data\n"); 857 goto cleanup; 858 } 859 break; 860 } 861 } else { 862 pkiDebug("received RSA key delivery AS REQ\n"); 863 864 retval = krb5_c_make_random_key(context, enctype, encrypting_key); 865 if (retval) { 866 pkiDebug("unable to make a session key\n"); 867 goto cleanup; 868 } 869 870 /* check if PA_TYPE of 132 is present which means the client is 871 * requesting that a checksum is send back instead of the nonce 872 */ 873 for (i = 0; request->padata[i] != NULL; i++) { 874 pkiDebug("%s: Checking pa_type 0x%08x\n", 875 __FUNCTION__, request->padata[i]->pa_type); 876 if (request->padata[i]->pa_type == 132) 877 fixed_keypack = 1; 878 } 879 pkiDebug("%s: return checksum instead of nonce = %d\n", 880 __FUNCTION__, fixed_keypack); 881 882 /* if this is an RFC reply or draft9 client requested a checksum 883 * in the reply instead of the nonce, create an RFC-style keypack 884 */ 885 if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { 886 init_krb5_reply_key_pack(&key_pack); 887 if (key_pack == NULL) { 888 retval = ENOMEM; 889 goto cleanup; 890 } 891 /* retrieve checksums for a given enctype of the reply key */ 892 retval = krb5_c_keyed_checksum_types(context, 893 encrypting_key->enctype, &num_types, &cksum_types); 894 if (retval) 895 goto cleanup; 896 897 /* pick the first of acceptable enctypes for the checksum */ 898 retval = krb5_c_make_checksum(context, cksum_types[0], 899 encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 900 req_pkt, &key_pack->asChecksum); 901 if (retval) { 902 pkiDebug("unable to calculate AS REQ checksum\n"); 903 goto cleanup; 904 } 905 #ifdef DEBUG_CKSUM 906 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); 907 print_buffer(req_pkt->data, req_pkt->length); 908 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); 909 print_buffer(key_pack->asChecksum.contents, 910 key_pack->asChecksum.length); 911 pkiDebug("encrypting key (%d)\n", encrypting_key->length); 912 print_buffer(encrypting_key->contents, encrypting_key->length); 913 #endif 914 915 krb5_copy_keyblock_contents(context, encrypting_key, 916 &key_pack->replyKey); 917 918 retval = k5int_encode_krb5_reply_key_pack(key_pack, 919 &encoded_key_pack); 920 if (retval) { 921 pkiDebug("failed to encode reply_key_pack\n"); 922 goto cleanup; 923 } 924 } 925 926 switch ((int)padata->pa_type) { 927 case KRB5_PADATA_PK_AS_REQ: 928 rep->choice = choice_pa_pk_as_rep_encKeyPack; 929 retval = cms_envelopeddata_create(context, plgctx->cryptoctx, 930 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 931 (unsigned char *)encoded_key_pack->data, 932 encoded_key_pack->length, 933 &rep->u.encKeyPack.data, &rep->u.encKeyPack.length); 934 break; 935 case KRB5_PADATA_PK_AS_REP_OLD: 936 case KRB5_PADATA_PK_AS_REQ_OLD: 937 /* if the request is from the broken draft9 client that 938 * expects back a nonce, create it now 939 */ 940 if (!fixed_keypack) { 941 init_krb5_reply_key_pack_draft9(&key_pack9); 942 if (key_pack9 == NULL) { 943 retval = ENOMEM; 944 goto cleanup; 945 } 946 key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; 947 krb5_copy_keyblock_contents(context, encrypting_key, 948 &key_pack9->replyKey); 949 950 retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, 951 &encoded_key_pack); 952 if (retval) { 953 pkiDebug("failed to encode reply_key_pack\n"); 954 goto cleanup; 955 } 956 } 957 958 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; 959 retval = cms_envelopeddata_create(context, plgctx->cryptoctx, 960 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 961 (unsigned char *)encoded_key_pack->data, 962 encoded_key_pack->length, 963 &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); 964 break; 965 } 966 if (retval) { 967 pkiDebug("failed to create pkcs7 enveloped data: %s\n", 968 error_message(retval)); 969 goto cleanup; 970 } 971 #ifdef DEBUG_ASN1 972 print_buffer_bin((unsigned char *)encoded_key_pack->data, 973 encoded_key_pack->length, 974 "/tmp/kdc_key_pack"); 975 switch ((int)padata->pa_type) { 976 case KRB5_PADATA_PK_AS_REQ: 977 print_buffer_bin(rep->u.encKeyPack.data, 978 rep->u.encKeyPack.length, 979 "/tmp/kdc_enc_key_pack"); 980 break; 981 case KRB5_PADATA_PK_AS_REP_OLD: 982 case KRB5_PADATA_PK_AS_REQ_OLD: 983 print_buffer_bin(rep9->u.encKeyPack.data, 984 rep9->u.encKeyPack.length, 985 "/tmp/kdc_enc_key_pack"); 986 break; 987 } 988 #endif 989 } 990 991 switch ((int)padata->pa_type) { 992 case KRB5_PADATA_PK_AS_REQ: 993 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); 994 break; 995 case KRB5_PADATA_PK_AS_REP_OLD: 996 case KRB5_PADATA_PK_AS_REQ_OLD: 997 retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); 998 break; 999 } 1000 if (retval) { 1001 pkiDebug("failed to encode AS_REP\n"); 1002 goto cleanup; 1003 } 1004 #ifdef DEBUG_ASN1 1005 if (out_data != NULL) 1006 print_buffer_bin((unsigned char *)out_data->data, out_data->length, 1007 "/tmp/kdc_as_rep"); 1008 #endif 1009 1010 *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); 1011 if (*send_pa == NULL) { 1012 retval = ENOMEM; 1013 free(out_data->data); 1014 free(out_data); 1015 out_data = NULL; 1016 goto cleanup; 1017 } 1018 (*send_pa)->magic = KV5M_PA_DATA; 1019 switch ((int)padata->pa_type) { 1020 case KRB5_PADATA_PK_AS_REQ: 1021 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; 1022 break; 1023 case KRB5_PADATA_PK_AS_REQ_OLD: 1024 case KRB5_PADATA_PK_AS_REP_OLD: 1025 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; 1026 break; 1027 } 1028 (*send_pa)->length = out_data->length; 1029 (*send_pa)->contents = (krb5_octet *) out_data->data; 1030 1031 1032 cleanup: 1033 pkinit_fini_kdc_req_context(context, reqctx); 1034 if (scratch.data != NULL) 1035 free(scratch.data); 1036 if (out_data != NULL) 1037 free(out_data); 1038 if (encoded_dhkey_info != NULL) 1039 krb5_free_data(context, encoded_dhkey_info); 1040 if (encoded_key_pack != NULL) 1041 krb5_free_data(context, encoded_key_pack); 1042 if (dh_pubkey != NULL) 1043 free(dh_pubkey); 1044 if (server_key != NULL) 1045 free(server_key); 1046 if (cksum_types != NULL) 1047 free(cksum_types); 1048 1049 switch ((int)padata->pa_type) { 1050 case KRB5_PADATA_PK_AS_REQ: 1051 free_krb5_pa_pk_as_req(&reqp); 1052 free_krb5_pa_pk_as_rep(&rep); 1053 free_krb5_reply_key_pack(&key_pack); 1054 break; 1055 case KRB5_PADATA_PK_AS_REP_OLD: 1056 case KRB5_PADATA_PK_AS_REQ_OLD: 1057 free_krb5_pa_pk_as_req_draft9(&reqp9); 1058 free_krb5_pa_pk_as_rep_draft9(&rep9); 1059 if (!fixed_keypack) 1060 free_krb5_reply_key_pack_draft9(&key_pack9); 1061 else 1062 free_krb5_reply_key_pack(&key_pack); 1063 break; 1064 } 1065 1066 if (retval) 1067 pkiDebug("pkinit_verify_padata failure"); 1068 1069 return retval; 1070 } 1071 1072 /* ARGSUSED */ 1073 static int 1074 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) 1075 { 1076 return PA_SUFFICIENT | PA_REPLACES_KEY; 1077 } 1078 1079 static krb5_preauthtype supported_server_pa_types[] = { 1080 KRB5_PADATA_PK_AS_REQ, 1081 KRB5_PADATA_PK_AS_REQ_OLD, 1082 KRB5_PADATA_PK_AS_REP_OLD, 1083 0 1084 }; 1085 1086 /* ARGSUSED */ 1087 static void 1088 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1089 { 1090 /* 1091 * There is nothing currently allocated by pkinit_init_kdc_profile() 1092 * which needs to be freed here. 1093 */ 1094 } 1095 1096 static krb5_error_code 1097 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1098 { 1099 krb5_error_code retval; 1100 char *eku_string = NULL; 1101 1102 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname); 1103 retval = pkinit_kdcdefault_string(context, plgctx->realmname, 1104 "pkinit_identity", 1105 &plgctx->idopts->identity); 1106 if (retval != 0 || NULL == plgctx->idopts->identity) { 1107 retval = EINVAL; 1108 krb5_set_error_message(context, retval, 1109 "No pkinit_identity supplied for realm %s", 1110 plgctx->realmname); 1111 goto errout; 1112 } 1113 1114 retval = pkinit_kdcdefault_strings(context, plgctx->realmname, 1115 "pkinit_anchors", 1116 &plgctx->idopts->anchors); 1117 if (retval != 0 || NULL == plgctx->idopts->anchors) { 1118 retval = EINVAL; 1119 krb5_set_error_message(context, retval, 1120 "No pkinit_anchors supplied for realm %s", 1121 plgctx->realmname); 1122 goto errout; 1123 } 1124 1125 /* Solaris Kerberos */ 1126 (void) pkinit_kdcdefault_strings(context, plgctx->realmname, 1127 "pkinit_pool", 1128 &plgctx->idopts->intermediates); 1129 1130 (void) pkinit_kdcdefault_strings(context, plgctx->realmname, 1131 "pkinit_revoke", 1132 &plgctx->idopts->crls); 1133 1134 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1135 "pkinit_kdc_ocsp", 1136 &plgctx->idopts->ocsp); 1137 1138 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1139 "pkinit_mappings_file", 1140 &plgctx->idopts->dn_mapping_file); 1141 1142 (void) pkinit_kdcdefault_integer(context, plgctx->realmname, 1143 "pkinit_dh_min_bits", 1144 PKINIT_DEFAULT_DH_MIN_BITS, 1145 &plgctx->opts->dh_min_bits); 1146 if (plgctx->opts->dh_min_bits < 1024) { 1147 pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " 1148 "using default value (%d) instead\n", __FUNCTION__, 1149 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS); 1150 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; 1151 } 1152 1153 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, 1154 "pkinit_allow_upn", 1155 0, &plgctx->opts->allow_upn); 1156 1157 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, 1158 "pkinit_require_crl_checking", 1159 0, &plgctx->opts->require_crl_checking); 1160 1161 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1162 "pkinit_eku_checking", 1163 &eku_string); 1164 if (eku_string != NULL) { 1165 if (strcasecmp(eku_string, "kpClientAuth") == 0) { 1166 plgctx->opts->require_eku = 1; 1167 plgctx->opts->accept_secondary_eku = 0; 1168 } else if (strcasecmp(eku_string, "scLogin") == 0) { 1169 plgctx->opts->require_eku = 1; 1170 plgctx->opts->accept_secondary_eku = 1; 1171 } else if (strcasecmp(eku_string, "none") == 0) { 1172 plgctx->opts->require_eku = 0; 1173 plgctx->opts->accept_secondary_eku = 0; 1174 } else { 1175 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", 1176 __FUNCTION__, eku_string); 1177 } 1178 free(eku_string); 1179 } 1180 1181 1182 return 0; 1183 errout: 1184 pkinit_fini_kdc_profile(context, plgctx); 1185 return retval; 1186 } 1187 1188 /* ARGSUSED */ 1189 static pkinit_kdc_context 1190 pkinit_find_realm_context(krb5_context context, void *pa_plugin_context, 1191 krb5_principal princ) 1192 { 1193 int i; 1194 pkinit_kdc_context *realm_contexts = pa_plugin_context; 1195 1196 if (pa_plugin_context == NULL) 1197 return NULL; 1198 1199 for (i = 0; realm_contexts[i] != NULL; i++) { 1200 pkinit_kdc_context p = realm_contexts[i]; 1201 1202 if ((p->realmname_len == princ->realm.length) && 1203 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) { 1204 pkiDebug("%s: returning context at %p for realm '%s'\n", 1205 __FUNCTION__, p, p->realmname); 1206 return p; 1207 } 1208 } 1209 pkiDebug("%s: unable to find realm context for realm '%.*s'\n", 1210 __FUNCTION__, princ->realm.length, princ->realm.data); 1211 return NULL; 1212 } 1213 1214 static int 1215 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname, 1216 pkinit_kdc_context *pplgctx) 1217 { 1218 krb5_error_code retval = ENOMEM; 1219 pkinit_kdc_context plgctx = NULL; 1220 1221 *pplgctx = NULL; 1222 1223 plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx)); 1224 if (plgctx == NULL) 1225 goto errout; 1226 1227 pkiDebug("%s: initializing context at %p for realm '%s'\n", 1228 __FUNCTION__, plgctx, realmname); 1229 (void) memset(plgctx, 0, sizeof(*plgctx)); 1230 plgctx->magic = PKINIT_CTX_MAGIC; 1231 1232 plgctx->realmname = strdup(realmname); 1233 if (plgctx->realmname == NULL) 1234 goto errout; 1235 plgctx->realmname_len = strlen(plgctx->realmname); 1236 1237 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx); 1238 if (retval) 1239 goto errout; 1240 1241 retval = pkinit_init_plg_opts(&plgctx->opts); 1242 if (retval) 1243 goto errout; 1244 1245 retval = pkinit_init_identity_crypto(&plgctx->idctx); 1246 if (retval) 1247 goto errout; 1248 1249 retval = pkinit_init_identity_opts(&plgctx->idopts); 1250 if (retval) 1251 goto errout; 1252 1253 retval = pkinit_init_kdc_profile(context, plgctx); 1254 if (retval) 1255 goto errout; 1256 1257 /* 1258 * Solaris Kerberos: 1259 * Some methods of storing key information (PKCS11, PKCS12,...) may 1260 * require interactive prompting. 1261 */ 1262 retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix, 1263 NULL); 1264 if (retval) 1265 goto errout; 1266 1267 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL, 1268 plgctx->idopts, plgctx->idctx, 0, NULL); 1269 if (retval) 1270 goto errout; 1271 1272 pkiDebug("%s: returning context at %p for realm '%s'\n", 1273 __FUNCTION__, plgctx, realmname); 1274 *pplgctx = plgctx; 1275 retval = 0; 1276 1277 errout: 1278 if (retval) 1279 pkinit_server_plugin_fini_realm(context, plgctx); 1280 1281 return retval; 1282 } 1283 1284 static int 1285 pkinit_server_plugin_init(krb5_context context, void **blob, 1286 const char **realmnames) 1287 { 1288 krb5_error_code retval = ENOMEM; 1289 pkinit_kdc_context plgctx, *realm_contexts = NULL; 1290 int i, j; 1291 size_t numrealms; 1292 1293 retval = pkinit_accessor_init(); 1294 if (retval) 1295 return retval; 1296 1297 /* Determine how many realms we may need to support */ 1298 for (i = 0; realmnames[i] != NULL; i++) {}; 1299 numrealms = i; 1300 1301 realm_contexts = (pkinit_kdc_context *) 1302 calloc(numrealms+1, sizeof(pkinit_kdc_context)); 1303 if (realm_contexts == NULL) 1304 return ENOMEM; 1305 1306 for (i = 0, j = 0; i < numrealms; i++) { 1307 pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]); 1308 retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx); 1309 if (retval == 0 && plgctx != NULL) 1310 realm_contexts[j++] = plgctx; 1311 } 1312 1313 if (j == 0) { 1314 /* 1315 * Solaris Kerberos 1316 * Improve error messages for the common case of a single realm 1317 */ 1318 if (numrealms != 1) { 1319 retval = EINVAL; 1320 krb5_set_error_message(context, retval, "No realms configured " 1321 "correctly for pkinit support"); 1322 } 1323 1324 goto errout; 1325 } 1326 1327 *blob = realm_contexts; 1328 retval = 0; 1329 pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts); 1330 1331 errout: 1332 if (retval) 1333 pkinit_server_plugin_fini(context, realm_contexts); 1334 1335 return retval; 1336 } 1337 1338 static void 1339 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx) 1340 { 1341 if (plgctx == NULL) 1342 return; 1343 1344 pkinit_fini_kdc_profile(context, plgctx); 1345 pkinit_fini_identity_opts(plgctx->idopts); 1346 pkinit_fini_identity_crypto(plgctx->idctx); 1347 pkinit_fini_plg_crypto(plgctx->cryptoctx); 1348 pkinit_fini_plg_opts(plgctx->opts); 1349 free(plgctx->realmname); 1350 free(plgctx); 1351 } 1352 1353 static void 1354 pkinit_server_plugin_fini(krb5_context context, void *blob) 1355 { 1356 pkinit_kdc_context *realm_contexts = blob; 1357 int i; 1358 1359 if (realm_contexts == NULL) 1360 return; 1361 1362 for (i = 0; realm_contexts[i] != NULL; i++) { 1363 pkinit_server_plugin_fini_realm(context, realm_contexts[i]); 1364 } 1365 pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); 1366 free(realm_contexts); 1367 } 1368 1369 static krb5_error_code 1370 pkinit_init_kdc_req_context(krb5_context context, void **ctx) 1371 { 1372 krb5_error_code retval = ENOMEM; 1373 pkinit_kdc_req_context reqctx = NULL; 1374 1375 reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx)); 1376 if (reqctx == NULL) 1377 return retval; 1378 (void) memset(reqctx, 0, sizeof(*reqctx)); 1379 reqctx->magic = PKINIT_CTX_MAGIC; 1380 1381 retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 1382 if (retval) 1383 goto cleanup; 1384 reqctx->rcv_auth_pack = NULL; 1385 reqctx->rcv_auth_pack9 = NULL; 1386 1387 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); 1388 *ctx = reqctx; 1389 retval = 0; 1390 cleanup: 1391 if (retval) 1392 pkinit_fini_kdc_req_context(context, reqctx); 1393 1394 return retval; 1395 } 1396 1397 static void 1398 pkinit_fini_kdc_req_context(krb5_context context, void *ctx) 1399 { 1400 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx; 1401 1402 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) { 1403 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx); 1404 return; 1405 } 1406 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx); 1407 1408 pkinit_fini_req_crypto(reqctx->cryptoctx); 1409 if (reqctx->rcv_auth_pack != NULL) 1410 free_krb5_auth_pack(&reqctx->rcv_auth_pack); 1411 if (reqctx->rcv_auth_pack9 != NULL) 1412 free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9); 1413 1414 free(reqctx); 1415 } 1416 1417 struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = { 1418 "pkinit", /* name */ 1419 supported_server_pa_types, /* pa_type_list */ 1420 pkinit_server_plugin_init, /* (*init_proc) */ 1421 pkinit_server_plugin_fini, /* (*fini_proc) */ 1422 pkinit_server_get_flags, /* (*flags_proc) */ 1423 pkinit_server_get_edata, /* (*edata_proc) */ 1424 pkinit_server_verify_padata,/* (*verify_proc) */ 1425 pkinit_server_return_padata,/* (*return_proc) */ 1426 NULL, /* (*freepa_reqcontext_proc) */ 1427 }; 1428