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