1/* 2 * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "kdc_locl.h" 35 36RCSID("$Id: pkinit.c,v 1.1.1.1 2011/06/10 09:34:43 andrew Exp $"); 37 38#ifdef PKINIT 39 40#include <heim_asn1.h> 41#include <rfc2459_asn1.h> 42#include <cms_asn1.h> 43#include <pkinit_asn1.h> 44 45#include <hx509.h> 46#include "crypto-headers.h" 47 48struct pk_client_params { 49 enum krb5_pk_type type; 50 enum { USE_RSA, USE_DH, USE_ECDH } keyex; 51 union { 52 struct { 53 BIGNUM *public_key; 54 DH *key; 55 } dh; 56#ifdef HAVE_OPENSSL 57 struct { 58 EC_KEY *public_key; 59 EC_KEY *key; 60 } ecdh; 61#endif 62 } u; 63 hx509_cert cert; 64 unsigned nonce; 65 EncryptionKey reply_key; 66 char *dh_group_name; 67 hx509_peer_info peer; 68 hx509_certs client_anchors; 69 hx509_verify_ctx verify_ctx; 70}; 71 72struct pk_principal_mapping { 73 unsigned int len; 74 struct pk_allowed_princ { 75 krb5_principal principal; 76 char *subject; 77 } *val; 78}; 79 80static struct krb5_pk_identity *kdc_identity; 81static struct pk_principal_mapping principal_mappings; 82static struct krb5_dh_moduli **moduli; 83 84static struct { 85 krb5_data data; 86 time_t expire; 87 time_t next_update; 88} ocsp; 89 90/* 91 * 92 */ 93 94static krb5_error_code 95pk_check_pkauthenticator_win2k(krb5_context context, 96 PKAuthenticator_Win2k *a, 97 const KDC_REQ *req) 98{ 99 krb5_timestamp now; 100 101 krb5_timeofday (context, &now); 102 103 /* XXX cusec */ 104 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) { 105 krb5_clear_error_message(context); 106 return KRB5KRB_AP_ERR_SKEW; 107 } 108 return 0; 109} 110 111static krb5_error_code 112pk_check_pkauthenticator(krb5_context context, 113 PKAuthenticator *a, 114 const KDC_REQ *req) 115{ 116 u_char *buf = NULL; 117 size_t buf_size; 118 krb5_error_code ret; 119 size_t len; 120 krb5_timestamp now; 121 Checksum checksum; 122 123 krb5_timeofday (context, &now); 124 125 /* XXX cusec */ 126 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) { 127 krb5_clear_error_message(context); 128 return KRB5KRB_AP_ERR_SKEW; 129 } 130 131 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret); 132 if (ret) { 133 krb5_clear_error_message(context); 134 return ret; 135 } 136 if (buf_size != len) 137 krb5_abortx(context, "Internal error in ASN.1 encoder"); 138 139 ret = krb5_create_checksum(context, 140 NULL, 141 0, 142 CKSUMTYPE_SHA1, 143 buf, 144 len, 145 &checksum); 146 free(buf); 147 if (ret) { 148 krb5_clear_error_message(context); 149 return ret; 150 } 151 152 if (a->paChecksum == NULL) { 153 krb5_clear_error_message(context); 154 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; 155 goto out; 156 } 157 158 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) { 159 krb5_clear_error_message(context); 160 ret = KRB5KRB_ERR_GENERIC; 161 } 162 163out: 164 free_Checksum(&checksum); 165 166 return ret; 167} 168 169void 170_kdc_pk_free_client_param(krb5_context context, pk_client_params *cp) 171{ 172 if (cp == NULL) 173 return; 174 if (cp->cert) 175 hx509_cert_free(cp->cert); 176 if (cp->verify_ctx) 177 hx509_verify_destroy_ctx(cp->verify_ctx); 178 if (cp->keyex == USE_DH) { 179 if (cp->u.dh.key) 180 DH_free(cp->u.dh.key); 181 if (cp->u.dh.public_key) 182 BN_free(cp->u.dh.public_key); 183 } 184#ifdef HAVE_OPENSSL 185 if (cp->keyex == USE_ECDH) { 186 if (cp->u.ecdh.key) 187 EC_KEY_free(cp->u.ecdh.key); 188 if (cp->u.ecdh.public_key) 189 EC_KEY_free(cp->u.ecdh.public_key); 190 } 191#endif 192 krb5_free_keyblock_contents(context, &cp->reply_key); 193 if (cp->dh_group_name) 194 free(cp->dh_group_name); 195 if (cp->peer) 196 hx509_peer_info_free(cp->peer); 197 if (cp->client_anchors) 198 hx509_certs_free(&cp->client_anchors); 199 memset(cp, 0, sizeof(*cp)); 200 free(cp); 201} 202 203static krb5_error_code 204generate_dh_keyblock(krb5_context context, 205 pk_client_params *client_params, 206 krb5_enctype enctype) 207{ 208 unsigned char *dh_gen_key = NULL; 209 krb5_keyblock key; 210 krb5_error_code ret; 211 size_t dh_gen_keylen, size; 212 213 memset(&key, 0, sizeof(key)); 214 215 if (client_params->keyex == USE_DH) { 216 217 if (client_params->u.dh.public_key == NULL) { 218 ret = KRB5KRB_ERR_GENERIC; 219 krb5_set_error_message(context, ret, "public_key"); 220 goto out; 221 } 222 223 if (!DH_generate_key(client_params->u.dh.key)) { 224 ret = KRB5KRB_ERR_GENERIC; 225 krb5_set_error_message(context, ret, 226 "Can't generate Diffie-Hellman keys"); 227 goto out; 228 } 229 230 dh_gen_keylen = DH_size(client_params->u.dh.key); 231 size = BN_num_bytes(client_params->u.dh.key->p); 232 if (size < dh_gen_keylen) 233 size = dh_gen_keylen; 234 235 dh_gen_key = malloc(size); 236 if (dh_gen_key == NULL) { 237 ret = ENOMEM; 238 krb5_set_error_message(context, ret, "malloc: out of memory"); 239 goto out; 240 } 241 memset(dh_gen_key, 0, size - dh_gen_keylen); 242 243 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), 244 client_params->u.dh.public_key, 245 client_params->u.dh.key); 246 if (dh_gen_keylen == -1) { 247 ret = KRB5KRB_ERR_GENERIC; 248 krb5_set_error_message(context, ret, 249 "Can't compute Diffie-Hellman key"); 250 goto out; 251 } 252 ret = 0; 253#ifdef HAVE_OPENSSL 254 } else if (client_params->keyex == USE_ECDH) { 255 256 if (client_params->u.ecdh.public_key == NULL) { 257 ret = KRB5KRB_ERR_GENERIC; 258 krb5_set_error_message(context, ret, "public_key"); 259 goto out; 260 } 261 262 client_params->u.ecdh.key = EC_KEY_new(); 263 if (client_params->u.ecdh.key == NULL) { 264 ret = ENOMEM; 265 goto out; 266 } 267 EC_KEY_set_group(client_params->u.ecdh.key, 268 EC_KEY_get0_group(client_params->u.ecdh.public_key)); 269 270 if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) { 271 ret = ENOMEM; 272 goto out; 273 } 274 275 size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8; 276 dh_gen_key = malloc(size); 277 if (dh_gen_key == NULL) { 278 ret = ENOMEM; 279 krb5_set_error_message(context, ret, 280 N_("malloc: out of memory", "")); 281 goto out; 282 } 283 284 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size, 285 EC_KEY_get0_public_key(client_params->u.ecdh.public_key), 286 client_params->u.ecdh.key, NULL); 287 288#endif /* HAVE_OPENSSL */ 289 } else { 290 ret = KRB5KRB_ERR_GENERIC; 291 krb5_set_error_message(context, ret, 292 "Diffie-Hellman not selected keys"); 293 goto out; 294 } 295 296 ret = _krb5_pk_octetstring2key(context, 297 enctype, 298 dh_gen_key, dh_gen_keylen, 299 NULL, NULL, 300 &client_params->reply_key); 301 302 out: 303 if (dh_gen_key) 304 free(dh_gen_key); 305 if (key.keyvalue.data) 306 krb5_free_keyblock_contents(context, &key); 307 308 return ret; 309} 310 311static BIGNUM * 312integer_to_BN(krb5_context context, const char *field, heim_integer *f) 313{ 314 BIGNUM *bn; 315 316 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL); 317 if (bn == NULL) { 318 krb5_set_error_message(context, KRB5_BADMSGTYPE, 319 "PKINIT: parsing BN failed %s", field); 320 return NULL; 321 } 322 BN_set_negative(bn, f->negative); 323 return bn; 324} 325 326static krb5_error_code 327get_dh_param(krb5_context context, 328 krb5_kdc_configuration *config, 329 SubjectPublicKeyInfo *dh_key_info, 330 pk_client_params *client_params) 331{ 332 DomainParameters dhparam; 333 DH *dh = NULL; 334 krb5_error_code ret; 335 336 memset(&dhparam, 0, sizeof(dhparam)); 337 338 if ((dh_key_info->subjectPublicKey.length % 8) != 0) { 339 ret = KRB5_BADMSGTYPE; 340 krb5_set_error_message(context, ret, 341 "PKINIT: subjectPublicKey not aligned " 342 "to 8 bit boundary"); 343 goto out; 344 } 345 346 if (dh_key_info->algorithm.parameters == NULL) { 347 krb5_set_error_message(context, KRB5_BADMSGTYPE, 348 "PKINIT missing algorithm parameter " 349 "in clientPublicValue"); 350 return KRB5_BADMSGTYPE; 351 } 352 353 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data, 354 dh_key_info->algorithm.parameters->length, 355 &dhparam, 356 NULL); 357 if (ret) { 358 krb5_set_error_message(context, ret, "Can't decode algorithm " 359 "parameters in clientPublicValue"); 360 goto out; 361 } 362 363 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits, 364 &dhparam.p, &dhparam.g, &dhparam.q, moduli, 365 &client_params->dh_group_name); 366 if (ret) { 367 /* XXX send back proposal of better group */ 368 goto out; 369 } 370 371 dh = DH_new(); 372 if (dh == NULL) { 373 ret = ENOMEM; 374 krb5_set_error_message(context, ret, "Cannot create DH structure"); 375 goto out; 376 } 377 ret = KRB5_BADMSGTYPE; 378 dh->p = integer_to_BN(context, "DH prime", &dhparam.p); 379 if (dh->p == NULL) 380 goto out; 381 dh->g = integer_to_BN(context, "DH base", &dhparam.g); 382 if (dh->g == NULL) 383 goto out; 384 dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q); 385 if (dh->g == NULL) 386 goto out; 387 388 { 389 heim_integer glue; 390 size_t size; 391 392 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data, 393 dh_key_info->subjectPublicKey.length / 8, 394 &glue, 395 &size); 396 if (ret) { 397 krb5_clear_error_message(context); 398 return ret; 399 } 400 401 client_params->u.dh.public_key = integer_to_BN(context, 402 "subjectPublicKey", 403 &glue); 404 der_free_heim_integer(&glue); 405 if (client_params->u.dh.public_key == NULL) { 406 ret = KRB5_BADMSGTYPE; 407 goto out; 408 } 409 } 410 411 client_params->u.dh.key = dh; 412 dh = NULL; 413 ret = 0; 414 415 out: 416 if (dh) 417 DH_free(dh); 418 free_DomainParameters(&dhparam); 419 return ret; 420} 421 422#ifdef HAVE_OPENSSL 423 424static krb5_error_code 425get_ecdh_param(krb5_context context, 426 krb5_kdc_configuration *config, 427 SubjectPublicKeyInfo *dh_key_info, 428 pk_client_params *client_params) 429{ 430 ECParameters ecp; 431 EC_KEY *public = NULL; 432 krb5_error_code ret; 433 const unsigned char *p; 434 size_t len; 435 int nid; 436 437 if (dh_key_info->algorithm.parameters == NULL) { 438 krb5_set_error_message(context, KRB5_BADMSGTYPE, 439 "PKINIT missing algorithm parameter " 440 "in clientPublicValue"); 441 return KRB5_BADMSGTYPE; 442 } 443 444 memset(&ecp, 0, sizeof(ecp)); 445 446 ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, 447 dh_key_info->algorithm.parameters->length, &ecp, &len); 448 if (ret) 449 goto out; 450 451 if (ecp.element != choice_ECParameters_namedCurve) { 452 ret = KRB5_BADMSGTYPE; 453 goto out; 454 } 455 456 if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0) 457 nid = NID_X9_62_prime256v1; 458 else { 459 ret = KRB5_BADMSGTYPE; 460 goto out; 461 } 462 463 /* XXX verify group is ok */ 464 465 public = EC_KEY_new_by_curve_name(nid); 466 467 p = dh_key_info->subjectPublicKey.data; 468 len = dh_key_info->subjectPublicKey.length / 8; 469 if (o2i_ECPublicKey(&public, &p, len) == NULL) { 470 ret = KRB5_BADMSGTYPE; 471 krb5_set_error_message(context, ret, 472 "PKINIT failed to decode ECDH key"); 473 goto out; 474 } 475 client_params->u.ecdh.public_key = public; 476 public = NULL; 477 478 out: 479 if (public) 480 EC_KEY_free(public); 481 free_ECParameters(&ecp); 482 return ret; 483} 484 485#endif /* HAVE_OPENSSL */ 486 487krb5_error_code 488_kdc_pk_rd_padata(krb5_context context, 489 krb5_kdc_configuration *config, 490 const KDC_REQ *req, 491 const PA_DATA *pa, 492 hdb_entry_ex *client, 493 pk_client_params **ret_params) 494{ 495 pk_client_params *cp; 496 krb5_error_code ret; 497 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; 498 krb5_data eContent = { 0, NULL }; 499 krb5_data signed_content = { 0, NULL }; 500 const char *type = "unknown type"; 501 hx509_certs trust_anchors; 502 int have_data = 0; 503 const HDB_Ext_PKINIT_cert *pc; 504 505 *ret_params = NULL; 506 507 if (!config->enable_pkinit) { 508 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); 509 krb5_clear_error_message(context); 510 return 0; 511 } 512 513 cp = calloc(1, sizeof(*cp)); 514 if (cp == NULL) { 515 krb5_clear_error_message(context); 516 ret = ENOMEM; 517 goto out; 518 } 519 520 ret = hx509_certs_init(kdc_identity->hx509ctx, 521 "MEMORY:trust-anchors", 522 0, NULL, &trust_anchors); 523 if (ret) { 524 krb5_set_error_message(context, ret, "failed to create trust anchors"); 525 goto out; 526 } 527 528 ret = hx509_certs_merge(kdc_identity->hx509ctx, trust_anchors, 529 kdc_identity->anchors); 530 if (ret) { 531 hx509_certs_free(&trust_anchors); 532 krb5_set_error_message(context, ret, "failed to create verify context"); 533 goto out; 534 } 535 536 /* Add any registered certificates for this client as trust anchors */ 537 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); 538 if (ret == 0 && pc != NULL) { 539 hx509_cert cert; 540 unsigned int i; 541 542 for (i = 0; i < pc->len; i++) { 543 ret = hx509_cert_init_data(kdc_identity->hx509ctx, 544 pc->val[i].cert.data, 545 pc->val[i].cert.length, 546 &cert); 547 if (ret) 548 continue; 549 hx509_certs_add(kdc_identity->hx509ctx, trust_anchors, cert); 550 hx509_cert_free(cert); 551 } 552 } 553 554 ret = hx509_verify_init_ctx(kdc_identity->hx509ctx, &cp->verify_ctx); 555 if (ret) { 556 hx509_certs_free(&trust_anchors); 557 krb5_set_error_message(context, ret, "failed to create verify context"); 558 goto out; 559 } 560 561 hx509_verify_set_time(cp->verify_ctx, kdc_time); 562 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); 563 hx509_certs_free(&trust_anchors); 564 565 if (config->pkinit_allow_proxy_certs) 566 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); 567 568 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { 569 PA_PK_AS_REQ_Win2k r; 570 571 type = "PK-INIT-Win2k"; 572 573 if (req->req_body.kdc_options.request_anonymous) { 574 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; 575 krb5_set_error_message(context, ret, 576 "Anon not supported in RSA mode"); 577 goto out; 578 } 579 580 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, 581 pa->padata_value.length, 582 &r, 583 NULL); 584 if (ret) { 585 krb5_set_error_message(context, ret, "Can't decode " 586 "PK-AS-REQ-Win2k: %d", ret); 587 goto out; 588 } 589 590 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, 591 &contentInfoOid, 592 &signed_content, 593 &have_data); 594 free_PA_PK_AS_REQ_Win2k(&r); 595 if (ret) { 596 krb5_set_error_message(context, ret, 597 "Can't unwrap ContentInfo(win): %d", ret); 598 goto out; 599 } 600 601 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { 602 PA_PK_AS_REQ r; 603 604 type = "PK-INIT-IETF"; 605 606 ret = decode_PA_PK_AS_REQ(pa->padata_value.data, 607 pa->padata_value.length, 608 &r, 609 NULL); 610 if (ret) { 611 krb5_set_error_message(context, ret, 612 "Can't decode PK-AS-REQ: %d", ret); 613 goto out; 614 } 615 616 /* XXX look at r.kdcPkId */ 617 if (r.trustedCertifiers) { 618 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; 619 unsigned int i, maxedi; 620 621 ret = hx509_certs_init(kdc_identity->hx509ctx, 622 "MEMORY:client-anchors", 623 0, NULL, 624 &cp->client_anchors); 625 if (ret) { 626 krb5_set_error_message(context, ret, 627 "Can't allocate client anchors: %d", 628 ret); 629 goto out; 630 631 } 632 /* 633 * If the client sent more then 10 EDI, don't bother 634 * looking more then 10 of performance reasons. 635 */ 636 maxedi = edi->len; 637 if (maxedi > 10) 638 maxedi = 10; 639 for (i = 0; i < maxedi; i++) { 640 IssuerAndSerialNumber iasn; 641 hx509_query *q; 642 hx509_cert cert; 643 size_t size; 644 645 if (edi->val[i].issuerAndSerialNumber == NULL) 646 continue; 647 648 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); 649 if (ret) { 650 krb5_set_error_message(context, ret, 651 "Failed to allocate hx509_query"); 652 goto out; 653 } 654 655 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, 656 edi->val[i].issuerAndSerialNumber->length, 657 &iasn, 658 &size); 659 if (ret) { 660 hx509_query_free(kdc_identity->hx509ctx, q); 661 continue; 662 } 663 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); 664 free_IssuerAndSerialNumber(&iasn); 665 if (ret) { 666 hx509_query_free(kdc_identity->hx509ctx, q); 667 continue; 668 } 669 670 ret = hx509_certs_find(kdc_identity->hx509ctx, 671 kdc_identity->certs, 672 q, 673 &cert); 674 hx509_query_free(kdc_identity->hx509ctx, q); 675 if (ret) 676 continue; 677 hx509_certs_add(kdc_identity->hx509ctx, 678 cp->client_anchors, cert); 679 hx509_cert_free(cert); 680 } 681 } 682 683 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, 684 &contentInfoOid, 685 &signed_content, 686 &have_data); 687 free_PA_PK_AS_REQ(&r); 688 if (ret) { 689 krb5_set_error_message(context, ret, 690 "Can't unwrap ContentInfo: %d", ret); 691 goto out; 692 } 693 694 } else { 695 krb5_clear_error_message(context); 696 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; 697 goto out; 698 } 699 700 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); 701 if (ret != 0) { 702 ret = KRB5KRB_ERR_GENERIC; 703 krb5_set_error_message(context, ret, 704 "PK-AS-REQ-Win2k invalid content type oid"); 705 goto out; 706 } 707 708 if (!have_data) { 709 ret = KRB5KRB_ERR_GENERIC; 710 krb5_set_error_message(context, ret, 711 "PK-AS-REQ-Win2k no signed auth pack"); 712 goto out; 713 } 714 715 { 716 hx509_certs signer_certs; 717 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ 718 719 if (req->req_body.kdc_options.request_anonymous) 720 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; 721 722 ret = hx509_cms_verify_signed(kdc_identity->hx509ctx, 723 cp->verify_ctx, 724 flags, 725 signed_content.data, 726 signed_content.length, 727 NULL, 728 kdc_identity->certpool, 729 &eContentType, 730 &eContent, 731 &signer_certs); 732 if (ret) { 733 char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret); 734 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", 735 s, ret); 736 free(s); 737 goto out; 738 } 739 740 if (signer_certs) { 741 ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs, 742 &cp->cert); 743 hx509_certs_free(&signer_certs); 744 } 745 if (ret) 746 goto out; 747 } 748 749 /* Signature is correct, now verify the signed message */ 750 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && 751 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) 752 { 753 ret = KRB5_BADMSGTYPE; 754 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); 755 goto out; 756 } 757 758 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { 759 AuthPack_Win2k ap; 760 761 ret = decode_AuthPack_Win2k(eContent.data, 762 eContent.length, 763 &ap, 764 NULL); 765 if (ret) { 766 krb5_set_error_message(context, ret, 767 "Can't decode AuthPack: %d", ret); 768 goto out; 769 } 770 771 ret = pk_check_pkauthenticator_win2k(context, 772 &ap.pkAuthenticator, 773 req); 774 if (ret) { 775 free_AuthPack_Win2k(&ap); 776 goto out; 777 } 778 779 cp->type = PKINIT_WIN2K; 780 cp->nonce = ap.pkAuthenticator.nonce; 781 782 if (ap.clientPublicValue) { 783 ret = KRB5KRB_ERR_GENERIC; 784 krb5_set_error_message(context, ret, 785 "DH not supported for windows"); 786 goto out; 787 } 788 free_AuthPack_Win2k(&ap); 789 790 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { 791 AuthPack ap; 792 793 ret = decode_AuthPack(eContent.data, 794 eContent.length, 795 &ap, 796 NULL); 797 if (ret) { 798 krb5_set_error_message(context, ret, 799 "Can't decode AuthPack: %d", ret); 800 free_AuthPack(&ap); 801 goto out; 802 } 803 804 if (req->req_body.kdc_options.request_anonymous && 805 ap.clientPublicValue == NULL) { 806 free_AuthPack(&ap); 807 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; 808 krb5_set_error_message(context, ret, 809 "Anon not supported in RSA mode"); 810 goto out; 811 } 812 813 ret = pk_check_pkauthenticator(context, 814 &ap.pkAuthenticator, 815 req); 816 if (ret) { 817 free_AuthPack(&ap); 818 goto out; 819 } 820 821 cp->type = PKINIT_27; 822 cp->nonce = ap.pkAuthenticator.nonce; 823 824 if (ap.clientPublicValue) { 825 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { 826 cp->keyex = USE_DH; 827 ret = get_dh_param(context, config, 828 ap.clientPublicValue, cp); 829#ifdef HAVE_OPENSSL 830 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { 831 cp->keyex = USE_ECDH; 832 ret = get_ecdh_param(context, config, 833 ap.clientPublicValue, cp); 834#endif /* HAVE_OPENSSL */ 835 } else { 836 ret = KRB5_BADMSGTYPE; 837 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); 838 } 839 if (ret) { 840 free_AuthPack(&ap); 841 goto out; 842 } 843 } else 844 cp->keyex = USE_RSA; 845 846 ret = hx509_peer_info_alloc(kdc_identity->hx509ctx, 847 &cp->peer); 848 if (ret) { 849 free_AuthPack(&ap); 850 goto out; 851 } 852 853 if (ap.supportedCMSTypes) { 854 ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx, 855 cp->peer, 856 ap.supportedCMSTypes->val, 857 ap.supportedCMSTypes->len); 858 if (ret) { 859 free_AuthPack(&ap); 860 goto out; 861 } 862 } else { 863 /* assume old client */ 864 hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, 865 hx509_crypto_des_rsdi_ede3_cbc()); 866 hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, 867 hx509_signature_rsa_with_sha1()); 868 hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, 869 hx509_signature_sha1()); 870 } 871 free_AuthPack(&ap); 872 } else 873 krb5_abortx(context, "internal pkinit error"); 874 875 kdc_log(context, config, 0, "PK-INIT request of type %s", type); 876 877out: 878 if (ret) 879 krb5_warn(context, ret, "PKINIT"); 880 881 if (signed_content.data) 882 free(signed_content.data); 883 krb5_data_free(&eContent); 884 der_free_oid(&eContentType); 885 der_free_oid(&contentInfoOid); 886 if (ret) { 887 _kdc_pk_free_client_param(context, cp); 888 } else 889 *ret_params = cp; 890 return ret; 891} 892 893/* 894 * 895 */ 896 897static krb5_error_code 898BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer) 899{ 900 integer->length = BN_num_bytes(bn); 901 integer->data = malloc(integer->length); 902 if (integer->data == NULL) { 903 krb5_clear_error_message(context); 904 return ENOMEM; 905 } 906 BN_bn2bin(bn, integer->data); 907 integer->negative = BN_is_negative(bn); 908 return 0; 909} 910 911static krb5_error_code 912pk_mk_pa_reply_enckey(krb5_context context, 913 krb5_kdc_configuration *config, 914 pk_client_params *cp, 915 const KDC_REQ *req, 916 const krb5_data *req_buffer, 917 krb5_keyblock *reply_key, 918 ContentInfo *content_info, 919 hx509_cert *kdc_cert) 920{ 921 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL; 922 krb5_error_code ret; 923 krb5_data buf, signed_data; 924 size_t size; 925 int do_win2k = 0; 926 927 krb5_data_zero(&buf); 928 krb5_data_zero(&signed_data); 929 930 *kdc_cert = NULL; 931 932 /* 933 * If the message client is a win2k-type but it send pa data 934 * 09-binding it expects a IETF (checksum) reply so there can be 935 * no replay attacks. 936 */ 937 938 switch (cp->type) { 939 case PKINIT_WIN2K: { 940 int i = 0; 941 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL 942 && config->pkinit_require_binding == 0) 943 { 944 do_win2k = 1; 945 } 946 sdAlg = &asn1_oid_id_pkcs7_data; 947 evAlg = &asn1_oid_id_pkcs7_data; 948 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc; 949 break; 950 } 951 case PKINIT_27: 952 sdAlg = &asn1_oid_id_pkrkeydata; 953 evAlg = &asn1_oid_id_pkcs7_signedData; 954 break; 955 default: 956 krb5_abortx(context, "internal pkinit error"); 957 } 958 959 if (do_win2k) { 960 ReplyKeyPack_Win2k kp; 961 memset(&kp, 0, sizeof(kp)); 962 963 ret = copy_EncryptionKey(reply_key, &kp.replyKey); 964 if (ret) { 965 krb5_clear_error_message(context); 966 goto out; 967 } 968 kp.nonce = cp->nonce; 969 970 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k, 971 buf.data, buf.length, 972 &kp, &size,ret); 973 free_ReplyKeyPack_Win2k(&kp); 974 } else { 975 krb5_crypto ascrypto; 976 ReplyKeyPack kp; 977 memset(&kp, 0, sizeof(kp)); 978 979 ret = copy_EncryptionKey(reply_key, &kp.replyKey); 980 if (ret) { 981 krb5_clear_error_message(context); 982 goto out; 983 } 984 985 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto); 986 if (ret) { 987 krb5_clear_error_message(context); 988 goto out; 989 } 990 991 ret = krb5_create_checksum(context, ascrypto, 6, 0, 992 req_buffer->data, req_buffer->length, 993 &kp.asChecksum); 994 if (ret) { 995 krb5_clear_error_message(context); 996 goto out; 997 } 998 999 ret = krb5_crypto_destroy(context, ascrypto); 1000 if (ret) { 1001 krb5_clear_error_message(context); 1002 goto out; 1003 } 1004 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret); 1005 free_ReplyKeyPack(&kp); 1006 } 1007 if (ret) { 1008 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack " 1009 "failed (%d)", ret); 1010 goto out; 1011 } 1012 if (buf.length != size) 1013 krb5_abortx(context, "Internal ASN.1 encoder error"); 1014 1015 { 1016 hx509_query *q; 1017 hx509_cert cert; 1018 1019 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); 1020 if (ret) 1021 goto out; 1022 1023 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 1024 if (config->pkinit_kdc_friendly_name) 1025 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); 1026 1027 ret = hx509_certs_find(kdc_identity->hx509ctx, 1028 kdc_identity->certs, 1029 q, 1030 &cert); 1031 hx509_query_free(kdc_identity->hx509ctx, q); 1032 if (ret) 1033 goto out; 1034 1035 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx, 1036 0, 1037 sdAlg, 1038 buf.data, 1039 buf.length, 1040 NULL, 1041 cert, 1042 cp->peer, 1043 cp->client_anchors, 1044 kdc_identity->certpool, 1045 &signed_data); 1046 *kdc_cert = cert; 1047 } 1048 1049 krb5_data_free(&buf); 1050 if (ret) 1051 goto out; 1052 1053 if (cp->type == PKINIT_WIN2K) { 1054 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, 1055 &signed_data, 1056 &buf); 1057 if (ret) 1058 goto out; 1059 krb5_data_free(&signed_data); 1060 signed_data = buf; 1061 } 1062 1063 ret = hx509_cms_envelope_1(kdc_identity->hx509ctx, 1064 HX509_CMS_EV_NO_KU_CHECK, 1065 cp->cert, 1066 signed_data.data, signed_data.length, 1067 envelopedAlg, 1068 evAlg, &buf); 1069 if (ret) 1070 goto out; 1071 1072 ret = _krb5_pk_mk_ContentInfo(context, 1073 &buf, 1074 &asn1_oid_id_pkcs7_envelopedData, 1075 content_info); 1076out: 1077 if (ret && *kdc_cert) { 1078 hx509_cert_free(*kdc_cert); 1079 *kdc_cert = NULL; 1080 } 1081 1082 krb5_data_free(&buf); 1083 krb5_data_free(&signed_data); 1084 return ret; 1085} 1086 1087/* 1088 * 1089 */ 1090 1091static krb5_error_code 1092pk_mk_pa_reply_dh(krb5_context context, 1093 krb5_kdc_configuration *config, 1094 pk_client_params *cp, 1095 ContentInfo *content_info, 1096 hx509_cert *kdc_cert) 1097{ 1098 KDCDHKeyInfo dh_info; 1099 krb5_data signed_data, buf; 1100 ContentInfo contentinfo; 1101 krb5_error_code ret; 1102 hx509_cert cert; 1103 hx509_query *q; 1104 size_t size; 1105 1106 memset(&contentinfo, 0, sizeof(contentinfo)); 1107 memset(&dh_info, 0, sizeof(dh_info)); 1108 krb5_data_zero(&signed_data); 1109 krb5_data_zero(&buf); 1110 1111 *kdc_cert = NULL; 1112 1113 if (cp->keyex == USE_DH) { 1114 DH *kdc_dh = cp->u.dh.key; 1115 heim_integer i; 1116 1117 ret = BN_to_integer(context, kdc_dh->pub_key, &i); 1118 if (ret) 1119 return ret; 1120 1121 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret); 1122 der_free_heim_integer(&i); 1123 if (ret) { 1124 krb5_set_error_message(context, ret, "ASN.1 encoding of " 1125 "DHPublicKey failed (%d)", ret); 1126 return ret; 1127 } 1128 if (buf.length != size) 1129 krb5_abortx(context, "Internal ASN.1 encoder error"); 1130 1131 dh_info.subjectPublicKey.length = buf.length * 8; 1132 dh_info.subjectPublicKey.data = buf.data; 1133 krb5_data_zero(&buf); 1134#ifdef HAVE_OPENSSL 1135 } else if (cp->keyex == USE_ECDH) { 1136 unsigned char *p; 1137 int len; 1138 1139 len = i2o_ECPublicKey(cp->u.ecdh.key, NULL); 1140 if (len <= 0) 1141 abort(); 1142 1143 p = malloc(len); 1144 if (p == NULL) 1145 abort(); 1146 1147 dh_info.subjectPublicKey.length = len * 8; 1148 dh_info.subjectPublicKey.data = p; 1149 1150 len = i2o_ECPublicKey(cp->u.ecdh.key, &p); 1151 if (len <= 0) 1152 abort(); 1153#endif 1154 } else 1155 krb5_abortx(context, "no keyex selected ?"); 1156 1157 1158 dh_info.nonce = cp->nonce; 1159 1160 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size, 1161 ret); 1162 if (ret) { 1163 krb5_set_error_message(context, ret, "ASN.1 encoding of " 1164 "KdcDHKeyInfo failed (%d)", ret); 1165 goto out; 1166 } 1167 if (buf.length != size) 1168 krb5_abortx(context, "Internal ASN.1 encoder error"); 1169 1170 /* 1171 * Create the SignedData structure and sign the KdcDHKeyInfo 1172 * filled in above 1173 */ 1174 1175 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); 1176 if (ret) 1177 goto out; 1178 1179 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 1180 if (config->pkinit_kdc_friendly_name) 1181 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); 1182 1183 ret = hx509_certs_find(kdc_identity->hx509ctx, 1184 kdc_identity->certs, 1185 q, 1186 &cert); 1187 hx509_query_free(kdc_identity->hx509ctx, q); 1188 if (ret) 1189 goto out; 1190 1191 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx, 1192 0, 1193 &asn1_oid_id_pkdhkeydata, 1194 buf.data, 1195 buf.length, 1196 NULL, 1197 cert, 1198 cp->peer, 1199 cp->client_anchors, 1200 kdc_identity->certpool, 1201 &signed_data); 1202 if (ret) { 1203 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret); 1204 goto out; 1205 } 1206 *kdc_cert = cert; 1207 1208 ret = _krb5_pk_mk_ContentInfo(context, 1209 &signed_data, 1210 &asn1_oid_id_pkcs7_signedData, 1211 content_info); 1212 if (ret) 1213 goto out; 1214 1215 out: 1216 if (ret && *kdc_cert) { 1217 hx509_cert_free(*kdc_cert); 1218 *kdc_cert = NULL; 1219 } 1220 1221 krb5_data_free(&buf); 1222 krb5_data_free(&signed_data); 1223 free_KDCDHKeyInfo(&dh_info); 1224 1225 return ret; 1226} 1227 1228/* 1229 * 1230 */ 1231 1232krb5_error_code 1233_kdc_pk_mk_pa_reply(krb5_context context, 1234 krb5_kdc_configuration *config, 1235 pk_client_params *cp, 1236 const hdb_entry_ex *client, 1237 krb5_enctype sessionetype, 1238 const KDC_REQ *req, 1239 const krb5_data *req_buffer, 1240 krb5_keyblock **reply_key, 1241 krb5_keyblock *sessionkey, 1242 METHOD_DATA *md) 1243{ 1244 krb5_error_code ret; 1245 void *buf; 1246 size_t len, size; 1247 krb5_enctype enctype; 1248 int pa_type; 1249 hx509_cert kdc_cert = NULL; 1250 int i; 1251 1252 if (!config->enable_pkinit) { 1253 krb5_clear_error_message(context); 1254 return 0; 1255 } 1256 1257 if (req->req_body.etype.len > 0) { 1258 for (i = 0; i < req->req_body.etype.len; i++) 1259 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0) 1260 break; 1261 if (req->req_body.etype.len <= i) { 1262 ret = KRB5KRB_ERR_GENERIC; 1263 krb5_set_error_message(context, ret, 1264 "No valid enctype available from client"); 1265 goto out; 1266 } 1267 enctype = req->req_body.etype.val[i]; 1268 } else 1269 enctype = ETYPE_DES3_CBC_SHA1; 1270 1271 if (cp->type == PKINIT_27) { 1272 PA_PK_AS_REP rep; 1273 const char *type, *other = ""; 1274 1275 memset(&rep, 0, sizeof(rep)); 1276 1277 pa_type = KRB5_PADATA_PK_AS_REP; 1278 1279 if (cp->keyex == USE_RSA) { 1280 ContentInfo info; 1281 1282 type = "enckey"; 1283 1284 rep.element = choice_PA_PK_AS_REP_encKeyPack; 1285 1286 ret = krb5_generate_random_keyblock(context, enctype, 1287 &cp->reply_key); 1288 if (ret) { 1289 free_PA_PK_AS_REP(&rep); 1290 goto out; 1291 } 1292 ret = pk_mk_pa_reply_enckey(context, 1293 config, 1294 cp, 1295 req, 1296 req_buffer, 1297 &cp->reply_key, 1298 &info, 1299 &kdc_cert); 1300 if (ret) { 1301 free_PA_PK_AS_REP(&rep); 1302 goto out; 1303 } 1304 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, 1305 rep.u.encKeyPack.length, &info, &size, 1306 ret); 1307 free_ContentInfo(&info); 1308 if (ret) { 1309 krb5_set_error_message(context, ret, "encoding of Key ContentInfo " 1310 "failed %d", ret); 1311 free_PA_PK_AS_REP(&rep); 1312 goto out; 1313 } 1314 if (rep.u.encKeyPack.length != size) 1315 krb5_abortx(context, "Internal ASN.1 encoder error"); 1316 1317 ret = krb5_generate_random_keyblock(context, sessionetype, 1318 sessionkey); 1319 if (ret) { 1320 free_PA_PK_AS_REP(&rep); 1321 goto out; 1322 } 1323 1324 } else { 1325 ContentInfo info; 1326 1327 switch (cp->keyex) { 1328 case USE_DH: type = "dh"; break; 1329#ifdef HAVE_OPENSSL 1330 case USE_ECDH: type = "ecdh"; break; 1331#endif 1332 default: krb5_abortx(context, "unknown keyex"); break; 1333 } 1334 1335 if (cp->dh_group_name) 1336 other = cp->dh_group_name; 1337 1338 rep.element = choice_PA_PK_AS_REP_dhInfo; 1339 1340 ret = generate_dh_keyblock(context, cp, enctype); 1341 if (ret) 1342 return ret; 1343 1344 ret = pk_mk_pa_reply_dh(context, config, 1345 cp, 1346 &info, 1347 &kdc_cert); 1348 if (ret) { 1349 free_PA_PK_AS_REP(&rep); 1350 krb5_set_error_message(context, ret, 1351 "create pa-reply-dh " 1352 "failed %d", ret); 1353 goto out; 1354 } 1355 1356 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data, 1357 rep.u.dhInfo.dhSignedData.length, &info, &size, 1358 ret); 1359 free_ContentInfo(&info); 1360 if (ret) { 1361 krb5_set_error_message(context, ret, 1362 "encoding of Key ContentInfo " 1363 "failed %d", ret); 1364 free_PA_PK_AS_REP(&rep); 1365 goto out; 1366 } 1367 if (rep.u.encKeyPack.length != size) 1368 krb5_abortx(context, "Internal ASN.1 encoder error"); 1369 1370 /* XXX KRB-FX-CF2 */ 1371 ret = krb5_generate_random_keyblock(context, sessionetype, 1372 sessionkey); 1373 if (ret) { 1374 free_PA_PK_AS_REP(&rep); 1375 goto out; 1376 } 1377 1378 /* XXX Add PA-PKINIT-KX */ 1379 1380 } 1381 1382 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret); 1383 free_PA_PK_AS_REP(&rep); 1384 if (ret) { 1385 krb5_set_error_message(context, ret, 1386 "encode PA-PK-AS-REP failed %d", ret); 1387 goto out; 1388 } 1389 if (len != size) 1390 krb5_abortx(context, "Internal ASN.1 encoder error"); 1391 1392 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other); 1393 1394 } else if (cp->type == PKINIT_WIN2K) { 1395 PA_PK_AS_REP_Win2k rep; 1396 ContentInfo info; 1397 1398 if (cp->keyex != USE_RSA) { 1399 ret = KRB5KRB_ERR_GENERIC; 1400 krb5_set_error_message(context, ret, 1401 "Windows PK-INIT doesn't support DH"); 1402 goto out; 1403 } 1404 1405 memset(&rep, 0, sizeof(rep)); 1406 1407 pa_type = KRB5_PADATA_PK_AS_REP_19; 1408 rep.element = choice_PA_PK_AS_REP_encKeyPack; 1409 1410 ret = krb5_generate_random_keyblock(context, enctype, 1411 &cp->reply_key); 1412 if (ret) { 1413 free_PA_PK_AS_REP_Win2k(&rep); 1414 goto out; 1415 } 1416 ret = pk_mk_pa_reply_enckey(context, 1417 config, 1418 cp, 1419 req, 1420 req_buffer, 1421 &cp->reply_key, 1422 &info, 1423 &kdc_cert); 1424 if (ret) { 1425 free_PA_PK_AS_REP_Win2k(&rep); 1426 goto out; 1427 } 1428 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, 1429 rep.u.encKeyPack.length, &info, &size, 1430 ret); 1431 free_ContentInfo(&info); 1432 if (ret) { 1433 krb5_set_error_message(context, ret, "encoding of Key ContentInfo " 1434 "failed %d", ret); 1435 free_PA_PK_AS_REP_Win2k(&rep); 1436 goto out; 1437 } 1438 if (rep.u.encKeyPack.length != size) 1439 krb5_abortx(context, "Internal ASN.1 encoder error"); 1440 1441 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret); 1442 free_PA_PK_AS_REP_Win2k(&rep); 1443 if (ret) { 1444 krb5_set_error_message(context, ret, 1445 "encode PA-PK-AS-REP-Win2k failed %d", ret); 1446 goto out; 1447 } 1448 if (len != size) 1449 krb5_abortx(context, "Internal ASN.1 encoder error"); 1450 1451 ret = krb5_generate_random_keyblock(context, sessionetype, 1452 sessionkey); 1453 if (ret) { 1454 free(buf); 1455 goto out; 1456 } 1457 1458 } else 1459 krb5_abortx(context, "PK-INIT internal error"); 1460 1461 1462 ret = krb5_padata_add(context, md, pa_type, buf, len); 1463 if (ret) { 1464 krb5_set_error_message(context, ret, 1465 "Failed adding PA-PK-AS-REP %d", ret); 1466 free(buf); 1467 goto out; 1468 } 1469 1470 if (config->pkinit_kdc_ocsp_file) { 1471 1472 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) { 1473 struct stat sb; 1474 int fd; 1475 1476 krb5_data_free(&ocsp.data); 1477 1478 ocsp.expire = 0; 1479 ocsp.next_update = kdc_time + 60 * 5; 1480 1481 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY); 1482 if (fd < 0) { 1483 kdc_log(context, config, 0, 1484 "PK-INIT failed to open ocsp data file %d", errno); 1485 goto out_ocsp; 1486 } 1487 ret = fstat(fd, &sb); 1488 if (ret) { 1489 ret = errno; 1490 close(fd); 1491 kdc_log(context, config, 0, 1492 "PK-INIT failed to stat ocsp data %d", ret); 1493 goto out_ocsp; 1494 } 1495 1496 ret = krb5_data_alloc(&ocsp.data, sb.st_size); 1497 if (ret) { 1498 close(fd); 1499 kdc_log(context, config, 0, 1500 "PK-INIT failed to stat ocsp data %d", ret); 1501 goto out_ocsp; 1502 } 1503 ocsp.data.length = sb.st_size; 1504 ret = read(fd, ocsp.data.data, sb.st_size); 1505 close(fd); 1506 if (ret != sb.st_size) { 1507 kdc_log(context, config, 0, 1508 "PK-INIT failed to read ocsp data %d", errno); 1509 goto out_ocsp; 1510 } 1511 1512 ret = hx509_ocsp_verify(kdc_identity->hx509ctx, 1513 kdc_time, 1514 kdc_cert, 1515 0, 1516 ocsp.data.data, ocsp.data.length, 1517 &ocsp.expire); 1518 if (ret) { 1519 kdc_log(context, config, 0, 1520 "PK-INIT failed to verify ocsp data %d", ret); 1521 krb5_data_free(&ocsp.data); 1522 ocsp.expire = 0; 1523 } else if (ocsp.expire > 180) { 1524 ocsp.expire -= 180; /* refetch the ocsp before it expire */ 1525 ocsp.next_update = ocsp.expire; 1526 } else { 1527 ocsp.next_update = kdc_time; 1528 } 1529 out_ocsp: 1530 ret = 0; 1531 } 1532 1533 if (ocsp.expire != 0 && ocsp.expire > kdc_time) { 1534 1535 ret = krb5_padata_add(context, md, 1536 KRB5_PADATA_PA_PK_OCSP_RESPONSE, 1537 ocsp.data.data, ocsp.data.length); 1538 if (ret) { 1539 krb5_set_error_message(context, ret, 1540 "Failed adding OCSP response %d", ret); 1541 goto out; 1542 } 1543 } 1544 } 1545 1546out: 1547 if (kdc_cert) 1548 hx509_cert_free(kdc_cert); 1549 1550 if (ret == 0) 1551 *reply_key = &cp->reply_key; 1552 return ret; 1553} 1554 1555static int 1556match_rfc_san(krb5_context context, 1557 krb5_kdc_configuration *config, 1558 hx509_context hx509ctx, 1559 hx509_cert client_cert, 1560 krb5_const_principal match) 1561{ 1562 hx509_octet_string_list list; 1563 int ret, i, found = 0; 1564 1565 memset(&list, 0 , sizeof(list)); 1566 1567 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, 1568 client_cert, 1569 &asn1_oid_id_pkinit_san, 1570 &list); 1571 if (ret) 1572 goto out; 1573 1574 for (i = 0; !found && i < list.len; i++) { 1575 krb5_principal_data principal; 1576 KRB5PrincipalName kn; 1577 size_t size; 1578 1579 ret = decode_KRB5PrincipalName(list.val[i].data, 1580 list.val[i].length, 1581 &kn, &size); 1582 if (ret) { 1583 kdc_log(context, config, 0, 1584 "Decoding kerberos name in certificate failed: %s", 1585 krb5_get_err_text(context, ret)); 1586 break; 1587 } 1588 if (size != list.val[i].length) { 1589 kdc_log(context, config, 0, 1590 "Decoding kerberos name have extra bits on the end"); 1591 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; 1592 } 1593 1594 principal.name = kn.principalName; 1595 principal.realm = kn.realm; 1596 1597 if (krb5_principal_compare(context, &principal, match) == TRUE) 1598 found = 1; 1599 free_KRB5PrincipalName(&kn); 1600 } 1601 1602out: 1603 hx509_free_octet_string_list(&list); 1604 if (ret) 1605 return ret; 1606 1607 if (!found) 1608 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; 1609 1610 return 0; 1611} 1612 1613static int 1614match_ms_upn_san(krb5_context context, 1615 krb5_kdc_configuration *config, 1616 hx509_context hx509ctx, 1617 hx509_cert client_cert, 1618 HDB *clientdb, 1619 hdb_entry_ex *client) 1620{ 1621 hx509_octet_string_list list; 1622 krb5_principal principal = NULL; 1623 int ret; 1624 MS_UPN_SAN upn; 1625 size_t size; 1626 1627 memset(&list, 0 , sizeof(list)); 1628 1629 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, 1630 client_cert, 1631 &asn1_oid_id_pkinit_ms_san, 1632 &list); 1633 if (ret) 1634 goto out; 1635 1636 if (list.len != 1) { 1637 kdc_log(context, config, 0, 1638 "More then one PK-INIT MS UPN SAN"); 1639 goto out; 1640 } 1641 1642 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size); 1643 if (ret) { 1644 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed"); 1645 goto out; 1646 } 1647 1648 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn); 1649 1650 ret = krb5_parse_name(context, upn, &principal); 1651 free_MS_UPN_SAN(&upn); 1652 if (ret) { 1653 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN"); 1654 goto out; 1655 } 1656 1657 if (clientdb->hdb_check_pkinit_ms_upn_match) { 1658 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal); 1659 } else { 1660 1661 /* 1662 * This is very wrong, but will do for a fallback 1663 */ 1664 strupr(principal->realm); 1665 1666 if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE) 1667 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; 1668 } 1669 1670out: 1671 if (principal) 1672 krb5_free_principal(context, principal); 1673 hx509_free_octet_string_list(&list); 1674 1675 return ret; 1676} 1677 1678krb5_error_code 1679_kdc_pk_check_client(krb5_context context, 1680 krb5_kdc_configuration *config, 1681 HDB *clientdb, 1682 hdb_entry_ex *client, 1683 pk_client_params *cp, 1684 char **subject_name) 1685{ 1686 const HDB_Ext_PKINIT_acl *acl; 1687 const HDB_Ext_PKINIT_cert *pc; 1688 krb5_error_code ret; 1689 hx509_name name; 1690 int i; 1691 1692 if (cp->cert == NULL) { 1693 1694 *subject_name = strdup("anonymous client client"); 1695 if (*subject_name == NULL) 1696 return ENOMEM; 1697 return 0; 1698 } 1699 1700 ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx, 1701 cp->cert, 1702 &name); 1703 if (ret) 1704 return ret; 1705 1706 ret = hx509_name_to_string(name, subject_name); 1707 hx509_name_free(&name); 1708 if (ret) 1709 return ret; 1710 1711 kdc_log(context, config, 0, 1712 "Trying to authorize PK-INIT subject DN %s", 1713 *subject_name); 1714 1715 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); 1716 if (ret == 0 && pc) { 1717 hx509_cert cert; 1718 unsigned int i; 1719 1720 for (i = 0; i < pc->len; i++) { 1721 ret = hx509_cert_init_data(kdc_identity->hx509ctx, 1722 pc->val[i].cert.data, 1723 pc->val[i].cert.length, 1724 &cert); 1725 if (ret) 1726 continue; 1727 ret = hx509_cert_cmp(cert, cp->cert); 1728 hx509_cert_free(cert); 1729 if (ret == 0) { 1730 kdc_log(context, config, 5, 1731 "Found matching PK-INIT cert in hdb"); 1732 return 0; 1733 } 1734 } 1735 } 1736 1737 1738 if (config->pkinit_princ_in_cert) { 1739 ret = match_rfc_san(context, config, 1740 kdc_identity->hx509ctx, 1741 cp->cert, 1742 client->entry.principal); 1743 if (ret == 0) { 1744 kdc_log(context, config, 5, 1745 "Found matching PK-INIT SAN in certificate"); 1746 return 0; 1747 } 1748 ret = match_ms_upn_san(context, config, 1749 kdc_identity->hx509ctx, 1750 cp->cert, 1751 clientdb, 1752 client); 1753 if (ret == 0) { 1754 kdc_log(context, config, 5, 1755 "Found matching MS UPN SAN in certificate"); 1756 return 0; 1757 } 1758 } 1759 1760 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl); 1761 if (ret == 0 && acl != NULL) { 1762 /* 1763 * Cheat here and compare the generated name with the string 1764 * and not the reverse. 1765 */ 1766 for (i = 0; i < acl->len; i++) { 1767 if (strcmp(*subject_name, acl->val[0].subject) != 0) 1768 continue; 1769 1770 /* Don't support isser and anchor checking right now */ 1771 if (acl->val[0].issuer) 1772 continue; 1773 if (acl->val[0].anchor) 1774 continue; 1775 1776 kdc_log(context, config, 5, 1777 "Found matching PK-INIT database ACL"); 1778 return 0; 1779 } 1780 } 1781 1782 for (i = 0; i < principal_mappings.len; i++) { 1783 krb5_boolean b; 1784 1785 b = krb5_principal_compare(context, 1786 client->entry.principal, 1787 principal_mappings.val[i].principal); 1788 if (b == FALSE) 1789 continue; 1790 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0) 1791 continue; 1792 kdc_log(context, config, 5, 1793 "Found matching PK-INIT FILE ACL"); 1794 return 0; 1795 } 1796 1797 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; 1798 krb5_set_error_message(context, ret, 1799 "PKINIT no matching principals for %s", 1800 *subject_name); 1801 1802 kdc_log(context, config, 5, 1803 "PKINIT no matching principals for %s", 1804 *subject_name); 1805 1806 free(*subject_name); 1807 *subject_name = NULL; 1808 1809 return ret; 1810} 1811 1812static krb5_error_code 1813add_principal_mapping(krb5_context context, 1814 const char *principal_name, 1815 const char * subject) 1816{ 1817 struct pk_allowed_princ *tmp; 1818 krb5_principal principal; 1819 krb5_error_code ret; 1820 1821 tmp = realloc(principal_mappings.val, 1822 (principal_mappings.len + 1) * sizeof(*tmp)); 1823 if (tmp == NULL) 1824 return ENOMEM; 1825 principal_mappings.val = tmp; 1826 1827 ret = krb5_parse_name(context, principal_name, &principal); 1828 if (ret) 1829 return ret; 1830 1831 principal_mappings.val[principal_mappings.len].principal = principal; 1832 1833 principal_mappings.val[principal_mappings.len].subject = strdup(subject); 1834 if (principal_mappings.val[principal_mappings.len].subject == NULL) { 1835 krb5_free_principal(context, principal); 1836 return ENOMEM; 1837 } 1838 principal_mappings.len++; 1839 1840 return 0; 1841} 1842 1843krb5_error_code 1844_kdc_add_inital_verified_cas(krb5_context context, 1845 krb5_kdc_configuration *config, 1846 pk_client_params *cp, 1847 EncTicketPart *tkt) 1848{ 1849 AD_INITIAL_VERIFIED_CAS cas; 1850 krb5_error_code ret; 1851 krb5_data data; 1852 size_t size; 1853 1854 memset(&cas, 0, sizeof(cas)); 1855 1856 /* XXX add CAs to cas here */ 1857 1858 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length, 1859 &cas, &size, ret); 1860 if (ret) 1861 return ret; 1862 if (data.length != size) 1863 krb5_abortx(context, "internal asn.1 encoder error"); 1864 1865 ret = _kdc_tkt_add_if_relevant_ad(context, tkt, 1866 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS, 1867 &data); 1868 krb5_data_free(&data); 1869 return ret; 1870} 1871 1872/* 1873 * 1874 */ 1875 1876static void 1877load_mappings(krb5_context context, const char *fn) 1878{ 1879 krb5_error_code ret; 1880 char buf[1024]; 1881 unsigned long lineno = 0; 1882 FILE *f; 1883 1884 f = fopen(fn, "r"); 1885 if (f == NULL) 1886 return; 1887 1888 while (fgets(buf, sizeof(buf), f) != NULL) { 1889 char *subject_name, *p; 1890 1891 buf[strcspn(buf, "\n")] = '\0'; 1892 lineno++; 1893 1894 p = buf + strspn(buf, " \t"); 1895 1896 if (*p == '#' || *p == '\0') 1897 continue; 1898 1899 subject_name = strchr(p, ':'); 1900 if (subject_name == NULL) { 1901 krb5_warnx(context, "pkinit mapping file line %lu " 1902 "missing \":\" :%s", 1903 lineno, buf); 1904 continue; 1905 } 1906 *subject_name++ = '\0'; 1907 1908 ret = add_principal_mapping(context, p, subject_name); 1909 if (ret) { 1910 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n", 1911 lineno, buf); 1912 continue; 1913 } 1914 } 1915 1916 fclose(f); 1917} 1918 1919/* 1920 * 1921 */ 1922 1923krb5_error_code 1924_kdc_pk_initialize(krb5_context context, 1925 krb5_kdc_configuration *config, 1926 const char *user_id, 1927 const char *anchors, 1928 char **pool, 1929 char **revoke_list) 1930{ 1931 const char *file; 1932 char *fn = NULL; 1933 krb5_error_code ret; 1934 1935 file = krb5_config_get_string(context, NULL, 1936 "libdefaults", "moduli", NULL); 1937 1938 ret = _krb5_parse_moduli(context, file, &moduli); 1939 if (ret) 1940 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file"); 1941 1942 principal_mappings.len = 0; 1943 principal_mappings.val = NULL; 1944 1945 ret = _krb5_pk_load_id(context, 1946 &kdc_identity, 1947 0, 1948 user_id, 1949 anchors, 1950 pool, 1951 revoke_list, 1952 NULL, 1953 NULL, 1954 NULL); 1955 if (ret) { 1956 krb5_warn(context, ret, "PKINIT: "); 1957 config->enable_pkinit = 0; 1958 return ret; 1959 } 1960 1961 { 1962 hx509_query *q; 1963 hx509_cert cert; 1964 1965 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); 1966 if (ret) { 1967 krb5_warnx(context, "PKINIT: out of memory"); 1968 return ENOMEM; 1969 } 1970 1971 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 1972 if (config->pkinit_kdc_friendly_name) 1973 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); 1974 1975 ret = hx509_certs_find(kdc_identity->hx509ctx, 1976 kdc_identity->certs, 1977 q, 1978 &cert); 1979 hx509_query_free(kdc_identity->hx509ctx, q); 1980 if (ret == 0) { 1981 if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert, 1982 &asn1_oid_id_pkkdcekuoid, 0)) { 1983 hx509_name name; 1984 char *str; 1985 ret = hx509_cert_get_subject(cert, &name); 1986 if (ret == 0) { 1987 hx509_name_to_string(name, &str); 1988 krb5_warnx(context, "WARNING Found KDC certificate (%s)" 1989 "is missing the PK-INIT KDC EKU, this is bad for " 1990 "interoperability.", str); 1991 hx509_name_free(&name); 1992 free(str); 1993 } 1994 } 1995 hx509_cert_free(cert); 1996 } else 1997 krb5_warnx(context, "PKINIT: failed to find a signing " 1998 "certifiate with a public key"); 1999 } 2000 2001 if (krb5_config_get_bool_default(context, 2002 NULL, 2003 FALSE, 2004 "kdc", 2005 "pkinit_allow_proxy_certificate", 2006 NULL)) 2007 config->pkinit_allow_proxy_certs = 1; 2008 2009 file = krb5_config_get_string(context, 2010 NULL, 2011 "kdc", 2012 "pkinit_mappings_file", 2013 NULL); 2014 if (file == NULL) { 2015 asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context)); 2016 file = fn; 2017 } 2018 2019 load_mappings(context, file); 2020 if (fn) 2021 free(fn); 2022 2023 return 0; 2024} 2025 2026#endif /* PKINIT */ 2027