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 rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) { 794 pkiDebug("received DH key delivery AS REQ\n"); 795 retval = server_process_dh(context, plgctx->cryptoctx, 796 reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, 797 subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, 798 &server_key, &server_key_len); 799 if (retval) { 800 pkiDebug("failed to process/create dh paramters\n"); 801 goto cleanup; 802 } 803 } 804 805 if ((rep9 != NULL && 806 rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || 807 (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { 808 retval = pkinit_octetstring2key(context, enctype, server_key, 809 server_key_len, encrypting_key); 810 if (retval) { 811 pkiDebug("pkinit_octetstring2key failed: %s\n", 812 error_message(retval)); 813 goto cleanup; 814 } 815 816 dhkey_info.subjectPublicKey.length = dh_pubkey_len; 817 dhkey_info.subjectPublicKey.data = dh_pubkey; 818 dhkey_info.nonce = request->nonce; 819 dhkey_info.dhKeyExpiration = 0; 820 821 retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, 822 &encoded_dhkey_info); 823 if (retval) { 824 pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); 825 goto cleanup; 826 } 827 #ifdef DEBUG_ASN1 828 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, 829 encoded_dhkey_info->length, 830 "/tmp/kdc_dh_key_info"); 831 #endif 832 833 switch ((int)padata->pa_type) { 834 case KRB5_PADATA_PK_AS_REQ: 835 retval = cms_signeddata_create(context, plgctx->cryptoctx, 836 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, 837 (unsigned char *)encoded_dhkey_info->data, 838 encoded_dhkey_info->length, 839 &rep->u.dh_Info.dhSignedData.data, 840 &rep->u.dh_Info.dhSignedData.length); 841 if (retval) { 842 pkiDebug("failed to create pkcs7 signed data\n"); 843 goto cleanup; 844 } 845 break; 846 case KRB5_PADATA_PK_AS_REP_OLD: 847 case KRB5_PADATA_PK_AS_REQ_OLD: 848 retval = cms_signeddata_create(context, plgctx->cryptoctx, 849 reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, 850 (unsigned char *)encoded_dhkey_info->data, 851 encoded_dhkey_info->length, 852 &rep9->u.dhSignedData.data, 853 &rep9->u.dhSignedData.length); 854 if (retval) { 855 pkiDebug("failed to create pkcs7 signed data\n"); 856 goto cleanup; 857 } 858 break; 859 } 860 } else { 861 pkiDebug("received RSA key delivery AS REQ\n"); 862 863 retval = krb5_c_make_random_key(context, enctype, encrypting_key); 864 if (retval) { 865 pkiDebug("unable to make a session key\n"); 866 goto cleanup; 867 } 868 869 /* check if PA_TYPE of 132 is present which means the client is 870 * requesting that a checksum is send back instead of the nonce 871 */ 872 for (i = 0; request->padata[i] != NULL; i++) { 873 pkiDebug("%s: Checking pa_type 0x%08x\n", 874 __FUNCTION__, request->padata[i]->pa_type); 875 if (request->padata[i]->pa_type == 132) 876 fixed_keypack = 1; 877 } 878 pkiDebug("%s: return checksum instead of nonce = %d\n", 879 __FUNCTION__, fixed_keypack); 880 881 /* if this is an RFC reply or draft9 client requested a checksum 882 * in the reply instead of the nonce, create an RFC-style keypack 883 */ 884 if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { 885 init_krb5_reply_key_pack(&key_pack); 886 if (key_pack == NULL) { 887 retval = ENOMEM; 888 goto cleanup; 889 } 890 /* retrieve checksums for a given enctype of the reply key */ 891 retval = krb5_c_keyed_checksum_types(context, 892 encrypting_key->enctype, &num_types, &cksum_types); 893 if (retval) 894 goto cleanup; 895 896 /* pick the first of acceptable enctypes for the checksum */ 897 retval = krb5_c_make_checksum(context, cksum_types[0], 898 encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, 899 req_pkt, &key_pack->asChecksum); 900 if (retval) { 901 pkiDebug("unable to calculate AS REQ checksum\n"); 902 goto cleanup; 903 } 904 #ifdef DEBUG_CKSUM 905 pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); 906 print_buffer(req_pkt->data, req_pkt->length); 907 pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); 908 print_buffer(key_pack->asChecksum.contents, 909 key_pack->asChecksum.length); 910 pkiDebug("encrypting key (%d)\n", encrypting_key->length); 911 print_buffer(encrypting_key->contents, encrypting_key->length); 912 #endif 913 914 krb5_copy_keyblock_contents(context, encrypting_key, 915 &key_pack->replyKey); 916 917 retval = k5int_encode_krb5_reply_key_pack(key_pack, 918 &encoded_key_pack); 919 if (retval) { 920 pkiDebug("failed to encode reply_key_pack\n"); 921 goto cleanup; 922 } 923 } 924 925 switch ((int)padata->pa_type) { 926 case KRB5_PADATA_PK_AS_REQ: 927 rep->choice = choice_pa_pk_as_rep_encKeyPack; 928 retval = cms_envelopeddata_create(context, plgctx->cryptoctx, 929 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 930 (unsigned char *)encoded_key_pack->data, 931 encoded_key_pack->length, 932 &rep->u.encKeyPack.data, &rep->u.encKeyPack.length); 933 break; 934 case KRB5_PADATA_PK_AS_REP_OLD: 935 case KRB5_PADATA_PK_AS_REQ_OLD: 936 /* if the request is from the broken draft9 client that 937 * expects back a nonce, create it now 938 */ 939 if (!fixed_keypack) { 940 init_krb5_reply_key_pack_draft9(&key_pack9); 941 if (key_pack9 == NULL) { 942 retval = ENOMEM; 943 goto cleanup; 944 } 945 key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; 946 krb5_copy_keyblock_contents(context, encrypting_key, 947 &key_pack9->replyKey); 948 949 retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, 950 &encoded_key_pack); 951 if (retval) { 952 pkiDebug("failed to encode reply_key_pack\n"); 953 goto cleanup; 954 } 955 } 956 957 rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; 958 retval = cms_envelopeddata_create(context, plgctx->cryptoctx, 959 reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 960 (unsigned char *)encoded_key_pack->data, 961 encoded_key_pack->length, 962 &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); 963 break; 964 } 965 if (retval) { 966 pkiDebug("failed to create pkcs7 enveloped data: %s\n", 967 error_message(retval)); 968 goto cleanup; 969 } 970 #ifdef DEBUG_ASN1 971 print_buffer_bin((unsigned char *)encoded_key_pack->data, 972 encoded_key_pack->length, 973 "/tmp/kdc_key_pack"); 974 switch ((int)padata->pa_type) { 975 case KRB5_PADATA_PK_AS_REQ: 976 print_buffer_bin(rep->u.encKeyPack.data, 977 rep->u.encKeyPack.length, 978 "/tmp/kdc_enc_key_pack"); 979 break; 980 case KRB5_PADATA_PK_AS_REP_OLD: 981 case KRB5_PADATA_PK_AS_REQ_OLD: 982 print_buffer_bin(rep9->u.encKeyPack.data, 983 rep9->u.encKeyPack.length, 984 "/tmp/kdc_enc_key_pack"); 985 break; 986 } 987 #endif 988 } 989 990 switch ((int)padata->pa_type) { 991 case KRB5_PADATA_PK_AS_REQ: 992 retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); 993 break; 994 case KRB5_PADATA_PK_AS_REP_OLD: 995 case KRB5_PADATA_PK_AS_REQ_OLD: 996 retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); 997 break; 998 } 999 if (retval) { 1000 pkiDebug("failed to encode AS_REP\n"); 1001 goto cleanup; 1002 } 1003 #ifdef DEBUG_ASN1 1004 if (out_data != NULL) 1005 print_buffer_bin((unsigned char *)out_data->data, out_data->length, 1006 "/tmp/kdc_as_rep"); 1007 #endif 1008 1009 *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); 1010 if (*send_pa == NULL) { 1011 retval = ENOMEM; 1012 free(out_data->data); 1013 free(out_data); 1014 out_data = NULL; 1015 goto cleanup; 1016 } 1017 (*send_pa)->magic = KV5M_PA_DATA; 1018 switch ((int)padata->pa_type) { 1019 case KRB5_PADATA_PK_AS_REQ: 1020 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; 1021 break; 1022 case KRB5_PADATA_PK_AS_REQ_OLD: 1023 case KRB5_PADATA_PK_AS_REP_OLD: 1024 (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; 1025 break; 1026 } 1027 (*send_pa)->length = out_data->length; 1028 (*send_pa)->contents = (krb5_octet *) out_data->data; 1029 1030 1031 cleanup: 1032 pkinit_fini_kdc_req_context(context, reqctx); 1033 if (scratch.data != NULL) 1034 free(scratch.data); 1035 if (out_data != NULL) 1036 free(out_data); 1037 if (encoded_dhkey_info != NULL) 1038 krb5_free_data(context, encoded_dhkey_info); 1039 if (encoded_key_pack != NULL) 1040 krb5_free_data(context, encoded_key_pack); 1041 if (dh_pubkey != NULL) 1042 free(dh_pubkey); 1043 if (server_key != NULL) 1044 free(server_key); 1045 if (cksum_types != NULL) 1046 free(cksum_types); 1047 1048 switch ((int)padata->pa_type) { 1049 case KRB5_PADATA_PK_AS_REQ: 1050 free_krb5_pa_pk_as_req(&reqp); 1051 free_krb5_pa_pk_as_rep(&rep); 1052 free_krb5_reply_key_pack(&key_pack); 1053 break; 1054 case KRB5_PADATA_PK_AS_REP_OLD: 1055 case KRB5_PADATA_PK_AS_REQ_OLD: 1056 free_krb5_pa_pk_as_req_draft9(&reqp9); 1057 free_krb5_pa_pk_as_rep_draft9(&rep9); 1058 if (!fixed_keypack) 1059 free_krb5_reply_key_pack_draft9(&key_pack9); 1060 else 1061 free_krb5_reply_key_pack(&key_pack); 1062 break; 1063 } 1064 1065 if (retval) 1066 pkiDebug("pkinit_verify_padata failure"); 1067 1068 return retval; 1069 } 1070 1071 /* ARGSUSED */ 1072 static int 1073 pkinit_server_get_flags(krb5_context kcontext, krb5_preauthtype patype) 1074 { 1075 return PA_SUFFICIENT | PA_REPLACES_KEY; 1076 } 1077 1078 static krb5_preauthtype supported_server_pa_types[] = { 1079 KRB5_PADATA_PK_AS_REQ, 1080 KRB5_PADATA_PK_AS_REQ_OLD, 1081 KRB5_PADATA_PK_AS_REP_OLD, 1082 0 1083 }; 1084 1085 /* ARGSUSED */ 1086 static void 1087 pkinit_fini_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1088 { 1089 /* 1090 * There is nothing currently allocated by pkinit_init_kdc_profile() 1091 * which needs to be freed here. 1092 */ 1093 } 1094 1095 static krb5_error_code 1096 pkinit_init_kdc_profile(krb5_context context, pkinit_kdc_context plgctx) 1097 { 1098 krb5_error_code retval; 1099 char *eku_string = NULL; 1100 1101 pkiDebug("%s: entered for realm %s\n", __FUNCTION__, plgctx->realmname); 1102 retval = pkinit_kdcdefault_string(context, plgctx->realmname, 1103 "pkinit_identity", 1104 &plgctx->idopts->identity); 1105 if (retval != 0 || NULL == plgctx->idopts->identity) { 1106 retval = EINVAL; 1107 krb5_set_error_message(context, retval, 1108 "No pkinit_identity supplied for realm %s", 1109 plgctx->realmname); 1110 goto errout; 1111 } 1112 1113 retval = pkinit_kdcdefault_strings(context, plgctx->realmname, 1114 "pkinit_anchors", 1115 &plgctx->idopts->anchors); 1116 if (retval != 0 || NULL == plgctx->idopts->anchors) { 1117 retval = EINVAL; 1118 krb5_set_error_message(context, retval, 1119 "No pkinit_anchors supplied for realm %s", 1120 plgctx->realmname); 1121 goto errout; 1122 } 1123 1124 /* Solaris Kerberos */ 1125 (void) pkinit_kdcdefault_strings(context, plgctx->realmname, 1126 "pkinit_pool", 1127 &plgctx->idopts->intermediates); 1128 1129 (void) pkinit_kdcdefault_strings(context, plgctx->realmname, 1130 "pkinit_revoke", 1131 &plgctx->idopts->crls); 1132 1133 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1134 "pkinit_kdc_ocsp", 1135 &plgctx->idopts->ocsp); 1136 1137 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1138 "pkinit_mappings_file", 1139 &plgctx->idopts->dn_mapping_file); 1140 1141 (void) pkinit_kdcdefault_integer(context, plgctx->realmname, 1142 "pkinit_dh_min_bits", 1143 PKINIT_DEFAULT_DH_MIN_BITS, 1144 &plgctx->opts->dh_min_bits); 1145 if (plgctx->opts->dh_min_bits < 1024) { 1146 pkiDebug("%s: invalid value (%d) for pkinit_dh_min_bits, " 1147 "using default value (%d) instead\n", __FUNCTION__, 1148 plgctx->opts->dh_min_bits, PKINIT_DEFAULT_DH_MIN_BITS); 1149 plgctx->opts->dh_min_bits = PKINIT_DEFAULT_DH_MIN_BITS; 1150 } 1151 1152 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, 1153 "pkinit_allow_upn", 1154 0, &plgctx->opts->allow_upn); 1155 1156 (void) pkinit_kdcdefault_boolean(context, plgctx->realmname, 1157 "pkinit_require_crl_checking", 1158 0, &plgctx->opts->require_crl_checking); 1159 1160 (void) pkinit_kdcdefault_string(context, plgctx->realmname, 1161 "pkinit_eku_checking", 1162 &eku_string); 1163 if (eku_string != NULL) { 1164 if (strcasecmp(eku_string, "kpClientAuth") == 0) { 1165 plgctx->opts->require_eku = 1; 1166 plgctx->opts->accept_secondary_eku = 0; 1167 } else if (strcasecmp(eku_string, "scLogin") == 0) { 1168 plgctx->opts->require_eku = 1; 1169 plgctx->opts->accept_secondary_eku = 1; 1170 } else if (strcasecmp(eku_string, "none") == 0) { 1171 plgctx->opts->require_eku = 0; 1172 plgctx->opts->accept_secondary_eku = 0; 1173 } else { 1174 pkiDebug("%s: Invalid value for pkinit_eku_checking: '%s'\n", 1175 __FUNCTION__, eku_string); 1176 } 1177 free(eku_string); 1178 } 1179 1180 1181 return 0; 1182 errout: 1183 pkinit_fini_kdc_profile(context, plgctx); 1184 return retval; 1185 } 1186 1187 /* ARGSUSED */ 1188 static pkinit_kdc_context 1189 pkinit_find_realm_context(krb5_context context, void *pa_plugin_context, 1190 krb5_principal princ) 1191 { 1192 int i; 1193 pkinit_kdc_context *realm_contexts = pa_plugin_context; 1194 1195 if (pa_plugin_context == NULL) 1196 return NULL; 1197 1198 for (i = 0; realm_contexts[i] != NULL; i++) { 1199 pkinit_kdc_context p = realm_contexts[i]; 1200 1201 if ((p->realmname_len == princ->realm.length) && 1202 (strncmp(p->realmname, princ->realm.data, p->realmname_len) == 0)) { 1203 pkiDebug("%s: returning context at %p for realm '%s'\n", 1204 __FUNCTION__, p, p->realmname); 1205 return p; 1206 } 1207 } 1208 pkiDebug("%s: unable to find realm context for realm '%.*s'\n", 1209 __FUNCTION__, princ->realm.length, princ->realm.data); 1210 return NULL; 1211 } 1212 1213 static int 1214 pkinit_server_plugin_init_realm(krb5_context context, const char *realmname, 1215 pkinit_kdc_context *pplgctx) 1216 { 1217 krb5_error_code retval = ENOMEM; 1218 pkinit_kdc_context plgctx = NULL; 1219 1220 *pplgctx = NULL; 1221 1222 plgctx = (pkinit_kdc_context) calloc(1, sizeof(*plgctx)); 1223 if (plgctx == NULL) 1224 goto errout; 1225 1226 pkiDebug("%s: initializing context at %p for realm '%s'\n", 1227 __FUNCTION__, plgctx, realmname); 1228 (void) memset(plgctx, 0, sizeof(*plgctx)); 1229 plgctx->magic = PKINIT_CTX_MAGIC; 1230 1231 plgctx->realmname = strdup(realmname); 1232 if (plgctx->realmname == NULL) 1233 goto errout; 1234 plgctx->realmname_len = strlen(plgctx->realmname); 1235 1236 retval = pkinit_init_plg_crypto(&plgctx->cryptoctx); 1237 if (retval) 1238 goto errout; 1239 1240 retval = pkinit_init_plg_opts(&plgctx->opts); 1241 if (retval) 1242 goto errout; 1243 1244 retval = pkinit_init_identity_crypto(&plgctx->idctx); 1245 if (retval) 1246 goto errout; 1247 1248 retval = pkinit_init_identity_opts(&plgctx->idopts); 1249 if (retval) 1250 goto errout; 1251 1252 retval = pkinit_init_kdc_profile(context, plgctx); 1253 if (retval) 1254 goto errout; 1255 1256 /* 1257 * Solaris Kerberos: 1258 * Some methods of storing key information (PKCS11, PKCS12,...) may 1259 * require interactive prompting. 1260 */ 1261 retval = pkinit_identity_set_prompter(plgctx->idctx, krb5_prompter_posix, 1262 NULL); 1263 if (retval) 1264 goto errout; 1265 1266 retval = pkinit_identity_initialize(context, plgctx->cryptoctx, NULL, 1267 plgctx->idopts, plgctx->idctx, 0, NULL); 1268 if (retval) 1269 goto errout; 1270 1271 pkiDebug("%s: returning context at %p for realm '%s'\n", 1272 __FUNCTION__, plgctx, realmname); 1273 *pplgctx = plgctx; 1274 retval = 0; 1275 1276 errout: 1277 if (retval) 1278 pkinit_server_plugin_fini_realm(context, plgctx); 1279 1280 return retval; 1281 } 1282 1283 static int 1284 pkinit_server_plugin_init(krb5_context context, void **blob, 1285 const char **realmnames) 1286 { 1287 krb5_error_code retval = ENOMEM; 1288 pkinit_kdc_context plgctx, *realm_contexts = NULL; 1289 int i, j; 1290 size_t numrealms; 1291 1292 retval = pkinit_accessor_init(); 1293 if (retval) 1294 return retval; 1295 1296 /* Determine how many realms we may need to support */ 1297 for (i = 0; realmnames[i] != NULL; i++) {}; 1298 numrealms = i; 1299 1300 realm_contexts = (pkinit_kdc_context *) 1301 calloc(numrealms+1, sizeof(pkinit_kdc_context)); 1302 if (realm_contexts == NULL) 1303 return ENOMEM; 1304 1305 for (i = 0, j = 0; i < numrealms; i++) { 1306 pkiDebug("%s: processing realm '%s'\n", __FUNCTION__, realmnames[i]); 1307 retval = pkinit_server_plugin_init_realm(context, realmnames[i], &plgctx); 1308 if (retval == 0 && plgctx != NULL) 1309 realm_contexts[j++] = plgctx; 1310 } 1311 1312 if (j == 0) { 1313 /* 1314 * Solaris Kerberos 1315 * Improve error messages for the common case of a single realm 1316 */ 1317 if (numrealms != 1) { 1318 retval = EINVAL; 1319 krb5_set_error_message(context, retval, "No realms configured " 1320 "correctly for pkinit support"); 1321 } 1322 1323 goto errout; 1324 } 1325 1326 *blob = realm_contexts; 1327 retval = 0; 1328 pkiDebug("%s: returning context at %p\n", __FUNCTION__, realm_contexts); 1329 1330 errout: 1331 if (retval) 1332 pkinit_server_plugin_fini(context, realm_contexts); 1333 1334 return retval; 1335 } 1336 1337 static void 1338 pkinit_server_plugin_fini_realm(krb5_context context, pkinit_kdc_context plgctx) 1339 { 1340 if (plgctx == NULL) 1341 return; 1342 1343 pkinit_fini_kdc_profile(context, plgctx); 1344 pkinit_fini_identity_opts(plgctx->idopts); 1345 pkinit_fini_identity_crypto(plgctx->idctx); 1346 pkinit_fini_plg_crypto(plgctx->cryptoctx); 1347 pkinit_fini_plg_opts(plgctx->opts); 1348 free(plgctx->realmname); 1349 free(plgctx); 1350 } 1351 1352 static void 1353 pkinit_server_plugin_fini(krb5_context context, void *blob) 1354 { 1355 pkinit_kdc_context *realm_contexts = blob; 1356 int i; 1357 1358 if (realm_contexts == NULL) 1359 return; 1360 1361 for (i = 0; realm_contexts[i] != NULL; i++) { 1362 pkinit_server_plugin_fini_realm(context, realm_contexts[i]); 1363 } 1364 pkiDebug("%s: freeing context at %p\n", __FUNCTION__, realm_contexts); 1365 free(realm_contexts); 1366 } 1367 1368 static krb5_error_code 1369 pkinit_init_kdc_req_context(krb5_context context, void **ctx) 1370 { 1371 krb5_error_code retval = ENOMEM; 1372 pkinit_kdc_req_context reqctx = NULL; 1373 1374 reqctx = (pkinit_kdc_req_context)malloc(sizeof(*reqctx)); 1375 if (reqctx == NULL) 1376 return retval; 1377 (void) memset(reqctx, 0, sizeof(*reqctx)); 1378 reqctx->magic = PKINIT_CTX_MAGIC; 1379 1380 retval = pkinit_init_req_crypto(&reqctx->cryptoctx); 1381 if (retval) 1382 goto cleanup; 1383 reqctx->rcv_auth_pack = NULL; 1384 reqctx->rcv_auth_pack9 = NULL; 1385 1386 pkiDebug("%s: returning reqctx at %p\n", __FUNCTION__, reqctx); 1387 *ctx = reqctx; 1388 retval = 0; 1389 cleanup: 1390 if (retval) 1391 pkinit_fini_kdc_req_context(context, reqctx); 1392 1393 return retval; 1394 } 1395 1396 static void 1397 pkinit_fini_kdc_req_context(krb5_context context, void *ctx) 1398 { 1399 pkinit_kdc_req_context reqctx = (pkinit_kdc_req_context)ctx; 1400 1401 if (reqctx == NULL || reqctx->magic != PKINIT_CTX_MAGIC) { 1402 pkiDebug("pkinit_fini_kdc_req_context: got bad reqctx (%p)!\n", reqctx); 1403 return; 1404 } 1405 pkiDebug("%s: freeing reqctx at %p\n", __FUNCTION__, reqctx); 1406 1407 pkinit_fini_req_crypto(reqctx->cryptoctx); 1408 if (reqctx->rcv_auth_pack != NULL) 1409 free_krb5_auth_pack(&reqctx->rcv_auth_pack); 1410 if (reqctx->rcv_auth_pack9 != NULL) 1411 free_krb5_auth_pack_draft9(context, &reqctx->rcv_auth_pack9); 1412 1413 free(reqctx); 1414 } 1415 1416 struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = { 1417 "pkinit", /* name */ 1418 supported_server_pa_types, /* pa_type_list */ 1419 pkinit_server_plugin_init, /* (*init_proc) */ 1420 pkinit_server_plugin_fini, /* (*fini_proc) */ 1421 pkinit_server_get_flags, /* (*flags_proc) */ 1422 pkinit_server_get_edata, /* (*edata_proc) */ 1423 pkinit_server_verify_padata,/* (*verify_proc) */ 1424 pkinit_server_return_padata,/* (*return_proc) */ 1425 NULL, /* (*freepa_reqcontext_proc) */ 1426 }; 1427