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