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