2 * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
36 RCSID("$Id: pkinit.c,v 1.110 2006/10/14 09:52:50 lha Exp $");
38 struct krb5_dh_moduli {
48 #include <heim_asn1.h>
49 #include <rfc2459_asn1.h>
51 #include <pkcs8_asn1.h>
52 #include <pkcs9_asn1.h>
53 #include <pkcs12_asn1.h>
54 #include <pkinit_asn1.h>
66 struct krb5_pk_identity {
67 hx509_context hx509ctx;
68 hx509_verify_ctx verify_ctx;
72 hx509_revoke_ctx revokectx;
79 struct krb5_pk_init_ctx_data {
80 struct krb5_pk_identity *id;
82 krb5_data *clientDHNonce;
83 struct krb5_dh_moduli **m;
86 int require_krbtgt_otherName;
87 int require_hostname_match;
90 void KRB5_LIB_FUNCTION
91 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
94 hx509_cert_free(cert->cert);
99 static krb5_error_code
100 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
102 integer->length = BN_num_bytes(bn);
103 integer->data = malloc(integer->length);
104 if (integer->data == NULL) {
105 krb5_clear_error_string(context);
108 BN_bn2bin(bn, integer->data);
109 integer->negative = BN_is_negative(bn);
114 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
118 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
120 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
123 BN_set_negative(bn, f->negative);
128 static krb5_error_code
129 _krb5_pk_create_sign(krb5_context context,
130 const heim_oid *eContentType,
132 struct krb5_pk_identity *id,
139 ret = hx509_query_alloc(id->hx509ctx, &q);
143 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
144 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
146 ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
147 hx509_query_free(id->hx509ctx, q);
151 ret = hx509_cms_create_signed_1(id->hx509ctx,
160 hx509_cert_free(cert);
166 cert2epi(hx509_context context, void *ctx, hx509_cert c)
168 ExternalPrincipalIdentifiers *ids = ctx;
169 ExternalPrincipalIdentifier id;
170 hx509_name subject = NULL;
174 memset(&id, 0, sizeof(id));
176 ret = hx509_cert_get_subject(c, &subject);
180 if (hx509_name_is_null_p(subject) != 0) {
182 id.subjectName = calloc(1, sizeof(*id.subjectName));
183 if (id.subjectName == NULL) {
184 hx509_name_free(&subject);
185 free_ExternalPrincipalIdentifier(&id);
189 ret = hx509_name_to_der_name(subject, &id.subjectName->data,
190 &id.subjectName->length);
192 hx509_name_free(&subject);
193 free_ExternalPrincipalIdentifier(&id);
197 hx509_name_free(&subject);
200 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
201 if (id.issuerAndSerialNumber == NULL) {
202 free_ExternalPrincipalIdentifier(&id);
207 IssuerAndSerialNumber iasn;
211 memset(&iasn, 0, sizeof(iasn));
213 ret = hx509_cert_get_issuer(c, &issuer);
215 free_ExternalPrincipalIdentifier(&id);
219 ret = hx509_name_to_Name(issuer, &iasn.issuer);
220 hx509_name_free(&issuer);
222 free_ExternalPrincipalIdentifier(&id);
226 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
228 free_IssuerAndSerialNumber(&iasn);
229 free_ExternalPrincipalIdentifier(&id);
233 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
234 id.issuerAndSerialNumber->data,
235 id.issuerAndSerialNumber->length,
237 free_IssuerAndSerialNumber(&iasn);
240 if (id.issuerAndSerialNumber->length != size)
244 id.subjectKeyIdentifier = NULL;
246 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
248 free_ExternalPrincipalIdentifier(&id);
253 ids->val[ids->len] = id;
259 static krb5_error_code
260 build_edi(krb5_context context,
261 hx509_context hx509ctx,
263 ExternalPrincipalIdentifiers *ids)
265 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
268 static krb5_error_code
269 build_auth_pack(krb5_context context,
271 krb5_pk_init_ctx ctx,
273 const KDC_REQ_BODY *body,
276 size_t buf_size, len;
283 krb5_clear_error_string(context);
285 memset(&checksum, 0, sizeof(checksum));
287 krb5_us_timeofday(context, &sec, &usec);
288 a->pkAuthenticator.ctime = sec;
289 a->pkAuthenticator.nonce = nonce;
291 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
295 krb5_abortx(context, "internal error in ASN.1 encoder");
297 ret = krb5_create_checksum(context,
308 ALLOC(a->pkAuthenticator.paChecksum, 1);
309 if (a->pkAuthenticator.paChecksum == NULL) {
310 krb5_set_error_string(context, "malloc: out of memory");
314 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
315 checksum.checksum.data, checksum.checksum.length);
316 free_Checksum(&checksum);
322 heim_integer dh_pub_key;
326 if (1 /* support_cached_dh */) {
327 ALLOC(a->clientDHNonce, 1);
328 if (a->clientDHNonce == NULL) {
329 krb5_clear_error_string(context);
332 ret = krb5_data_alloc(a->clientDHNonce, 40);
333 if (a->clientDHNonce == NULL) {
334 krb5_clear_error_string(context);
337 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
338 ret = krb5_copy_data(context, a->clientDHNonce,
339 &ctx->clientDHNonce);
344 ALLOC(a->clientPublicValue, 1);
345 if (a->clientPublicValue == NULL)
347 ret = der_copy_oid(oid_id_dhpublicnumber(),
348 &a->clientPublicValue->algorithm.algorithm);
352 memset(&dp, 0, sizeof(dp));
354 ret = BN_to_integer(context, dh->p, &dp.p);
356 free_DomainParameters(&dp);
359 ret = BN_to_integer(context, dh->g, &dp.g);
361 free_DomainParameters(&dp);
364 ret = BN_to_integer(context, dh->q, &dp.q);
366 free_DomainParameters(&dp);
370 dp.validationParms = NULL;
372 a->clientPublicValue->algorithm.parameters =
373 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
374 if (a->clientPublicValue->algorithm.parameters == NULL) {
375 free_DomainParameters(&dp);
379 ASN1_MALLOC_ENCODE(DomainParameters,
380 a->clientPublicValue->algorithm.parameters->data,
381 a->clientPublicValue->algorithm.parameters->length,
383 free_DomainParameters(&dp);
386 if (size != a->clientPublicValue->algorithm.parameters->length)
387 krb5_abortx(context, "Internal ASN1 encoder error");
389 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
393 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
394 &dh_pub_key, &size, ret);
395 der_free_heim_integer(&dh_pub_key);
398 if (size != dhbuf.length)
399 krb5_abortx(context, "asn1 internal error");
401 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
402 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
408 krb5_error_code KRB5_LIB_FUNCTION
409 _krb5_pk_mk_ContentInfo(krb5_context context,
410 const krb5_data *buf,
412 struct ContentInfo *content_info)
416 ret = der_copy_oid(oid, &content_info->contentType);
419 ALLOC(content_info->content, 1);
420 if (content_info->content == NULL)
422 content_info->content->data = malloc(buf->length);
423 if (content_info->content->data == NULL)
425 memcpy(content_info->content->data, buf->data, buf->length);
426 content_info->content->length = buf->length;
430 static krb5_error_code
431 pk_mk_padata(krb5_context context,
433 krb5_pk_init_ctx ctx,
434 const KDC_REQ_BODY *req_body,
438 struct ContentInfo content_info;
442 krb5_data buf, sd_buf;
445 krb5_data_zero(&buf);
446 krb5_data_zero(&sd_buf);
447 memset(&content_info, 0, sizeof(content_info));
449 if (compat == COMPAT_WIN2K) {
454 memset(&ap, 0, sizeof(ap));
456 /* fill in PKAuthenticator */
457 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
459 free_AuthPack_Win2k(&ap);
460 krb5_clear_error_string(context);
463 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
465 free_AuthPack_Win2k(&ap);
466 krb5_clear_error_string(context);
470 krb5_us_timeofday(context, &sec, &usec);
471 ap.pkAuthenticator.ctime = sec;
472 ap.pkAuthenticator.cusec = usec;
473 ap.pkAuthenticator.nonce = nonce;
475 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
477 free_AuthPack_Win2k(&ap);
479 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
482 if (buf.length != size)
483 krb5_abortx(context, "internal ASN1 encoder error");
485 oid = oid_id_pkcs7_data();
486 } else if (compat == COMPAT_IETF) {
489 memset(&ap, 0, sizeof(ap));
491 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
497 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
500 krb5_set_error_string(context, "AuthPack: %d", ret);
503 if (buf.length != size)
504 krb5_abortx(context, "internal ASN1 encoder error");
506 oid = oid_id_pkauthdata();
508 krb5_abortx(context, "internal pkinit error");
510 ret = _krb5_pk_create_sign(context,
515 krb5_data_free(&buf);
519 ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, oid_id_pkcs7_signedData(),
521 krb5_data_free(&sd_buf);
525 ASN1_MALLOC_ENCODE(ContentInfo, buf.data, buf.length,
526 &content_info, &size, ret);
529 if (buf.length != size)
530 krb5_abortx(context, "Internal ASN1 encoder error");
532 if (compat == COMPAT_WIN2K) {
533 PA_PK_AS_REQ_Win2k winreq;
535 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
537 memset(&winreq, 0, sizeof(winreq));
539 winreq.signed_auth_pack = buf;
541 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
542 &winreq, &size, ret);
543 free_PA_PK_AS_REQ_Win2k(&winreq);
545 } else if (compat == COMPAT_IETF) {
548 pa_type = KRB5_PADATA_PK_AS_REQ;
550 memset(&req, 0, sizeof(req));
551 req.signedAuthPack = buf;
553 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
554 if (req.trustedCertifiers == NULL) {
555 krb5_set_error_string(context, "malloc: out of memory");
556 free_PA_PK_AS_REQ(&req);
559 ret = build_edi(context, ctx->id->hx509ctx,
560 ctx->id->anchors, req.trustedCertifiers);
562 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
563 free_PA_PK_AS_REQ(&req);
568 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
571 free_PA_PK_AS_REQ(&req);
574 krb5_abortx(context, "internal pkinit error");
576 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
579 if (buf.length != size)
580 krb5_abortx(context, "Internal ASN1 encoder error");
582 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
586 if (ret == 0 && compat == COMPAT_WIN2K)
587 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
590 free_ContentInfo(&content_info);
596 krb5_error_code KRB5_LIB_FUNCTION
597 _krb5_pk_mk_padata(krb5_context context,
599 const KDC_REQ_BODY *req_body,
603 krb5_pk_init_ctx ctx = c;
604 int win2k_compat, type;
606 win2k_compat = krb5_config_get_bool_default(context, NULL,
612 if (context->pkinit_flags & KRB5_PKINIT_WIN2K)
616 ctx->require_binding =
617 krb5_config_get_bool_default(context, NULL,
621 "win2k_pkinit_require_binding",
628 krb5_config_get_bool_default(context, NULL,
632 "pkinit_require_eku",
634 ctx->require_krbtgt_otherName =
635 krb5_config_get_bool_default(context, NULL,
639 "pkinit_require_krbtgt_otherName",
642 ctx->require_hostname_match =
643 krb5_config_get_bool_default(context, NULL,
647 "pkinit_require_hostname_match",
650 return pk_mk_padata(context, type, ctx, req_body, nonce, md);
653 krb5_error_code KRB5_LIB_FUNCTION
654 _krb5_pk_verify_sign(krb5_context context,
657 struct krb5_pk_identity *id,
658 heim_oid *contentType,
660 struct krb5_pk_cert **signer)
662 hx509_certs signer_certs;
667 ret = hx509_cms_verify_signed(id->hx509ctx,
676 char *s = hx509_get_error_string(id->hx509ctx, ret);
678 krb5_set_error_string(context,
679 "CMS verify signed failed with %s", s);
682 krb5_clear_error_string(context);
686 *signer = calloc(1, sizeof(**signer));
687 if (*signer == NULL) {
688 krb5_clear_error_string(context);
693 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
695 krb5_clear_error_string(context);
700 hx509_certs_free(&signer_certs);
703 hx509_cert_free((*signer)->cert);
712 static krb5_error_code
713 get_reply_key_win(krb5_context context,
714 const krb5_data *content,
718 ReplyKeyPack_Win2k key_pack;
722 ret = decode_ReplyKeyPack_Win2k(content->data,
727 krb5_set_error_string(context, "PKINIT decoding reply key failed");
728 free_ReplyKeyPack_Win2k(&key_pack);
732 if (key_pack.nonce != nonce) {
733 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
734 free_ReplyKeyPack_Win2k(&key_pack);
735 return KRB5KRB_AP_ERR_MODIFIED;
738 *key = malloc (sizeof (**key));
740 krb5_set_error_string(context, "PKINIT failed allocating reply key");
741 free_ReplyKeyPack_Win2k(&key_pack);
742 krb5_set_error_string(context, "malloc: out of memory");
746 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
747 free_ReplyKeyPack_Win2k(&key_pack);
749 krb5_set_error_string(context, "PKINIT failed copying reply key");
756 static krb5_error_code
757 get_reply_key(krb5_context context,
758 const krb5_data *content,
759 const krb5_data *req_buffer,
762 ReplyKeyPack key_pack;
766 ret = decode_ReplyKeyPack(content->data,
771 krb5_set_error_string(context, "PKINIT decoding reply key failed");
772 free_ReplyKeyPack(&key_pack);
780 * XXX Verify kp.replyKey is a allowed enctype in the
784 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
786 free_ReplyKeyPack(&key_pack);
790 ret = krb5_verify_checksum(context, crypto, 6,
791 req_buffer->data, req_buffer->length,
792 &key_pack.asChecksum);
793 krb5_crypto_destroy(context, crypto);
795 free_ReplyKeyPack(&key_pack);
800 *key = malloc (sizeof (**key));
802 krb5_set_error_string(context, "PKINIT failed allocating reply key");
803 free_ReplyKeyPack(&key_pack);
804 krb5_set_error_string(context, "malloc: out of memory");
808 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
809 free_ReplyKeyPack(&key_pack);
811 krb5_set_error_string(context, "PKINIT failed copying reply key");
819 static krb5_error_code
820 pk_verify_host(krb5_context context,
822 const krb5_krbhst_info *hi,
823 struct krb5_pk_init_ctx_data *ctx,
824 struct krb5_pk_cert *host)
826 krb5_error_code ret = 0;
828 if (ctx->require_eku) {
829 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
830 oid_id_pkkdcekuoid(), 0);
832 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
836 if (ctx->require_krbtgt_otherName) {
837 hx509_octet_string_list list;
840 ret = hx509_cert_find_subjectAltName_otherName(host->cert,
844 krb5_set_error_string(context, "Failed to find the PK-INIT "
845 "subjectAltName in the KDC certificate");
850 for (i = 0; i < list.len; i++) {
853 ret = decode_KRB5PrincipalName(list.val[i].data,
858 krb5_set_error_string(context, "Failed to decode the PK-INIT "
859 "subjectAltName in the KDC certificate");
864 if (r.principalName.name_string.len != 2 ||
865 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
866 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
867 strcmp(r.realm, realm) != 0)
869 krb5_set_error_string(context, "KDC have wrong realm name in "
871 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
874 free_KRB5PrincipalName(&r);
878 hx509_free_octet_string_list(&list);
884 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
885 ctx->require_hostname_match,
887 hi->ai->ai_addr, hi->ai->ai_addrlen);
890 krb5_set_error_string(context, "Address mismatch in "
891 "the KDC certificate");
896 static krb5_error_code
897 pk_rd_pa_reply_enckey(krb5_context context,
899 const ContentInfo *rep,
901 krb5_pk_init_ctx ctx,
903 const krb5_krbhst_info *hi,
905 const krb5_data *req_buffer,
910 struct krb5_pk_cert *host = NULL;
915 heim_oid contentType = { 0, NULL };
917 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), &rep->contentType)) {
918 krb5_set_error_string(context, "PKINIT: Invalid content type");
922 if (rep->content == NULL) {
923 krb5_set_error_string(context, "PKINIT: No content in reply");
927 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
929 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
931 rep->content->length,
939 length = content.length;
941 /* win2k uses ContentInfo */
942 if (type == COMPAT_WIN2K) {
945 ret = decode_ContentInfo(p, length, &ci, &size);
947 krb5_set_error_string(context,
948 "PKINIT: failed decoding ContentInfo: %d",
953 if (der_heim_oid_cmp(&ci.contentType, oid_id_pkcs7_signedData())) {
954 ret = EINVAL; /* XXX */
955 krb5_set_error_string(context, "PKINIT: Invalid content type");
958 if (ci.content == NULL) {
959 ret = EINVAL; /* XXX */
960 krb5_set_error_string(context, "PKINIT: Invalid content type");
963 krb5_data_free(&content);
964 content = *ci.content;
965 p = ci.content->data;
966 length = ci.content->length;
969 ret = _krb5_pk_verify_sign(context,
979 /* make sure that it is the kdc's certificate */
980 ret = pk_verify_host(context, realm, hi, ctx, host);
986 if (type == COMPAT_WIN2K) {
987 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
988 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
989 ret = KRB5KRB_AP_ERR_MSG_TYPE;
993 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
994 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
995 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1003 ret = get_reply_key(context, &content, req_buffer, key);
1004 if (ret != 0 && ctx->require_binding == 0)
1005 ret = get_reply_key_win(context, &content, nonce, key);
1008 ret = get_reply_key(context, &content, req_buffer, key);
1014 /* XXX compare given etype with key->etype */
1018 _krb5_pk_cert_free(host);
1019 der_free_oid(&contentType);
1020 krb5_data_free(&content);
1025 static krb5_error_code
1026 pk_rd_pa_reply_dh(krb5_context context,
1027 const ContentInfo *rep,
1029 krb5_pk_init_ctx ctx,
1031 const krb5_krbhst_info *hi,
1036 krb5_keyblock **key)
1038 unsigned char *p, *dh_gen_key = NULL;
1039 struct krb5_pk_cert *host = NULL;
1040 BIGNUM *kdc_dh_pubkey = NULL;
1041 KDCDHKeyInfo kdc_dh_info;
1042 heim_oid contentType = { 0, NULL };
1044 krb5_error_code ret;
1048 krb5_data_zero(&content);
1049 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1051 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), &rep->contentType)) {
1052 krb5_set_error_string(context, "PKINIT: Invalid content type");
1056 if (rep->content == NULL) {
1057 krb5_set_error_string(context, "PKINIT: No content in reply");
1061 ret = _krb5_pk_verify_sign(context,
1063 rep->content->length,
1071 /* make sure that it is the kdc's certificate */
1072 ret = pk_verify_host(context, realm, hi, ctx, host);
1076 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1077 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1078 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1082 ret = decode_KDCDHKeyInfo(content.data,
1090 if (kdc_dh_info.nonce != nonce) {
1091 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1092 ret = KRB5KRB_AP_ERR_MODIFIED;
1096 if (kdc_dh_info.dhKeyExpiration) {
1098 krb5_set_error_string(context, "pkinit; got key expiration "
1099 "without server nonce");
1100 ret = KRB5KRB_ERR_GENERIC;
1104 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1106 ret = KRB5KRB_ERR_GENERIC;
1111 krb5_set_error_string(context, "pkinit: got server nonce "
1112 "without key expiration");
1113 ret = KRB5KRB_ERR_GENERIC;
1120 p = kdc_dh_info.subjectPublicKey.data;
1121 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1125 ret = decode_DHPublicKey(p, size, &k, NULL);
1127 krb5_set_error_string(context, "pkinit: can't decode "
1128 "without key expiration");
1132 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1133 free_DHPublicKey(&k);
1134 if (kdc_dh_pubkey == NULL) {
1135 ret = KRB5KRB_ERR_GENERIC;
1140 dh_gen_keylen = DH_size(ctx->dh);
1141 size = BN_num_bytes(ctx->dh->p);
1142 if (size < dh_gen_keylen)
1143 size = dh_gen_keylen;
1145 dh_gen_key = malloc(size);
1146 if (dh_gen_key == NULL) {
1147 krb5_set_error_string(context, "malloc: out of memory");
1151 memset(dh_gen_key, 0, size - dh_gen_keylen);
1153 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1154 kdc_dh_pubkey, ctx->dh);
1155 if (dh_gen_keylen == -1) {
1156 krb5_set_error_string(context,
1157 "PKINIT: Can't compute Diffie-Hellman key");
1158 ret = KRB5KRB_ERR_GENERIC;
1162 *key = malloc (sizeof (**key));
1164 krb5_set_error_string(context, "malloc: out of memory");
1169 ret = _krb5_pk_octetstring2key(context,
1171 dh_gen_key, dh_gen_keylen,
1175 krb5_set_error_string(context,
1176 "PKINIT: can't create key from DH key");
1184 BN_free(kdc_dh_pubkey);
1186 memset(dh_gen_key, 0, DH_size(ctx->dh));
1190 _krb5_pk_cert_free(host);
1192 krb5_data_free(&content);
1193 free_KDCDHKeyInfo(&kdc_dh_info);
1198 krb5_error_code KRB5_LIB_FUNCTION
1199 _krb5_pk_rd_pa_reply(krb5_context context,
1203 const krb5_krbhst_info *hi,
1205 const krb5_data *req_buffer,
1207 krb5_keyblock **key)
1209 krb5_pk_init_ctx ctx = c;
1210 krb5_error_code ret;
1214 /* Check for IETF PK-INIT first */
1215 if (pa->padata_type == KRB5_PADATA_PK_AS_REP) {
1218 memset(&rep, 0, sizeof(rep));
1220 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1221 pa->padata_value.length,
1227 switch (rep.element) {
1228 case choice_PA_PK_AS_REP_dhInfo:
1229 ret = decode_ContentInfo(rep.u.dhInfo.dhSignedData.data,
1230 rep.u.dhInfo.dhSignedData.length,
1234 krb5_set_error_string(context,
1235 "PKINIT: decoding failed DH "
1236 "ContentInfo: %d", ret);
1238 free_PA_PK_AS_REP(&rep);
1241 ret = pk_rd_pa_reply_dh(context, &ci, realm, ctx, etype, hi,
1243 rep.u.dhInfo.serverDHNonce,
1245 free_ContentInfo(&ci);
1246 free_PA_PK_AS_REP(&rep);
1249 case choice_PA_PK_AS_REP_encKeyPack:
1250 ret = decode_ContentInfo(rep.u.encKeyPack.data,
1251 rep.u.encKeyPack.length,
1254 free_PA_PK_AS_REP(&rep);
1256 krb5_set_error_string(context,
1257 "PKINIT: -25 decoding failed "
1258 "ContentInfo: %d", ret);
1261 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &ci, realm, ctx,
1262 etype, hi, nonce, req_buffer, pa, key);
1263 free_ContentInfo(&ci);
1266 free_PA_PK_AS_REP(&rep);
1267 krb5_set_error_string(context, "PKINIT: -27 reply "
1268 "invalid content type");
1276 /* Check for Windows encoding of the AS-REP pa data */
1278 PA_PK_AS_REP_Win2k w2krep;
1280 memset(&w2krep, 0, sizeof(w2krep));
1282 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1283 pa->padata_value.length,
1287 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1288 "pkinit reply %d", ret);
1292 krb5_clear_error_string(context);
1294 switch (w2krep.element) {
1295 case choice_PA_PK_AS_REP_Win2k_encKeyPack:
1296 ret = decode_ContentInfo(w2krep.u.encKeyPack.data,
1297 w2krep.u.encKeyPack.length,
1300 free_PA_PK_AS_REP_Win2k(&w2krep);
1302 krb5_set_error_string(context,
1303 "PKINIT: decoding failed "
1308 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &ci, realm, ctx,
1309 etype, hi, nonce, req_buffer, pa, key);
1310 free_ContentInfo(&ci);
1313 free_PA_PK_AS_REP_Win2k(&w2krep);
1314 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1326 krb5_context context;
1327 krb5_prompter_fct prompter;
1328 void *prompter_data;
1332 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1334 krb5_error_code ret;
1336 krb5_data password_data;
1337 struct prompter *p = data;
1339 password_data.data = prompter->reply.data;
1340 password_data.length = prompter->reply.length;
1342 prompt.prompt = prompter->prompt;
1343 prompt.hidden = hx509_prompt_hidden(prompter->type);
1344 prompt.reply = &password_data;
1346 switch (prompter->type) {
1347 case HX509_PROMPT_TYPE_INFO:
1348 prompt.type = KRB5_PROMPT_TYPE_INFO;
1350 case HX509_PROMPT_TYPE_PASSWORD:
1351 case HX509_PROMPT_TYPE_QUESTION:
1353 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1357 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1359 memset (prompter->reply.data, 0, prompter->reply.length);
1366 void KRB5_LIB_FUNCTION
1367 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1370 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1374 krb5_error_code KRB5_LIB_FUNCTION
1375 _krb5_pk_load_id(krb5_context context,
1376 struct krb5_pk_identity **ret_id,
1377 const char *user_id,
1378 const char *anchor_id,
1379 char * const *chain_list,
1380 char * const *revoke_list,
1381 krb5_prompter_fct prompter,
1382 void *prompter_data,
1385 struct krb5_pk_identity *id = NULL;
1386 hx509_lock lock = NULL;
1392 if (anchor_id == NULL) {
1393 krb5_set_error_string(context, "PKINIT: No anchor given");
1394 return HEIM_PKINIT_NO_VALID_CA;
1397 if (user_id == NULL) {
1398 krb5_set_error_string(context,
1399 "PKINIT: No user certificate given");
1400 return HEIM_PKINIT_NO_PRIVATE_KEY;
1405 id = calloc(1, sizeof(*id));
1407 krb5_set_error_string(context, "malloc: out of memory");
1412 ret = hx509_context_init(&id->hx509ctx);
1416 ret = hx509_lock_init(id->hx509ctx, &lock);
1417 if (password && password[0])
1418 hx509_lock_add_password(lock, password);
1421 p.context = context;
1422 p.prompter = prompter;
1423 p.prompter_data = prompter_data;
1425 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1430 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1434 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1438 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1439 0, NULL, &id->certpool);
1443 while (chain_list && *chain_list) {
1444 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1447 krb5_set_error_string(context,
1448 "pkinit failed to load chain %s",
1456 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1458 krb5_set_error_string(context, "revoke failed to init");
1462 while (*revoke_list) {
1463 ret = hx509_revoke_add_crl(id->hx509ctx,
1467 krb5_set_error_string(context,
1468 "pkinit failed to load revoke %s",
1475 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1477 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1481 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1482 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1486 hx509_verify_destroy_ctx(id->verify_ctx);
1487 hx509_certs_free(&id->certs);
1488 hx509_certs_free(&id->anchors);
1489 hx509_certs_free(&id->certpool);
1490 hx509_revoke_free(&id->revokectx);
1491 hx509_context_free(&id->hx509ctx);
1496 hx509_lock_free(lock);
1501 static krb5_error_code
1502 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1503 struct krb5_dh_moduli **moduli)
1505 const struct krb5_dh_moduli *m;
1507 m = moduli[1]; /* XXX */
1509 m = moduli[0]; /* XXX */
1511 dh->p = integer_to_BN(context, "p", &m->p);
1514 dh->g = integer_to_BN(context, "g", &m->g);
1517 dh->q = integer_to_BN(context, "q", &m->q);
1527 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1528 const char *name, heim_integer *integer)
1532 p1 = strsep(p, " \t");
1534 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1535 file, name, lineno);
1538 ret = der_parse_hex_heim_integer(p1, integer);
1540 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1542 file, name, lineno);
1550 _krb5_parse_moduli_line(krb5_context context,
1554 struct krb5_dh_moduli **m)
1556 struct krb5_dh_moduli *m1;
1562 m1 = calloc(1, sizeof(*m1));
1564 krb5_set_error_string(context, "malloc - out of memory");
1568 while (isspace((unsigned char)*p))
1574 p1 = strsep(&p, " \t");
1576 krb5_set_error_string(context, "moduli file %s missing name "
1577 "on line %d", file, lineno);
1580 m1->name = strdup(p1);
1582 krb5_set_error_string(context, "malloc - out of memeory");
1587 p1 = strsep(&p, " \t");
1589 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1594 m1->bits = atoi(p1);
1595 if (m1->bits == 0) {
1596 krb5_set_error_string(context, "moduli file %s have un-parsable "
1597 "bits on line %d", file, lineno);
1601 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1604 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1607 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1616 der_free_heim_integer(&m1->p);
1617 der_free_heim_integer(&m1->g);
1618 der_free_heim_integer(&m1->q);
1624 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1627 for (i = 0; moduli[i] != NULL; i++) {
1628 free(moduli[i]->name);
1629 der_free_heim_integer(&moduli[i]->p);
1630 der_free_heim_integer(&moduli[i]->g);
1631 der_free_heim_integer(&moduli[i]->q);
1637 static const char *default_moduli =
1639 "RFC2412-MODP-group2 "
1643 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1644 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1645 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1646 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1647 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1648 "FFFFFFFF" "FFFFFFFF "
1652 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1653 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1654 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1655 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1656 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1657 "FFFFFFFF" "FFFFFFFF";
1661 _krb5_parse_moduli(krb5_context context, const char *file,
1662 struct krb5_dh_moduli ***moduli)
1664 /* name bits P G Q */
1665 krb5_error_code ret;
1666 struct krb5_dh_moduli **m = NULL, **m2;
1669 int lineno = 0, n = 0;
1673 m = calloc(1, sizeof(m[0]) * 2);
1675 krb5_set_error_string(context, "malloc: out of memory");
1679 strlcpy(buf, default_moduli, sizeof(buf));
1680 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1682 _krb5_free_moduli(m);
1690 f = fopen(file, "r");
1696 while(fgets(buf, sizeof(buf), f) != NULL) {
1697 struct krb5_dh_moduli *element;
1699 buf[strcspn(buf, "\n")] = '\0';
1702 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1704 krb5_set_error_string(context, "malloc: out of memory");
1705 _krb5_free_moduli(m);
1712 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1714 _krb5_free_moduli(m);
1717 if (element == NULL)
1729 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1730 heim_integer *p, heim_integer *g, heim_integer *q,
1731 struct krb5_dh_moduli **moduli,
1739 for (i = 0; moduli[i] != NULL; i++) {
1740 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1741 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1742 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1744 if (bits && bits > moduli[i]->bits) {
1745 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1746 "no accepted, not enough bits generated",
1748 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1751 *name = strdup(moduli[i]->name);
1755 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1756 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1759 void KRB5_LIB_FUNCTION
1760 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1763 krb5_pk_init_ctx ctx;
1765 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1767 ctx = opt->opt_private->pk_init_ctx;
1772 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1773 hx509_certs_free(&ctx->id->certs);
1774 hx509_certs_free(&ctx->id->anchors);
1775 hx509_certs_free(&ctx->id->certpool);
1776 hx509_context_free(&ctx->id->hx509ctx);
1778 if (ctx->clientDHNonce) {
1779 krb5_free_data(NULL, ctx->clientDHNonce);
1780 ctx->clientDHNonce = NULL;
1783 _krb5_free_moduli(ctx->m);
1787 opt->opt_private->pk_init_ctx = NULL;
1791 krb5_error_code KRB5_LIB_FUNCTION
1792 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1793 krb5_get_init_creds_opt *opt,
1794 krb5_principal principal,
1795 const char *user_id,
1796 const char *x509_anchors,
1797 char * const * pool,
1798 char * const * pki_revoke,
1800 krb5_prompter_fct prompter,
1801 void *prompter_data,
1805 krb5_error_code ret;
1806 char *anchors = NULL;
1808 if (opt->opt_private == NULL) {
1809 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1813 opt->opt_private->pk_init_ctx =
1814 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1815 if (opt->opt_private->pk_init_ctx == NULL) {
1816 krb5_set_error_string(context, "malloc: out of memory");
1819 opt->opt_private->pk_init_ctx->dh = NULL;
1820 opt->opt_private->pk_init_ctx->id = NULL;
1821 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1822 opt->opt_private->pk_init_ctx->require_binding = 0;
1823 opt->opt_private->pk_init_ctx->require_eku = 1;
1824 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1827 /* XXX implement krb5_appdefault_strings */
1829 pool = krb5_config_get_strings(context, NULL,
1834 if (pki_revoke == NULL)
1835 pki_revoke = krb5_config_get_strings(context, NULL,
1840 if (x509_anchors == NULL) {
1841 krb5_appdefault_string(context, "kinit",
1842 krb5_principal_get_realm(context, principal),
1843 "pkinit-anchors", NULL, &anchors);
1844 x509_anchors = anchors;
1847 ret = _krb5_pk_load_id(context,
1848 &opt->opt_private->pk_init_ctx->id,
1857 free(opt->opt_private->pk_init_ctx);
1858 opt->opt_private->pk_init_ctx = NULL;
1862 if ((flags & 2) == 0) {
1863 const char *moduli_file;
1865 moduli_file = krb5_config_get_string(context, NULL,
1870 ret = _krb5_parse_moduli(context, moduli_file,
1871 &opt->opt_private->pk_init_ctx->m);
1873 _krb5_get_init_creds_opt_free_pkinit(opt);
1877 opt->opt_private->pk_init_ctx->dh = DH_new();
1878 if (opt->opt_private->pk_init_ctx->dh == NULL) {
1879 krb5_set_error_string(context, "malloc: out of memory");
1880 _krb5_get_init_creds_opt_free_pkinit(opt);
1884 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh, 0,
1885 opt->opt_private->pk_init_ctx->m);
1887 _krb5_get_init_creds_opt_free_pkinit(opt);
1891 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
1892 krb5_set_error_string(context, "pkinit: failed to generate DH key");
1893 _krb5_get_init_creds_opt_free_pkinit(opt);
1900 krb5_set_error_string(context, "no support for PKINIT compiled in");