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