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