pkinit.c revision 1.3
1/* $NetBSD: pkinit.c,v 1.3 2018/02/05 16:00:53 christos Exp $ */ 2 3/* 4 * Copyright (c) 2003 - 2016 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * 3. Neither the name of the Institute nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#include "krb5_locl.h" 39 40struct krb5_dh_moduli { 41 char *name; 42 unsigned long bits; 43 heim_integer p; 44 heim_integer g; 45 heim_integer q; 46}; 47 48#ifdef PKINIT 49 50#include <krb5/cms_asn1.h> 51#include <krb5/pkcs8_asn1.h> 52#include <krb5/pkcs9_asn1.h> 53#include <krb5/pkcs12_asn1.h> 54#include <krb5/pkinit_asn1.h> 55#include <krb5/asn1_err.h> 56 57#include <krb5/der.h> 58 59struct krb5_pk_cert { 60 hx509_cert cert; 61}; 62 63static void 64pk_copy_error(krb5_context context, 65 hx509_context hx509ctx, 66 int hxret, 67 const char *fmt, 68 ...) 69 __attribute__ ((__format__ (__printf__, 4, 5))); 70 71/* 72 * 73 */ 74 75KRB5_LIB_FUNCTION void KRB5_LIB_CALL 76_krb5_pk_cert_free(struct krb5_pk_cert *cert) 77{ 78 if (cert->cert) { 79 hx509_cert_free(cert->cert); 80 } 81 free(cert); 82} 83 84static krb5_error_code 85BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer) 86{ 87 integer->length = BN_num_bytes(bn); 88 integer->data = malloc(integer->length); 89 if (integer->data == NULL) { 90 krb5_clear_error_message(context); 91 return ENOMEM; 92 } 93 BN_bn2bin(bn, integer->data); 94 integer->negative = BN_is_negative(bn); 95 return 0; 96} 97 98static BIGNUM * 99integer_to_BN(krb5_context context, const char *field, const heim_integer *f) 100{ 101 BIGNUM *bn; 102 103 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL); 104 if (bn == NULL) { 105 krb5_set_error_message(context, ENOMEM, 106 N_("PKINIT: parsing BN failed %s", ""), field); 107 return NULL; 108 } 109 BN_set_negative(bn, f->negative); 110 return bn; 111} 112 113static krb5_error_code 114select_dh_group(krb5_context context, DH *dh, unsigned long bits, 115 struct krb5_dh_moduli **moduli) 116{ 117 const struct krb5_dh_moduli *m; 118 119 if (bits == 0) { 120 m = moduli[1]; /* XXX */ 121 if (m == NULL) 122 m = moduli[0]; /* XXX */ 123 } else { 124 int i; 125 for (i = 0; moduli[i] != NULL; i++) { 126 if (bits < moduli[i]->bits) 127 break; 128 } 129 if (moduli[i] == NULL) { 130 krb5_set_error_message(context, EINVAL, 131 N_("Did not find a DH group parameter " 132 "matching requirement of %lu bits", ""), 133 bits); 134 return EINVAL; 135 } 136 m = moduli[i]; 137 } 138 139 BIGNUM *p = integer_to_BN(context, "p", &m->p); 140 if (p == NULL) 141 return ENOMEM; 142 BIGNUM *g = integer_to_BN(context, "g", &m->g); 143 if (g == NULL) 144 return ENOMEM; 145 BIGNUM *q = integer_to_BN(context, "q", &m->q); 146 if (q == NULL) 147 return ENOMEM; 148#if OPENSSL_VERSION_NUMBER < 0x10100000UL 149 dh->p = p; 150 dh->q = q; 151 dh->g = g; 152#else 153 DH_set0_pqg(dh, p, q, g); 154#endif 155 156 return 0; 157} 158 159struct certfind { 160 const char *type; 161 const heim_oid *oid; 162}; 163 164/* 165 * Try searchin the key by to use by first looking for for PK-INIT 166 * EKU, then the Microsoft smart card EKU and last, no special EKU at all. 167 */ 168 169static krb5_error_code 170find_cert(krb5_context context, struct krb5_pk_identity *id, 171 hx509_query *q, hx509_cert *cert) 172{ 173 struct certfind cf[4] = { 174 { "MobileMe EKU", NULL }, 175 { "PKINIT EKU", NULL }, 176 { "MS EKU", NULL }, 177 { "any (or no)", NULL } 178 }; 179 int ret = HX509_CERT_NOT_FOUND; 180 size_t i, start = 1; 181 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 }; 182 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids }; 183 184 185 if (id->flags & PKINIT_BTMM) 186 start = 0; 187 188 cf[0].oid = &mobileMe; 189 cf[1].oid = &asn1_oid_id_pkekuoid; 190 cf[2].oid = &asn1_oid_id_pkinit_ms_eku; 191 cf[3].oid = NULL; 192 193 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) { 194 ret = hx509_query_match_eku(q, cf[i].oid); 195 if (ret) { 196 pk_copy_error(context, context->hx509ctx, ret, 197 "Failed setting %s OID", cf[i].type); 198 return ret; 199 } 200 201 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert); 202 if (ret == 0) 203 break; 204 pk_copy_error(context, context->hx509ctx, ret, 205 "Failed finding certificate with %s OID", cf[i].type); 206 } 207 return ret; 208} 209 210 211static krb5_error_code 212create_signature(krb5_context context, 213 const heim_oid *eContentType, 214 krb5_data *eContent, 215 struct krb5_pk_identity *id, 216 hx509_peer_info peer, 217 krb5_data *sd_data) 218{ 219 int ret, flags = 0; 220 221 if (id->cert == NULL) 222 flags |= HX509_CMS_SIGNATURE_NO_SIGNER; 223 224 ret = hx509_cms_create_signed_1(context->hx509ctx, 225 flags, 226 eContentType, 227 eContent->data, 228 eContent->length, 229 NULL, 230 id->cert, 231 peer, 232 NULL, 233 id->certs, 234 sd_data); 235 if (ret) { 236 pk_copy_error(context, context->hx509ctx, ret, 237 "Create CMS signedData"); 238 return ret; 239 } 240 241 return 0; 242} 243 244static int 245cert2epi(hx509_context context, void *ctx, hx509_cert c) 246{ 247 ExternalPrincipalIdentifiers *ids = ctx; 248 ExternalPrincipalIdentifier id; 249 hx509_name subject = NULL; 250 void *p; 251 int ret; 252 253 if (ids->len > 10) 254 return 0; 255 256 memset(&id, 0, sizeof(id)); 257 258 ret = hx509_cert_get_subject(c, &subject); 259 if (ret) 260 return ret; 261 262 if (hx509_name_is_null_p(subject) != 0) { 263 264 id.subjectName = calloc(1, sizeof(*id.subjectName)); 265 if (id.subjectName == NULL) { 266 hx509_name_free(&subject); 267 free_ExternalPrincipalIdentifier(&id); 268 return ENOMEM; 269 } 270 271 ret = hx509_name_binary(subject, id.subjectName); 272 if (ret) { 273 hx509_name_free(&subject); 274 free_ExternalPrincipalIdentifier(&id); 275 return ret; 276 } 277 } 278 hx509_name_free(&subject); 279 280 281 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber)); 282 if (id.issuerAndSerialNumber == NULL) { 283 free_ExternalPrincipalIdentifier(&id); 284 return ENOMEM; 285 } 286 287 { 288 IssuerAndSerialNumber iasn; 289 hx509_name issuer; 290 size_t size = 0; 291 292 memset(&iasn, 0, sizeof(iasn)); 293 294 ret = hx509_cert_get_issuer(c, &issuer); 295 if (ret) { 296 free_ExternalPrincipalIdentifier(&id); 297 return ret; 298 } 299 300 ret = hx509_name_to_Name(issuer, &iasn.issuer); 301 hx509_name_free(&issuer); 302 if (ret) { 303 free_ExternalPrincipalIdentifier(&id); 304 return ret; 305 } 306 307 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber); 308 if (ret) { 309 free_IssuerAndSerialNumber(&iasn); 310 free_ExternalPrincipalIdentifier(&id); 311 return ret; 312 } 313 314 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber, 315 id.issuerAndSerialNumber->data, 316 id.issuerAndSerialNumber->length, 317 &iasn, &size, ret); 318 free_IssuerAndSerialNumber(&iasn); 319 if (ret) { 320 free_ExternalPrincipalIdentifier(&id); 321 return ret; 322 } 323 if (id.issuerAndSerialNumber->length != size) 324 abort(); 325 } 326 327 id.subjectKeyIdentifier = NULL; 328 329 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1)); 330 if (p == NULL) { 331 free_ExternalPrincipalIdentifier(&id); 332 return ENOMEM; 333 } 334 335 ids->val = p; 336 ids->val[ids->len] = id; 337 ids->len++; 338 339 return 0; 340} 341 342static krb5_error_code 343build_edi(krb5_context context, 344 hx509_context hx509ctx, 345 hx509_certs certs, 346 ExternalPrincipalIdentifiers *ids) 347{ 348 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids); 349} 350 351static krb5_error_code 352build_auth_pack(krb5_context context, 353 unsigned nonce, 354 krb5_pk_init_ctx ctx, 355 const KDC_REQ_BODY *body, 356 AuthPack *a) 357{ 358 size_t buf_size, len = 0; 359 krb5_error_code ret; 360 void *buf; 361 krb5_timestamp sec; 362 int32_t usec; 363 Checksum checksum; 364 365 krb5_clear_error_message(context); 366 367 memset(&checksum, 0, sizeof(checksum)); 368 369 krb5_us_timeofday(context, &sec, &usec); 370 a->pkAuthenticator.ctime = sec; 371 a->pkAuthenticator.nonce = nonce; 372 373 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); 374 if (ret) 375 return ret; 376 if (buf_size != len) 377 krb5_abortx(context, "internal error in ASN.1 encoder"); 378 379 ret = krb5_create_checksum(context, 380 NULL, 381 0, 382 CKSUMTYPE_SHA1, 383 buf, 384 len, 385 &checksum); 386 free(buf); 387 if (ret) 388 return ret; 389 390 ALLOC(a->pkAuthenticator.paChecksum, 1); 391 if (a->pkAuthenticator.paChecksum == NULL) { 392 return krb5_enomem(context); 393 } 394 395 ret = krb5_data_copy(a->pkAuthenticator.paChecksum, 396 checksum.checksum.data, checksum.checksum.length); 397 free_Checksum(&checksum); 398 if (ret) 399 return ret; 400 401 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) { 402 const char *moduli_file; 403 unsigned long dh_min_bits; 404 krb5_data dhbuf; 405 size_t size = 0; 406 407 krb5_data_zero(&dhbuf); 408 409 410 411 moduli_file = krb5_config_get_string(context, NULL, 412 "libdefaults", 413 "moduli", 414 NULL); 415 416 dh_min_bits = 417 krb5_config_get_int_default(context, NULL, 0, 418 "libdefaults", 419 "pkinit_dh_min_bits", 420 NULL); 421 422 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m); 423 if (ret) 424 return ret; 425 426 ctx->u.dh = DH_new(); 427 if (ctx->u.dh == NULL) 428 return krb5_enomem(context); 429 430 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m); 431 if (ret) 432 return ret; 433 434 if (DH_generate_key(ctx->u.dh) != 1) { 435 krb5_set_error_message(context, ENOMEM, 436 N_("pkinit: failed to generate DH key", "")); 437 return ENOMEM; 438 } 439 440 441 if (1 /* support_cached_dh */) { 442 ALLOC(a->clientDHNonce, 1); 443 if (a->clientDHNonce == NULL) { 444 krb5_clear_error_message(context); 445 return ENOMEM; 446 } 447 ret = krb5_data_alloc(a->clientDHNonce, 40); 448 if (a->clientDHNonce == NULL) { 449 krb5_clear_error_message(context); 450 return ret; 451 } 452 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length); 453 ret = krb5_copy_data(context, a->clientDHNonce, 454 &ctx->clientDHNonce); 455 if (ret) 456 return ret; 457 } 458 459 ALLOC(a->clientPublicValue, 1); 460 if (a->clientPublicValue == NULL) 461 return ENOMEM; 462 463 if (ctx->keyex == USE_DH) { 464 DH *dh = ctx->u.dh; 465 const BIGNUM *p, *q, *g; 466 DomainParameters dp; 467 heim_integer dh_pub_key; 468 469 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber, 470 &a->clientPublicValue->algorithm.algorithm); 471 if (ret) 472 return ret; 473 474 memset(&dp, 0, sizeof(dp)); 475 476#if OPENSSL_VERSION_NUMBER < 0x10100000UL 477 p = dh->p; 478 q = dh->q; 479 g = dh->g; 480#else 481 DH_get0_pqg(dh, &p, &q, &g); 482#endif 483 484 ret = BN_to_integer(context, __UNCONST(p), &dp.p); 485 if (ret) { 486 free_DomainParameters(&dp); 487 return ret; 488 } 489 ret = BN_to_integer(context, __UNCONST(g), &dp.g); 490 if (ret) { 491 free_DomainParameters(&dp); 492 return ret; 493 } 494 dp.q = calloc(1, sizeof(*dp.q)); 495 if (dp.q == NULL) { 496 free_DomainParameters(&dp); 497 return ENOMEM; 498 } 499 ret = BN_to_integer(context, __UNCONST(q), dp.q); 500 if (ret) { 501 free_DomainParameters(&dp); 502 return ret; 503 } 504 dp.j = NULL; 505 dp.validationParms = NULL; 506 507 a->clientPublicValue->algorithm.parameters = 508 malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); 509 if (a->clientPublicValue->algorithm.parameters == NULL) { 510 free_DomainParameters(&dp); 511 return ret; 512 } 513 514 ASN1_MALLOC_ENCODE(DomainParameters, 515 a->clientPublicValue->algorithm.parameters->data, 516 a->clientPublicValue->algorithm.parameters->length, 517 &dp, &size, ret); 518 free_DomainParameters(&dp); 519 if (ret) 520 return ret; 521 if (size != a->clientPublicValue->algorithm.parameters->length) 522 krb5_abortx(context, "Internal ASN1 encoder error"); 523 524 const BIGNUM *pub_key; 525#if OPENSSL_VERSION_NUMBER < 0x10100000UL 526 pub_key = dh->pub_key; 527#else 528 DH_get0_key(dh, &pub_key, NULL); 529#endif 530 ret = BN_to_integer(context, __UNCONST(pub_key), &dh_pub_key); 531 if (ret) 532 return ret; 533 534 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, 535 &dh_pub_key, &size, ret); 536 der_free_heim_integer(&dh_pub_key); 537 if (ret) 538 return ret; 539 if (size != dhbuf.length) 540 krb5_abortx(context, "asn1 internal error"); 541 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8; 542 a->clientPublicValue->subjectPublicKey.data = dhbuf.data; 543 } else if (ctx->keyex == USE_ECDH) { 544 ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a); 545 if (ret) 546 return ret; 547 } else 548 krb5_abortx(context, "internal error"); 549 } 550 551 { 552 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes)); 553 if (a->supportedCMSTypes == NULL) 554 return ENOMEM; 555 556 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, 557 ctx->id->cert, 558 &a->supportedCMSTypes->val, 559 &a->supportedCMSTypes->len); 560 if (ret) 561 return ret; 562 } 563 564 return ret; 565} 566 567KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 568_krb5_pk_mk_ContentInfo(krb5_context context, 569 const krb5_data *buf, 570 const heim_oid *oid, 571 struct ContentInfo *content_info) 572{ 573 krb5_error_code ret; 574 575 ret = der_copy_oid(oid, &content_info->contentType); 576 if (ret) 577 return ret; 578 ALLOC(content_info->content, 1); 579 if (content_info->content == NULL) 580 return ENOMEM; 581 content_info->content->data = malloc(buf->length); 582 if (content_info->content->data == NULL) 583 return ENOMEM; 584 memcpy(content_info->content->data, buf->data, buf->length); 585 content_info->content->length = buf->length; 586 return 0; 587} 588 589static krb5_error_code 590pk_mk_padata(krb5_context context, 591 krb5_pk_init_ctx ctx, 592 const KDC_REQ_BODY *req_body, 593 unsigned nonce, 594 METHOD_DATA *md) 595{ 596 struct ContentInfo content_info; 597 krb5_error_code ret; 598 const heim_oid *oid = NULL; 599 size_t size = 0; 600 krb5_data buf, sd_buf; 601 int pa_type = -1; 602 603 krb5_data_zero(&buf); 604 krb5_data_zero(&sd_buf); 605 memset(&content_info, 0, sizeof(content_info)); 606 607 if (ctx->type == PKINIT_WIN2K) { 608 AuthPack_Win2k ap; 609 krb5_timestamp sec; 610 int32_t usec; 611 612 memset(&ap, 0, sizeof(ap)); 613 614 /* fill in PKAuthenticator */ 615 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName); 616 if (ret) { 617 free_AuthPack_Win2k(&ap); 618 krb5_clear_error_message(context); 619 goto out; 620 } 621 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm); 622 if (ret) { 623 free_AuthPack_Win2k(&ap); 624 krb5_clear_error_message(context); 625 goto out; 626 } 627 628 krb5_us_timeofday(context, &sec, &usec); 629 ap.pkAuthenticator.ctime = sec; 630 ap.pkAuthenticator.cusec = usec; 631 ap.pkAuthenticator.nonce = nonce; 632 633 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length, 634 &ap, &size, ret); 635 free_AuthPack_Win2k(&ap); 636 if (ret) { 637 krb5_set_error_message(context, ret, 638 N_("Failed encoding AuthPackWin: %d", ""), 639 (int)ret); 640 goto out; 641 } 642 if (buf.length != size) 643 krb5_abortx(context, "internal ASN1 encoder error"); 644 645 oid = &asn1_oid_id_pkcs7_data; 646 } else if (ctx->type == PKINIT_27) { 647 AuthPack ap; 648 649 memset(&ap, 0, sizeof(ap)); 650 651 ret = build_auth_pack(context, nonce, ctx, req_body, &ap); 652 if (ret) { 653 free_AuthPack(&ap); 654 goto out; 655 } 656 657 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret); 658 free_AuthPack(&ap); 659 if (ret) { 660 krb5_set_error_message(context, ret, 661 N_("Failed encoding AuthPack: %d", ""), 662 (int)ret); 663 goto out; 664 } 665 if (buf.length != size) 666 krb5_abortx(context, "internal ASN1 encoder error"); 667 668 oid = &asn1_oid_id_pkauthdata; 669 } else 670 krb5_abortx(context, "internal pkinit error"); 671 672 ret = create_signature(context, oid, &buf, ctx->id, 673 ctx->peer, &sd_buf); 674 krb5_data_free(&buf); 675 if (ret) 676 goto out; 677 678 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf); 679 krb5_data_free(&sd_buf); 680 if (ret) { 681 krb5_set_error_message(context, ret, 682 N_("ContentInfo wrapping of signedData failed","")); 683 goto out; 684 } 685 686 if (ctx->type == PKINIT_WIN2K) { 687 PA_PK_AS_REQ_Win2k winreq; 688 689 pa_type = KRB5_PADATA_PK_AS_REQ_WIN; 690 691 memset(&winreq, 0, sizeof(winreq)); 692 693 winreq.signed_auth_pack = buf; 694 695 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length, 696 &winreq, &size, ret); 697 free_PA_PK_AS_REQ_Win2k(&winreq); 698 699 } else if (ctx->type == PKINIT_27) { 700 PA_PK_AS_REQ req; 701 702 pa_type = KRB5_PADATA_PK_AS_REQ; 703 704 memset(&req, 0, sizeof(req)); 705 req.signedAuthPack = buf; 706 707 if (ctx->trustedCertifiers) { 708 709 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers)); 710 if (req.trustedCertifiers == NULL) { 711 ret = krb5_enomem(context); 712 free_PA_PK_AS_REQ(&req); 713 goto out; 714 } 715 ret = build_edi(context, context->hx509ctx, 716 ctx->id->anchors, req.trustedCertifiers); 717 if (ret) { 718 krb5_set_error_message(context, ret, 719 N_("pk-init: failed to build " 720 "trustedCertifiers", "")); 721 free_PA_PK_AS_REQ(&req); 722 goto out; 723 } 724 } 725 req.kdcPkId = NULL; 726 727 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length, 728 &req, &size, ret); 729 730 free_PA_PK_AS_REQ(&req); 731 732 } else 733 krb5_abortx(context, "internal pkinit error"); 734 if (ret) { 735 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret); 736 goto out; 737 } 738 if (buf.length != size) 739 krb5_abortx(context, "Internal ASN1 encoder error"); 740 741 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length); 742 if (ret) 743 free(buf.data); 744 745 if (ret == 0) 746 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0); 747 748 out: 749 free_ContentInfo(&content_info); 750 751 return ret; 752} 753 754 755KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 756_krb5_pk_mk_padata(krb5_context context, 757 void *c, 758 int ic_flags, 759 int win2k, 760 const KDC_REQ_BODY *req_body, 761 unsigned nonce, 762 METHOD_DATA *md) 763{ 764 krb5_pk_init_ctx ctx = c; 765 int win2k_compat; 766 767 if (ctx->id->certs == NULL && ctx->anonymous == 0) { 768 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY, 769 N_("PKINIT: No user certificate given", "")); 770 return HEIM_PKINIT_NO_PRIVATE_KEY; 771 } 772 773 win2k_compat = krb5_config_get_bool_default(context, NULL, 774 win2k, 775 "realms", 776 req_body->realm, 777 "pkinit_win2k", 778 NULL); 779 780 if (win2k_compat) { 781 ctx->require_binding = 782 krb5_config_get_bool_default(context, NULL, 783 TRUE, 784 "realms", 785 req_body->realm, 786 "pkinit_win2k_require_binding", 787 NULL); 788 ctx->type = PKINIT_WIN2K; 789 } else 790 ctx->type = PKINIT_27; 791 792 ctx->require_eku = 793 krb5_config_get_bool_default(context, NULL, 794 TRUE, 795 "realms", 796 req_body->realm, 797 "pkinit_require_eku", 798 NULL); 799 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK) 800 ctx->require_eku = 0; 801 if (ctx->id->flags & PKINIT_BTMM) 802 ctx->require_eku = 0; 803 804 ctx->require_krbtgt_otherName = 805 krb5_config_get_bool_default(context, NULL, 806 TRUE, 807 "realms", 808 req_body->realm, 809 "pkinit_require_krbtgt_otherName", 810 NULL); 811 812 ctx->require_hostname_match = 813 krb5_config_get_bool_default(context, NULL, 814 FALSE, 815 "realms", 816 req_body->realm, 817 "pkinit_require_hostname_match", 818 NULL); 819 820 ctx->trustedCertifiers = 821 krb5_config_get_bool_default(context, NULL, 822 TRUE, 823 "realms", 824 req_body->realm, 825 "pkinit_trustedCertifiers", 826 NULL); 827 828 return pk_mk_padata(context, ctx, req_body, nonce, md); 829} 830 831static krb5_error_code 832pk_verify_sign(krb5_context context, 833 const void *data, 834 size_t length, 835 struct krb5_pk_identity *id, 836 heim_oid *contentType, 837 krb5_data *content, 838 struct krb5_pk_cert **signer) 839{ 840 hx509_certs signer_certs; 841 int ret, flags = 0; 842 843 /* BTMM is broken in Leo and SnowLeo */ 844 if (id->flags & PKINIT_BTMM) { 845 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; 846 flags |= HX509_CMS_VS_NO_KU_CHECK; 847 flags |= HX509_CMS_VS_NO_VALIDATE; 848 } 849 850 *signer = NULL; 851 852 ret = hx509_cms_verify_signed(context->hx509ctx, 853 id->verify_ctx, 854 flags, 855 data, 856 length, 857 NULL, 858 id->certpool, 859 contentType, 860 content, 861 &signer_certs); 862 if (ret) { 863 pk_copy_error(context, context->hx509ctx, ret, 864 "CMS verify signed failed"); 865 return ret; 866 } 867 868 *signer = calloc(1, sizeof(**signer)); 869 if (*signer == NULL) { 870 krb5_clear_error_message(context); 871 ret = ENOMEM; 872 goto out; 873 } 874 875 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert); 876 if (ret) { 877 pk_copy_error(context, context->hx509ctx, ret, 878 "Failed to get on of the signer certs"); 879 goto out; 880 } 881 882 out: 883 hx509_certs_free(&signer_certs); 884 if (ret) { 885 if (*signer) { 886 hx509_cert_free((*signer)->cert); 887 free(*signer); 888 *signer = NULL; 889 } 890 } 891 892 return ret; 893} 894 895static krb5_error_code 896get_reply_key_win(krb5_context context, 897 const krb5_data *content, 898 unsigned nonce, 899 krb5_keyblock **key) 900{ 901 ReplyKeyPack_Win2k key_pack; 902 krb5_error_code ret; 903 size_t size; 904 905 ret = decode_ReplyKeyPack_Win2k(content->data, 906 content->length, 907 &key_pack, 908 &size); 909 if (ret) { 910 krb5_set_error_message(context, ret, 911 N_("PKINIT decoding reply key failed", "")); 912 free_ReplyKeyPack_Win2k(&key_pack); 913 return ret; 914 } 915 916 if ((unsigned)key_pack.nonce != nonce) { 917 krb5_set_error_message(context, ret, 918 N_("PKINIT enckey nonce is wrong", "")); 919 free_ReplyKeyPack_Win2k(&key_pack); 920 return KRB5KRB_AP_ERR_MODIFIED; 921 } 922 923 *key = malloc (sizeof (**key)); 924 if (*key == NULL) { 925 free_ReplyKeyPack_Win2k(&key_pack); 926 return krb5_enomem(context); 927 } 928 929 ret = copy_EncryptionKey(&key_pack.replyKey, *key); 930 free_ReplyKeyPack_Win2k(&key_pack); 931 if (ret) { 932 krb5_set_error_message(context, ret, 933 N_("PKINIT failed copying reply key", "")); 934 free(*key); 935 *key = NULL; 936 } 937 938 return ret; 939} 940 941static krb5_error_code 942get_reply_key(krb5_context context, 943 const krb5_data *content, 944 const krb5_data *req_buffer, 945 krb5_keyblock **key) 946{ 947 ReplyKeyPack key_pack; 948 krb5_error_code ret; 949 size_t size; 950 951 ret = decode_ReplyKeyPack(content->data, 952 content->length, 953 &key_pack, 954 &size); 955 if (ret) { 956 krb5_set_error_message(context, ret, 957 N_("PKINIT decoding reply key failed", "")); 958 free_ReplyKeyPack(&key_pack); 959 return ret; 960 } 961 962 { 963 krb5_crypto crypto; 964 965 /* 966 * XXX Verify kp.replyKey is a allowed enctype in the 967 * configuration file 968 */ 969 970 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto); 971 if (ret) { 972 free_ReplyKeyPack(&key_pack); 973 return ret; 974 } 975 976 ret = krb5_verify_checksum(context, crypto, 6, 977 req_buffer->data, req_buffer->length, 978 &key_pack.asChecksum); 979 krb5_crypto_destroy(context, crypto); 980 if (ret) { 981 free_ReplyKeyPack(&key_pack); 982 return ret; 983 } 984 } 985 986 *key = malloc (sizeof (**key)); 987 if (*key == NULL) { 988 free_ReplyKeyPack(&key_pack); 989 return krb5_enomem(context); 990 } 991 992 ret = copy_EncryptionKey(&key_pack.replyKey, *key); 993 free_ReplyKeyPack(&key_pack); 994 if (ret) { 995 krb5_set_error_message(context, ret, 996 N_("PKINIT failed copying reply key", "")); 997 free(*key); 998 *key = NULL; 999 } 1000 1001 return ret; 1002} 1003 1004 1005static krb5_error_code 1006pk_verify_host(krb5_context context, 1007 const char *realm, 1008 const krb5_krbhst_info *hi, 1009 struct krb5_pk_init_ctx_data *ctx, 1010 struct krb5_pk_cert *host) 1011{ 1012 krb5_error_code ret = 0; 1013 1014 if (ctx->require_eku) { 1015 ret = hx509_cert_check_eku(context->hx509ctx, host->cert, 1016 &asn1_oid_id_pkkdcekuoid, 0); 1017 if (ret) { 1018 krb5_set_error_message(context, ret, 1019 N_("No PK-INIT KDC EKU in kdc certificate", "")); 1020 return ret; 1021 } 1022 } 1023 if (ctx->require_krbtgt_otherName) { 1024 hx509_octet_string_list list; 1025 size_t i; 1026 int matched = 0; 1027 1028 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, 1029 host->cert, 1030 &asn1_oid_id_pkinit_san, 1031 &list); 1032 if (ret) { 1033 krb5_set_error_message(context, ret, 1034 N_("Failed to find the PK-INIT " 1035 "subjectAltName in the KDC " 1036 "certificate", "")); 1037 1038 return ret; 1039 } 1040 1041 /* 1042 * subjectAltNames are multi-valued, and a single KDC may serve 1043 * multiple realms. The SAN validation here must accept 1044 * the KDC's cert if *any* of the SANs match the expected KDC. 1045 * It is OK for *some* of the SANs to not match, provided at least 1046 * one does. 1047 */ 1048 for (i = 0; matched == 0 && i < list.len; i++) { 1049 KRB5PrincipalName r; 1050 1051 ret = decode_KRB5PrincipalName(list.val[i].data, 1052 list.val[i].length, 1053 &r, 1054 NULL); 1055 if (ret) { 1056 krb5_set_error_message(context, ret, 1057 N_("Failed to decode the PK-INIT " 1058 "subjectAltName in the " 1059 "KDC certificate", "")); 1060 1061 break; 1062 } 1063 1064 if (r.principalName.name_string.len == 2 && 1065 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0 1066 && strcmp(r.principalName.name_string.val[1], realm) == 0 1067 && strcmp(r.realm, realm) == 0) 1068 matched = 1; 1069 1070 free_KRB5PrincipalName(&r); 1071 } 1072 hx509_free_octet_string_list(&list); 1073 if (matched == 0) { 1074 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE; 1075 /* XXX: Lost in translation... */ 1076 krb5_set_error_message(context, ret, 1077 N_("KDC have wrong realm name in " 1078 "the certificate", "")); 1079 } 1080 } 1081 if (ret) 1082 return ret; 1083 1084 if (hi) { 1085 ret = hx509_verify_hostname(context->hx509ctx, host->cert, 1086 ctx->require_hostname_match, 1087 HX509_HN_HOSTNAME, 1088 hi->hostname, 1089 hi->ai->ai_addr, hi->ai->ai_addrlen); 1090 1091 if (ret) 1092 krb5_set_error_message(context, ret, 1093 N_("Address mismatch in " 1094 "the KDC certificate", "")); 1095 } 1096 return ret; 1097} 1098 1099static krb5_error_code 1100pk_rd_pa_reply_enckey(krb5_context context, 1101 int type, 1102 const heim_octet_string *indata, 1103 const heim_oid *dataType, 1104 const char *realm, 1105 krb5_pk_init_ctx ctx, 1106 krb5_enctype etype, 1107 const krb5_krbhst_info *hi, 1108 unsigned nonce, 1109 const krb5_data *req_buffer, 1110 PA_DATA *pa, 1111 krb5_keyblock **key) 1112{ 1113 krb5_error_code ret; 1114 struct krb5_pk_cert *host = NULL; 1115 krb5_data content; 1116 heim_oid contentType = { 0, NULL }; 1117 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT; 1118 1119 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) { 1120 krb5_set_error_message(context, EINVAL, 1121 N_("PKINIT: Invalid content type", "")); 1122 return EINVAL; 1123 } 1124 1125 if (ctx->type == PKINIT_WIN2K) 1126 flags |= HX509_CMS_UE_ALLOW_WEAK; 1127 1128 ret = hx509_cms_unenvelope(context->hx509ctx, 1129 ctx->id->certs, 1130 flags, 1131 indata->data, 1132 indata->length, 1133 NULL, 1134 0, 1135 &contentType, 1136 &content); 1137 if (ret) { 1138 pk_copy_error(context, context->hx509ctx, ret, 1139 "Failed to unenvelope CMS data in PK-INIT reply"); 1140 return ret; 1141 } 1142 der_free_oid(&contentType); 1143 1144 /* win2k uses ContentInfo */ 1145 if (type == PKINIT_WIN2K) { 1146 heim_oid type2; 1147 heim_octet_string out; 1148 1149 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); 1150 if (ret) { 1151 /* windows LH with interesting CMS packets */ 1152 size_t ph = 1 + der_length_len(content.length); 1153 unsigned char *ptr = malloc(content.length + ph); 1154 size_t l; 1155 1156 memcpy(ptr + ph, content.data, content.length); 1157 1158 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length, 1159 ASN1_C_UNIV, CONS, UT_Sequence, &l); 1160 if (ret) { 1161 free(ptr); 1162 return ret; 1163 } 1164 free(content.data); 1165 content.data = ptr; 1166 content.length += ph; 1167 1168 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); 1169 if (ret) 1170 goto out; 1171 } 1172 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) { 1173 ret = EINVAL; /* XXX */ 1174 krb5_set_error_message(context, ret, 1175 N_("PKINIT: Invalid content type", "")); 1176 der_free_oid(&type2); 1177 der_free_octet_string(&out); 1178 goto out; 1179 } 1180 der_free_oid(&type2); 1181 krb5_data_free(&content); 1182 ret = krb5_data_copy(&content, out.data, out.length); 1183 der_free_octet_string(&out); 1184 if (ret) { 1185 krb5_set_error_message(context, ret, 1186 N_("malloc: out of memory", "")); 1187 goto out; 1188 } 1189 } 1190 1191 ret = pk_verify_sign(context, 1192 content.data, 1193 content.length, 1194 ctx->id, 1195 &contentType, 1196 &content, 1197 &host); 1198 if (ret) 1199 goto out; 1200 1201 /* make sure that it is the kdc's certificate */ 1202 ret = pk_verify_host(context, realm, hi, ctx, host); 1203 if (ret) { 1204 goto out; 1205 } 1206 1207#if 0 1208 if (type == PKINIT_WIN2K) { 1209 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) { 1210 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1211 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid"); 1212 goto out; 1213 } 1214 } else { 1215 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) { 1216 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1217 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid"); 1218 goto out; 1219 } 1220 } 1221#endif 1222 1223 switch(type) { 1224 case PKINIT_WIN2K: 1225 ret = get_reply_key(context, &content, req_buffer, key); 1226 if (ret != 0 && ctx->require_binding == 0) 1227 ret = get_reply_key_win(context, &content, nonce, key); 1228 break; 1229 case PKINIT_27: 1230 ret = get_reply_key(context, &content, req_buffer, key); 1231 break; 1232 } 1233 if (ret) 1234 goto out; 1235 1236 /* XXX compare given etype with key->etype */ 1237 1238 out: 1239 if (host) 1240 _krb5_pk_cert_free(host); 1241 der_free_oid(&contentType); 1242 krb5_data_free(&content); 1243 1244 return ret; 1245} 1246 1247static krb5_error_code 1248pk_rd_pa_reply_dh(krb5_context context, 1249 const heim_octet_string *indata, 1250 const heim_oid *dataType, 1251 const char *realm, 1252 krb5_pk_init_ctx ctx, 1253 krb5_enctype etype, 1254 const krb5_krbhst_info *hi, 1255 const DHNonce *c_n, 1256 const DHNonce *k_n, 1257 unsigned nonce, 1258 PA_DATA *pa, 1259 krb5_keyblock **key) 1260{ 1261 const unsigned char *p; 1262 unsigned char *dh_gen_key = NULL; 1263 struct krb5_pk_cert *host = NULL; 1264 BIGNUM *kdc_dh_pubkey = NULL; 1265 KDCDHKeyInfo kdc_dh_info; 1266 heim_oid contentType = { 0, NULL }; 1267 krb5_data content; 1268 krb5_error_code ret; 1269 int dh_gen_keylen = 0; 1270 size_t size; 1271 1272 krb5_data_zero(&content); 1273 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info)); 1274 1275 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) { 1276 krb5_set_error_message(context, EINVAL, 1277 N_("PKINIT: Invalid content type", "")); 1278 return EINVAL; 1279 } 1280 1281 ret = pk_verify_sign(context, 1282 indata->data, 1283 indata->length, 1284 ctx->id, 1285 &contentType, 1286 &content, 1287 &host); 1288 if (ret) 1289 goto out; 1290 1291 /* make sure that it is the kdc's certificate */ 1292 ret = pk_verify_host(context, realm, hi, ctx, host); 1293 if (ret) 1294 goto out; 1295 1296 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) { 1297 ret = KRB5KRB_AP_ERR_MSG_TYPE; 1298 krb5_set_error_message(context, ret, 1299 N_("pkinit - dh reply contains wrong oid", "")); 1300 goto out; 1301 } 1302 1303 ret = decode_KDCDHKeyInfo(content.data, 1304 content.length, 1305 &kdc_dh_info, 1306 &size); 1307 1308 if (ret) { 1309 krb5_set_error_message(context, ret, 1310 N_("pkinit - failed to decode " 1311 "KDC DH Key Info", "")); 1312 goto out; 1313 } 1314 1315 if (kdc_dh_info.nonce != nonce) { 1316 ret = KRB5KRB_AP_ERR_MODIFIED; 1317 krb5_set_error_message(context, ret, 1318 N_("PKINIT: DH nonce is wrong", "")); 1319 goto out; 1320 } 1321 1322 if (kdc_dh_info.dhKeyExpiration) { 1323 if (k_n == NULL) { 1324 ret = KRB5KRB_ERR_GENERIC; 1325 krb5_set_error_message(context, ret, 1326 N_("pkinit; got key expiration " 1327 "without server nonce", "")); 1328 goto out; 1329 } 1330 if (c_n == NULL) { 1331 ret = KRB5KRB_ERR_GENERIC; 1332 krb5_set_error_message(context, ret, 1333 N_("pkinit; got DH reuse but no " 1334 "client nonce", "")); 1335 goto out; 1336 } 1337 } else { 1338 if (k_n) { 1339 ret = KRB5KRB_ERR_GENERIC; 1340 krb5_set_error_message(context, ret, 1341 N_("pkinit: got server nonce " 1342 "without key expiration", "")); 1343 goto out; 1344 } 1345 c_n = NULL; 1346 } 1347 1348 1349 p = kdc_dh_info.subjectPublicKey.data; 1350 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8; 1351 1352 if (ctx->keyex == USE_DH) { 1353 DHPublicKey k; 1354 ret = decode_DHPublicKey(p, size, &k, NULL); 1355 if (ret) { 1356 krb5_set_error_message(context, ret, 1357 N_("pkinit: can't decode " 1358 "without key expiration", "")); 1359 goto out; 1360 } 1361 1362 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k); 1363 free_DHPublicKey(&k); 1364 if (kdc_dh_pubkey == NULL) { 1365 ret = ENOMEM; 1366 goto out; 1367 } 1368 1369 1370 size = DH_size(ctx->u.dh); 1371 1372 dh_gen_key = malloc(size); 1373 if (dh_gen_key == NULL) { 1374 ret = krb5_enomem(context); 1375 goto out; 1376 } 1377 1378 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh); 1379 if (dh_gen_keylen == -1) { 1380 ret = KRB5KRB_ERR_GENERIC; 1381 dh_gen_keylen = 0; 1382 krb5_set_error_message(context, ret, 1383 N_("PKINIT: Can't compute Diffie-Hellman key", "")); 1384 goto out; 1385 } 1386 if (dh_gen_keylen < (int)size) { 1387 size -= dh_gen_keylen; 1388 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen); 1389 memset(dh_gen_key, 0, size); 1390 } 1391 1392 } else { 1393 ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p, 1394 size, &dh_gen_key, 1395 &dh_gen_keylen); 1396 if (ret) 1397 goto out; 1398 } 1399 1400 if (dh_gen_keylen <= 0) { 1401 ret = EINVAL; 1402 krb5_set_error_message(context, ret, 1403 N_("PKINIT: resulting DH key <= 0", "")); 1404 dh_gen_keylen = 0; 1405 goto out; 1406 } 1407 1408 *key = malloc (sizeof (**key)); 1409 if (*key == NULL) { 1410 ret = krb5_enomem(context); 1411 goto out; 1412 } 1413 1414 ret = _krb5_pk_octetstring2key(context, 1415 etype, 1416 dh_gen_key, dh_gen_keylen, 1417 c_n, k_n, 1418 *key); 1419 if (ret) { 1420 krb5_set_error_message(context, ret, 1421 N_("PKINIT: can't create key from DH key", "")); 1422 free(*key); 1423 *key = NULL; 1424 goto out; 1425 } 1426 1427 out: 1428 if (kdc_dh_pubkey) 1429 BN_free(kdc_dh_pubkey); 1430 if (dh_gen_key) { 1431 memset(dh_gen_key, 0, dh_gen_keylen); 1432 free(dh_gen_key); 1433 } 1434 if (host) 1435 _krb5_pk_cert_free(host); 1436 if (content.data) 1437 krb5_data_free(&content); 1438 der_free_oid(&contentType); 1439 free_KDCDHKeyInfo(&kdc_dh_info); 1440 1441 return ret; 1442} 1443 1444KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1445_krb5_pk_rd_pa_reply(krb5_context context, 1446 const char *realm, 1447 void *c, 1448 krb5_enctype etype, 1449 const krb5_krbhst_info *hi, 1450 unsigned nonce, 1451 const krb5_data *req_buffer, 1452 PA_DATA *pa, 1453 krb5_keyblock **key) 1454{ 1455 krb5_pk_init_ctx ctx = c; 1456 krb5_error_code ret; 1457 size_t size; 1458 1459 /* Check for IETF PK-INIT first */ 1460 if (ctx->type == PKINIT_27) { 1461 PA_PK_AS_REP rep; 1462 heim_octet_string os, data; 1463 heim_oid oid; 1464 1465 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { 1466 krb5_set_error_message(context, EINVAL, 1467 N_("PKINIT: wrong padata recv", "")); 1468 return EINVAL; 1469 } 1470 1471 ret = decode_PA_PK_AS_REP(pa->padata_value.data, 1472 pa->padata_value.length, 1473 &rep, 1474 &size); 1475 if (ret) { 1476 krb5_set_error_message(context, ret, 1477 N_("Failed to decode pkinit AS rep", "")); 1478 return ret; 1479 } 1480 1481 switch (rep.element) { 1482 case choice_PA_PK_AS_REP_dhInfo: 1483 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh"); 1484 os = rep.u.dhInfo.dhSignedData; 1485 break; 1486 case choice_PA_PK_AS_REP_encKeyPack: 1487 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key"); 1488 os = rep.u.encKeyPack; 1489 break; 1490 default: { 1491 PA_PK_AS_REP_BTMM btmm; 1492 free_PA_PK_AS_REP(&rep); 1493 memset(&rep, 0, sizeof(rep)); 1494 1495 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key"); 1496 1497 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data, 1498 pa->padata_value.length, 1499 &btmm, 1500 &size); 1501 if (ret) { 1502 krb5_set_error_message(context, EINVAL, 1503 N_("PKINIT: -27 reply " 1504 "invalid content type", "")); 1505 return EINVAL; 1506 } 1507 1508 if (btmm.dhSignedData || btmm.encKeyPack == NULL) { 1509 free_PA_PK_AS_REP_BTMM(&btmm); 1510 ret = EINVAL; 1511 krb5_set_error_message(context, ret, 1512 N_("DH mode not supported for BTMM mode", "")); 1513 return ret; 1514 } 1515 1516 /* 1517 * Transform to IETF style PK-INIT reply so that free works below 1518 */ 1519 1520 rep.element = choice_PA_PK_AS_REP_encKeyPack; 1521 rep.u.encKeyPack.data = btmm.encKeyPack->data; 1522 rep.u.encKeyPack.length = btmm.encKeyPack->length; 1523 btmm.encKeyPack->data = NULL; 1524 btmm.encKeyPack->length = 0; 1525 free_PA_PK_AS_REP_BTMM(&btmm); 1526 os = rep.u.encKeyPack; 1527 } 1528 } 1529 1530 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL); 1531 if (ret) { 1532 free_PA_PK_AS_REP(&rep); 1533 krb5_set_error_message(context, ret, 1534 N_("PKINIT: failed to unwrap CI", "")); 1535 return ret; 1536 } 1537 1538 switch (rep.element) { 1539 case choice_PA_PK_AS_REP_dhInfo: 1540 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi, 1541 ctx->clientDHNonce, 1542 rep.u.dhInfo.serverDHNonce, 1543 nonce, pa, key); 1544 break; 1545 case choice_PA_PK_AS_REP_encKeyPack: 1546 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm, 1547 ctx, etype, hi, nonce, req_buffer, pa, key); 1548 break; 1549 default: 1550 krb5_abortx(context, "pk-init as-rep case not possible to happen"); 1551 } 1552 der_free_octet_string(&data); 1553 der_free_oid(&oid); 1554 free_PA_PK_AS_REP(&rep); 1555 1556 } else if (ctx->type == PKINIT_WIN2K) { 1557 PA_PK_AS_REP_Win2k w2krep; 1558 1559 /* Check for Windows encoding of the AS-REP pa data */ 1560 1561#if 0 /* should this be ? */ 1562 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { 1563 krb5_set_error_message(context, EINVAL, 1564 "PKINIT: wrong padata recv"); 1565 return EINVAL; 1566 } 1567#endif 1568 1569 memset(&w2krep, 0, sizeof(w2krep)); 1570 1571 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data, 1572 pa->padata_value.length, 1573 &w2krep, 1574 &size); 1575 if (ret) { 1576 krb5_set_error_message(context, ret, 1577 N_("PKINIT: Failed decoding windows " 1578 "pkinit reply %d", ""), (int)ret); 1579 return ret; 1580 } 1581 1582 krb5_clear_error_message(context); 1583 1584 switch (w2krep.element) { 1585 case choice_PA_PK_AS_REP_Win2k_encKeyPack: { 1586 heim_octet_string data; 1587 heim_oid oid; 1588 1589 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack, 1590 &oid, &data, NULL); 1591 free_PA_PK_AS_REP_Win2k(&w2krep); 1592 if (ret) { 1593 krb5_set_error_message(context, ret, 1594 N_("PKINIT: failed to unwrap CI", "")); 1595 return ret; 1596 } 1597 1598 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm, 1599 ctx, etype, hi, nonce, req_buffer, pa, key); 1600 der_free_octet_string(&data); 1601 der_free_oid(&oid); 1602 1603 break; 1604 } 1605 default: 1606 free_PA_PK_AS_REP_Win2k(&w2krep); 1607 ret = EINVAL; 1608 krb5_set_error_message(context, ret, 1609 N_("PKINIT: win2k reply invalid " 1610 "content type", "")); 1611 break; 1612 } 1613 1614 } else { 1615 ret = EINVAL; 1616 krb5_set_error_message(context, ret, 1617 N_("PKINIT: unknown reply type", "")); 1618 } 1619 1620 return ret; 1621} 1622 1623struct prompter { 1624 krb5_context context; 1625 krb5_prompter_fct prompter; 1626 void *prompter_data; 1627}; 1628 1629static int 1630hx_pass_prompter(void *data, const hx509_prompt *prompter) 1631{ 1632 krb5_error_code ret; 1633 krb5_prompt prompt; 1634 krb5_data password_data; 1635 struct prompter *p = data; 1636 1637 password_data.data = prompter->reply.data; 1638 password_data.length = prompter->reply.length; 1639 1640 prompt.prompt = prompter->prompt; 1641 prompt.hidden = hx509_prompt_hidden(prompter->type); 1642 prompt.reply = &password_data; 1643 1644 switch (prompter->type) { 1645 case HX509_PROMPT_TYPE_INFO: 1646 prompt.type = KRB5_PROMPT_TYPE_INFO; 1647 break; 1648 case HX509_PROMPT_TYPE_PASSWORD: 1649 case HX509_PROMPT_TYPE_QUESTION: 1650 default: 1651 prompt.type = KRB5_PROMPT_TYPE_PASSWORD; 1652 break; 1653 } 1654 1655 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt); 1656 if (ret) { 1657 memset (prompter->reply.data, 0, prompter->reply.length); 1658 return 1; 1659 } 1660 return 0; 1661} 1662 1663static krb5_error_code 1664_krb5_pk_set_user_id(krb5_context context, 1665 krb5_principal principal, 1666 krb5_pk_init_ctx ctx, 1667 struct hx509_certs_data *certs) 1668{ 1669 hx509_certs c = hx509_certs_ref(certs); 1670 hx509_query *q = NULL; 1671 int ret; 1672 1673 if (ctx->id->certs) 1674 hx509_certs_free(&ctx->id->certs); 1675 if (ctx->id->cert) { 1676 hx509_cert_free(ctx->id->cert); 1677 ctx->id->cert = NULL; 1678 } 1679 1680 ctx->id->certs = c; 1681 ctx->anonymous = 0; 1682 1683 ret = hx509_query_alloc(context->hx509ctx, &q); 1684 if (ret) { 1685 pk_copy_error(context, context->hx509ctx, ret, 1686 "Allocate query to find signing certificate"); 1687 return ret; 1688 } 1689 1690 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 1691 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); 1692 1693 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) { 1694 ctx->id->flags |= PKINIT_BTMM; 1695 } 1696 1697 ret = find_cert(context, ctx->id, q, &ctx->id->cert); 1698 hx509_query_free(context->hx509ctx, q); 1699 1700 if (ret == 0 && _krb5_have_debug(context, 2)) { 1701 hx509_name name; 1702 char *str, *sn; 1703 heim_integer i; 1704 1705 ret = hx509_cert_get_subject(ctx->id->cert, &name); 1706 if (ret) 1707 goto out; 1708 1709 ret = hx509_name_to_string(name, &str); 1710 hx509_name_free(&name); 1711 if (ret) 1712 goto out; 1713 1714 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i); 1715 if (ret) { 1716 free(str); 1717 goto out; 1718 } 1719 1720 ret = der_print_hex_heim_integer(&i, &sn); 1721 der_free_heim_integer(&i); 1722 if (ret) { 1723 free(name); 1724 goto out; 1725 } 1726 1727 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn); 1728 free(str); 1729 free(sn); 1730 } 1731 out: 1732 1733 return ret; 1734} 1735 1736KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1737_krb5_pk_load_id(krb5_context context, 1738 struct krb5_pk_identity **ret_id, 1739 const char *user_id, 1740 const char *anchor_id, 1741 char * const *chain_list, 1742 char * const *revoke_list, 1743 krb5_prompter_fct prompter, 1744 void *prompter_data, 1745 char *password) 1746{ 1747 struct krb5_pk_identity *id = NULL; 1748 struct prompter p; 1749 int ret; 1750 1751 *ret_id = NULL; 1752 1753 if (anchor_id == NULL) { 1754 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA, 1755 N_("PKINIT: No anchor given", "")); 1756 return HEIM_PKINIT_NO_VALID_CA; 1757 } 1758 1759 /* load cert */ 1760 1761 id = calloc(1, sizeof(*id)); 1762 if (id == NULL) 1763 return krb5_enomem(context); 1764 1765 if (user_id) { 1766 hx509_lock lock; 1767 1768 ret = hx509_lock_init(context->hx509ctx, &lock); 1769 if (ret) { 1770 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock"); 1771 goto out; 1772 } 1773 1774 if (password && password[0]) 1775 hx509_lock_add_password(lock, password); 1776 1777 if (prompter) { 1778 p.context = context; 1779 p.prompter = prompter; 1780 p.prompter_data = prompter_data; 1781 1782 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); 1783 if (ret) { 1784 hx509_lock_free(lock); 1785 goto out; 1786 } 1787 } 1788 1789 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs); 1790 hx509_lock_free(lock); 1791 if (ret) { 1792 pk_copy_error(context, context->hx509ctx, ret, 1793 "Failed to init cert certs"); 1794 goto out; 1795 } 1796 } else { 1797 id->certs = NULL; 1798 } 1799 1800 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors); 1801 if (ret) { 1802 pk_copy_error(context, context->hx509ctx, ret, 1803 "Failed to init anchors"); 1804 goto out; 1805 } 1806 1807 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain", 1808 0, NULL, &id->certpool); 1809 if (ret) { 1810 pk_copy_error(context, context->hx509ctx, ret, 1811 "Failed to init chain"); 1812 goto out; 1813 } 1814 1815 while (chain_list && *chain_list) { 1816 ret = hx509_certs_append(context->hx509ctx, id->certpool, 1817 NULL, *chain_list); 1818 if (ret) { 1819 pk_copy_error(context, context->hx509ctx, ret, 1820 "Failed to laod chain %s", 1821 *chain_list); 1822 goto out; 1823 } 1824 chain_list++; 1825 } 1826 1827 if (revoke_list) { 1828 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx); 1829 if (ret) { 1830 pk_copy_error(context, context->hx509ctx, ret, 1831 "Failed init revoke list"); 1832 goto out; 1833 } 1834 1835 while (*revoke_list) { 1836 ret = hx509_revoke_add_crl(context->hx509ctx, 1837 id->revokectx, 1838 *revoke_list); 1839 if (ret) { 1840 pk_copy_error(context, context->hx509ctx, ret, 1841 "Failed load revoke list"); 1842 goto out; 1843 } 1844 revoke_list++; 1845 } 1846 } else 1847 hx509_context_set_missing_revoke(context->hx509ctx, 1); 1848 1849 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx); 1850 if (ret) { 1851 pk_copy_error(context, context->hx509ctx, ret, 1852 "Failed init verify context"); 1853 goto out; 1854 } 1855 1856 hx509_verify_attach_anchors(id->verify_ctx, id->anchors); 1857 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx); 1858 1859 out: 1860 if (ret) { 1861 hx509_verify_destroy_ctx(id->verify_ctx); 1862 hx509_certs_free(&id->certs); 1863 hx509_certs_free(&id->anchors); 1864 hx509_certs_free(&id->certpool); 1865 hx509_revoke_free(&id->revokectx); 1866 free(id); 1867 } else 1868 *ret_id = id; 1869 1870 return ret; 1871} 1872 1873/* 1874 * 1875 */ 1876 1877static void 1878pk_copy_error(krb5_context context, 1879 hx509_context hx509ctx, 1880 int hxret, 1881 const char *fmt, 1882 ...) 1883{ 1884 va_list va; 1885 char *s, *f; 1886 int ret; 1887 1888 va_start(va, fmt); 1889 ret = vasprintf(&f, fmt, va); 1890 va_end(va); 1891 if (ret == -1 || f == NULL) { 1892 krb5_clear_error_message(context); 1893 return; 1894 } 1895 1896 s = hx509_get_error_string(hx509ctx, hxret); 1897 if (s == NULL) { 1898 krb5_clear_error_message(context); 1899 free(f); 1900 return; 1901 } 1902 krb5_set_error_message(context, hxret, "%s: %s", f, s); 1903 free(s); 1904 free(f); 1905} 1906 1907static int 1908parse_integer(krb5_context context, char **p, const char *file, int lineno, 1909 const char *name, heim_integer *integer) 1910{ 1911 int ret; 1912 char *p1; 1913 p1 = strsep(p, " \t"); 1914 if (p1 == NULL) { 1915 krb5_set_error_message(context, EINVAL, 1916 N_("moduli file %s missing %s on line %d", ""), 1917 file, name, lineno); 1918 return EINVAL; 1919 } 1920 ret = der_parse_hex_heim_integer(p1, integer); 1921 if (ret) { 1922 krb5_set_error_message(context, ret, 1923 N_("moduli file %s failed parsing %s " 1924 "on line %d", ""), 1925 file, name, lineno); 1926 return ret; 1927 } 1928 1929 return 0; 1930} 1931 1932KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1933_krb5_parse_moduli_line(krb5_context context, 1934 const char *file, 1935 int lineno, 1936 char *p, 1937 struct krb5_dh_moduli **m) 1938{ 1939 struct krb5_dh_moduli *m1; 1940 char *p1; 1941 int ret; 1942 1943 *m = NULL; 1944 1945 m1 = calloc(1, sizeof(*m1)); 1946 if (m1 == NULL) 1947 return krb5_enomem(context); 1948 1949 while (isspace((unsigned char)*p)) 1950 p++; 1951 if (*p == '#') { 1952 free(m1); 1953 return 0; 1954 } 1955 ret = EINVAL; 1956 1957 p1 = strsep(&p, " \t"); 1958 if (p1 == NULL) { 1959 krb5_set_error_message(context, ret, 1960 N_("moduli file %s missing name on line %d", ""), 1961 file, lineno); 1962 goto out; 1963 } 1964 m1->name = strdup(p1); 1965 if (m1->name == NULL) { 1966 ret = krb5_enomem(context); 1967 goto out; 1968 } 1969 1970 p1 = strsep(&p, " \t"); 1971 if (p1 == NULL) { 1972 krb5_set_error_message(context, ret, 1973 N_("moduli file %s missing bits on line %d", ""), 1974 file, lineno); 1975 goto out; 1976 } 1977 1978 m1->bits = atoi(p1); 1979 if (m1->bits == 0) { 1980 krb5_set_error_message(context, ret, 1981 N_("moduli file %s have un-parsable " 1982 "bits on line %d", ""), file, lineno); 1983 goto out; 1984 } 1985 1986 ret = parse_integer(context, &p, file, lineno, "p", &m1->p); 1987 if (ret) 1988 goto out; 1989 ret = parse_integer(context, &p, file, lineno, "g", &m1->g); 1990 if (ret) 1991 goto out; 1992 ret = parse_integer(context, &p, file, lineno, "q", &m1->q); 1993 if (ret) 1994 goto out; 1995 1996 *m = m1; 1997 1998 return 0; 1999 out: 2000 free(m1->name); 2001 der_free_heim_integer(&m1->p); 2002 der_free_heim_integer(&m1->g); 2003 der_free_heim_integer(&m1->q); 2004 free(m1); 2005 return ret; 2006} 2007 2008KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2009_krb5_free_moduli(struct krb5_dh_moduli **moduli) 2010{ 2011 int i; 2012 for (i = 0; moduli[i] != NULL; i++) { 2013 free(moduli[i]->name); 2014 der_free_heim_integer(&moduli[i]->p); 2015 der_free_heim_integer(&moduli[i]->g); 2016 der_free_heim_integer(&moduli[i]->q); 2017 free(moduli[i]); 2018 } 2019 free(moduli); 2020} 2021 2022static const char *default_moduli_RFC2412_MODP_group2 = 2023 /* name */ 2024 "RFC2412-MODP-group2 " 2025 /* bits */ 2026 "1024 " 2027 /* p */ 2028 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" 2029 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" 2030 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" 2031 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" 2032 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" 2033 "FFFFFFFF" "FFFFFFFF " 2034 /* g */ 2035 "02 " 2036 /* q */ 2037 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68" 2038 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E" 2039 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122" 2040 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6" 2041 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0" 2042 "FFFFFFFF" "FFFFFFFF"; 2043 2044static const char *default_moduli_rfc3526_MODP_group14 = 2045 /* name */ 2046 "rfc3526-MODP-group14 " 2047 /* bits */ 2048 "1760 " 2049 /* p */ 2050 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" 2051 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" 2052 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" 2053 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" 2054 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" 2055 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" 2056 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" 2057 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" 2058 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" 2059 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" 2060 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF " 2061 /* g */ 2062 "02 " 2063 /* q */ 2064 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68" 2065 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E" 2066 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122" 2067 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6" 2068 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E" 2069 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF" 2070 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36" 2071 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D" 2072 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964" 2073 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288" 2074 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF"; 2075 2076KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2077_krb5_parse_moduli(krb5_context context, const char *file, 2078 struct krb5_dh_moduli ***moduli) 2079{ 2080 /* name bits P G Q */ 2081 krb5_error_code ret; 2082 struct krb5_dh_moduli **m = NULL, **m2; 2083 char buf[4096]; 2084 FILE *f; 2085 int lineno = 0, n = 0; 2086 2087 *moduli = NULL; 2088 2089 m = calloc(1, sizeof(m[0]) * 3); 2090 if (m == NULL) 2091 return krb5_enomem(context); 2092 2093 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf)); 2094 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]); 2095 if (ret) { 2096 _krb5_free_moduli(m); 2097 return ret; 2098 } 2099 n++; 2100 2101 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf)); 2102 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]); 2103 if (ret) { 2104 _krb5_free_moduli(m); 2105 return ret; 2106 } 2107 n++; 2108 2109 2110 if (file == NULL) 2111 file = MODULI_FILE; 2112 2113#ifdef KRB5_USE_PATH_TOKENS 2114 { 2115 char * exp_file; 2116 2117 if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) { 2118 f = fopen(exp_file, "r"); 2119 krb5_xfree(exp_file); 2120 } else { 2121 f = NULL; 2122 } 2123 } 2124#else 2125 f = fopen(file, "r"); 2126#endif 2127 2128 if (f == NULL) { 2129 *moduli = m; 2130 return 0; 2131 } 2132 rk_cloexec_file(f); 2133 2134 while(fgets(buf, sizeof(buf), f) != NULL) { 2135 struct krb5_dh_moduli *element; 2136 2137 buf[strcspn(buf, "\n")] = '\0'; 2138 lineno++; 2139 2140 m2 = realloc(m, (n + 2) * sizeof(m[0])); 2141 if (m2 == NULL) { 2142 _krb5_free_moduli(m); 2143 return krb5_enomem(context); 2144 } 2145 m = m2; 2146 2147 m[n] = NULL; 2148 2149 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element); 2150 if (ret) { 2151 _krb5_free_moduli(m); 2152 return ret; 2153 } 2154 if (element == NULL) 2155 continue; 2156 2157 m[n] = element; 2158 m[n + 1] = NULL; 2159 n++; 2160 } 2161 *moduli = m; 2162 return 0; 2163} 2164 2165KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2166_krb5_dh_group_ok(krb5_context context, unsigned long bits, 2167 heim_integer *p, heim_integer *g, heim_integer *q, 2168 struct krb5_dh_moduli **moduli, 2169 char **name) 2170{ 2171 int i; 2172 2173 if (name) 2174 *name = NULL; 2175 2176 for (i = 0; moduli[i] != NULL; i++) { 2177 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 && 2178 der_heim_integer_cmp(&moduli[i]->p, p) == 0 && 2179 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0)) 2180 { 2181 if (bits && bits > moduli[i]->bits) { 2182 krb5_set_error_message(context, 2183 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, 2184 N_("PKINIT: DH group parameter %s " 2185 "no accepted, not enough bits " 2186 "generated", ""), 2187 moduli[i]->name); 2188 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; 2189 } 2190 if (name) 2191 *name = strdup(moduli[i]->name); 2192 return 0; 2193 } 2194 } 2195 krb5_set_error_message(context, 2196 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, 2197 N_("PKINIT: DH group parameter no ok", "")); 2198 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; 2199} 2200#endif /* PKINIT */ 2201 2202KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2203_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt) 2204{ 2205#ifdef PKINIT 2206 krb5_pk_init_ctx ctx; 2207 2208 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL) 2209 return; 2210 ctx = opt->opt_private->pk_init_ctx; 2211 switch (ctx->keyex) { 2212 case USE_DH: 2213 if (ctx->u.dh) 2214 DH_free(ctx->u.dh); 2215 break; 2216 case USE_RSA: 2217 break; 2218 case USE_ECDH: 2219 if (ctx->u.eckey) 2220 _krb5_pk_eckey_free(ctx->u.eckey); 2221 break; 2222 } 2223 if (ctx->id) { 2224 hx509_verify_destroy_ctx(ctx->id->verify_ctx); 2225 hx509_certs_free(&ctx->id->certs); 2226 hx509_cert_free(ctx->id->cert); 2227 hx509_certs_free(&ctx->id->anchors); 2228 hx509_certs_free(&ctx->id->certpool); 2229 2230 if (ctx->clientDHNonce) { 2231 krb5_free_data(NULL, ctx->clientDHNonce); 2232 ctx->clientDHNonce = NULL; 2233 } 2234 if (ctx->m) 2235 _krb5_free_moduli(ctx->m); 2236 free(ctx->id); 2237 ctx->id = NULL; 2238 } 2239 free(opt->opt_private->pk_init_ctx); 2240 opt->opt_private->pk_init_ctx = NULL; 2241#endif 2242} 2243 2244KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2245krb5_get_init_creds_opt_set_pkinit(krb5_context context, 2246 krb5_get_init_creds_opt *opt, 2247 krb5_principal principal, 2248 const char *user_id, 2249 const char *x509_anchors, 2250 char * const * pool, 2251 char * const * pki_revoke, 2252 int flags, 2253 krb5_prompter_fct prompter, 2254 void *prompter_data, 2255 char *password) 2256{ 2257#ifdef PKINIT 2258 krb5_error_code ret; 2259 char *anchors = NULL; 2260 2261 if (opt->opt_private == NULL) { 2262 krb5_set_error_message(context, EINVAL, 2263 N_("PKINIT: on non extendable opt", "")); 2264 return EINVAL; 2265 } 2266 2267 opt->opt_private->pk_init_ctx = 2268 calloc(1, sizeof(*opt->opt_private->pk_init_ctx)); 2269 if (opt->opt_private->pk_init_ctx == NULL) 2270 return krb5_enomem(context); 2271 opt->opt_private->pk_init_ctx->require_binding = 0; 2272 opt->opt_private->pk_init_ctx->require_eku = 1; 2273 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1; 2274 opt->opt_private->pk_init_ctx->peer = NULL; 2275 2276 /* XXX implement krb5_appdefault_strings */ 2277 if (pool == NULL) 2278 pool = krb5_config_get_strings(context, NULL, 2279 "appdefaults", 2280 "pkinit_pool", 2281 NULL); 2282 2283 if (pki_revoke == NULL) 2284 pki_revoke = krb5_config_get_strings(context, NULL, 2285 "appdefaults", 2286 "pkinit_revoke", 2287 NULL); 2288 2289 if (x509_anchors == NULL) { 2290 krb5_appdefault_string(context, "kinit", 2291 krb5_principal_get_realm(context, principal), 2292 "pkinit_anchors", NULL, &anchors); 2293 x509_anchors = anchors; 2294 } 2295 2296 if (flags & 4) 2297 opt->opt_private->pk_init_ctx->anonymous = 1; 2298 2299 ret = _krb5_pk_load_id(context, 2300 &opt->opt_private->pk_init_ctx->id, 2301 user_id, 2302 x509_anchors, 2303 pool, 2304 pki_revoke, 2305 prompter, 2306 prompter_data, 2307 password); 2308 if (ret) { 2309 free(opt->opt_private->pk_init_ctx); 2310 opt->opt_private->pk_init_ctx = NULL; 2311 return ret; 2312 } 2313 2314 if (opt->opt_private->pk_init_ctx->id->certs) { 2315 _krb5_pk_set_user_id(context, 2316 principal, 2317 opt->opt_private->pk_init_ctx, 2318 opt->opt_private->pk_init_ctx->id->certs); 2319 } else 2320 opt->opt_private->pk_init_ctx->id->cert = NULL; 2321 2322 if ((flags & 2) == 0) { 2323 hx509_context hx509ctx = context->hx509ctx; 2324 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert; 2325 2326 opt->opt_private->pk_init_ctx->keyex = USE_DH; 2327 2328 /* 2329 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm. 2330 */ 2331 if (cert) { 2332 AlgorithmIdentifier alg; 2333 2334 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg); 2335 if (ret == 0) { 2336 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0) 2337 opt->opt_private->pk_init_ctx->keyex = USE_ECDH; 2338 free_AlgorithmIdentifier(&alg); 2339 } 2340 } 2341 2342 } else { 2343 opt->opt_private->pk_init_ctx->keyex = USE_RSA; 2344 2345 if (opt->opt_private->pk_init_ctx->id->certs == NULL) { 2346 krb5_set_error_message(context, EINVAL, 2347 N_("No anonymous pkinit support in RSA mode", "")); 2348 return EINVAL; 2349 } 2350 } 2351 2352 return 0; 2353#else 2354 krb5_set_error_message(context, EINVAL, 2355 N_("no support for PKINIT compiled in", "")); 2356 return EINVAL; 2357#endif 2358} 2359 2360krb5_error_code KRB5_LIB_FUNCTION 2361krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context, 2362 krb5_get_init_creds_opt *opt, 2363 struct hx509_certs_data *certs) 2364{ 2365#ifdef PKINIT 2366 if (opt->opt_private == NULL) { 2367 krb5_set_error_message(context, EINVAL, 2368 N_("PKINIT: on non extendable opt", "")); 2369 return EINVAL; 2370 } 2371 if (opt->opt_private->pk_init_ctx == NULL) { 2372 krb5_set_error_message(context, EINVAL, 2373 N_("PKINIT: on pkinit context", "")); 2374 return EINVAL; 2375 } 2376 2377 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs); 2378 2379 return 0; 2380#else 2381 krb5_set_error_message(context, EINVAL, 2382 N_("no support for PKINIT compiled in", "")); 2383 return EINVAL; 2384#endif 2385} 2386 2387#ifdef PKINIT 2388 2389static int 2390get_ms_san(hx509_context context, hx509_cert cert, char **upn) 2391{ 2392 hx509_octet_string_list list; 2393 int ret; 2394 2395 *upn = NULL; 2396 2397 ret = hx509_cert_find_subjectAltName_otherName(context, 2398 cert, 2399 &asn1_oid_id_pkinit_ms_san, 2400 &list); 2401 if (ret) 2402 return 0; 2403 2404 if (list.len > 0 && list.val[0].length > 0) 2405 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, 2406 upn, NULL); 2407 else 2408 ret = 1; 2409 hx509_free_octet_string_list(&list); 2410 2411 return ret; 2412} 2413 2414static int 2415find_ms_san(hx509_context context, hx509_cert cert, void *ctx) 2416{ 2417 char *upn; 2418 int ret; 2419 2420 ret = get_ms_san(context, cert, &upn); 2421 if (ret == 0) 2422 free(upn); 2423 return ret; 2424} 2425 2426 2427 2428#endif 2429 2430/* 2431 * Private since it need to be redesigned using krb5_get_init_creds() 2432 */ 2433 2434KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2435krb5_pk_enterprise_cert(krb5_context context, 2436 const char *user_id, 2437 krb5_const_realm realm, 2438 krb5_principal *principal, 2439 struct hx509_certs_data **res) 2440{ 2441#ifdef PKINIT 2442 krb5_error_code ret; 2443 hx509_certs certs, result; 2444 hx509_cert cert = NULL; 2445 hx509_query *q; 2446 char *name; 2447 2448 *principal = NULL; 2449 if (res) 2450 *res = NULL; 2451 2452 if (user_id == NULL) { 2453 krb5_set_error_message(context, ENOENT, "no user id"); 2454 return ENOENT; 2455 } 2456 2457 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs); 2458 if (ret) { 2459 pk_copy_error(context, context->hx509ctx, ret, 2460 "Failed to init cert certs"); 2461 goto out; 2462 } 2463 2464 ret = hx509_query_alloc(context->hx509ctx, &q); 2465 if (ret) { 2466 krb5_set_error_message(context, ret, "out of memory"); 2467 hx509_certs_free(&certs); 2468 goto out; 2469 } 2470 2471 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); 2472 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); 2473 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku); 2474 hx509_query_match_cmp_func(q, find_ms_san, NULL); 2475 2476 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result); 2477 hx509_query_free(context->hx509ctx, q); 2478 hx509_certs_free(&certs); 2479 if (ret) { 2480 pk_copy_error(context, context->hx509ctx, ret, 2481 "Failed to find PKINIT certificate"); 2482 return ret; 2483 } 2484 2485 ret = hx509_get_one_cert(context->hx509ctx, result, &cert); 2486 hx509_certs_free(&result); 2487 if (ret) { 2488 pk_copy_error(context, context->hx509ctx, ret, 2489 "Failed to get one cert"); 2490 goto out; 2491 } 2492 2493 ret = get_ms_san(context->hx509ctx, cert, &name); 2494 if (ret) { 2495 pk_copy_error(context, context->hx509ctx, ret, 2496 "Failed to get MS SAN"); 2497 goto out; 2498 } 2499 2500 ret = krb5_make_principal(context, principal, realm, name, NULL); 2501 free(name); 2502 if (ret) 2503 goto out; 2504 2505 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL); 2506 2507 if (res) { 2508 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res); 2509 if (ret) 2510 goto out; 2511 2512 ret = hx509_certs_add(context->hx509ctx, *res, cert); 2513 if (ret) { 2514 hx509_certs_free(res); 2515 goto out; 2516 } 2517 } 2518 2519 out: 2520 hx509_cert_free(cert); 2521 2522 return ret; 2523#else 2524 krb5_set_error_message(context, EINVAL, 2525 N_("no support for PKINIT compiled in", "")); 2526 return EINVAL; 2527#endif 2528} 2529