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