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