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