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