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.98 2006/05/06 13:24:54 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 revoke;
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 = 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 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 = 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,
678 *signer = calloc(1, sizeof(**signer));
679 if (*signer == NULL) {
680 krb5_clear_error_string(context);
689 ret = hx509_certs_start_seq(id->hx509ctx,
693 krb5_clear_error_string(context);
696 ret = hx509_certs_next_cert(id->hx509ctx,
701 krb5_clear_error_string(context);
704 ret = hx509_certs_end_seq(id->hx509ctx,
708 krb5_clear_error_string(context);
714 hx509_certs_free(&signer_certs);
717 hx509_cert_free((*signer)->cert);
726 static krb5_error_code
727 get_reply_key_win(krb5_context context,
728 const krb5_data *content,
732 ReplyKeyPack_Win2k key_pack;
736 ret = decode_ReplyKeyPack_Win2k(content->data,
741 krb5_set_error_string(context, "PKINIT decoding reply key failed");
742 free_ReplyKeyPack_Win2k(&key_pack);
746 if (key_pack.nonce != nonce) {
747 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
748 free_ReplyKeyPack_Win2k(&key_pack);
749 return KRB5KRB_AP_ERR_MODIFIED;
752 *key = malloc (sizeof (**key));
754 krb5_set_error_string(context, "PKINIT failed allocating reply key");
755 free_ReplyKeyPack_Win2k(&key_pack);
756 krb5_set_error_string(context, "malloc: out of memory");
760 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
761 free_ReplyKeyPack_Win2k(&key_pack);
763 krb5_set_error_string(context, "PKINIT failed copying reply key");
770 static krb5_error_code
771 get_reply_key(krb5_context context,
772 const krb5_data *content,
773 const krb5_data *req_buffer,
776 ReplyKeyPack key_pack;
780 ret = decode_ReplyKeyPack(content->data,
785 krb5_set_error_string(context, "PKINIT decoding reply key failed");
786 free_ReplyKeyPack(&key_pack);
794 * XXX Verify kp.replyKey is a allowed enctype in the
798 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
800 free_ReplyKeyPack(&key_pack);
804 ret = krb5_verify_checksum(context, crypto, 6,
805 req_buffer->data, req_buffer->length,
806 &key_pack.asChecksum);
807 krb5_crypto_destroy(context, crypto);
809 free_ReplyKeyPack(&key_pack);
814 *key = malloc (sizeof (**key));
816 krb5_set_error_string(context, "PKINIT failed allocating reply key");
817 free_ReplyKeyPack(&key_pack);
818 krb5_set_error_string(context, "malloc: out of memory");
822 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
823 free_ReplyKeyPack(&key_pack);
825 krb5_set_error_string(context, "PKINIT failed copying reply key");
833 static krb5_error_code
834 pk_verify_host(krb5_context context,
836 const krb5_krbhst_info *hi,
837 struct krb5_pk_init_ctx_data *ctx,
838 struct krb5_pk_cert *host)
840 krb5_error_code ret = 0;
842 if (ctx->require_eku) {
843 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
844 oid_id_pkkdcekuoid(), 0);
846 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
850 if (ctx->require_krbtgt_otherName) {
851 hx509_octet_string_list list;
854 ret = hx509_cert_find_subjectAltName_otherName(host->cert,
858 krb5_clear_error_string(context);
862 for (i = 0; i < list.len; i++) {
865 ret = decode_KRB5PrincipalName(list.val[i].data,
870 krb5_clear_error_string(context);
874 if (r.principalName.name_string.len != 2 ||
875 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
876 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
877 strcmp(r.realm, realm) != 0)
879 krb5_set_error_string(context, "KDC have wrong realm name in "
884 free_KRB5PrincipalName(&r);
888 hx509_free_octet_string_list(&list);
894 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
895 ctx->require_hostname_match,
897 hi->ai->ai_addr, hi->ai->ai_addrlen);
900 krb5_set_error_string(context, "Address mismatch in the KDC certificate");
905 static krb5_error_code
906 pk_rd_pa_reply_enckey(krb5_context context,
908 const ContentInfo *rep,
910 krb5_pk_init_ctx ctx,
912 const krb5_krbhst_info *hi,
914 const krb5_data *req_buffer,
919 struct krb5_pk_cert *host = NULL;
924 heim_oid contentType = { 0, NULL };
926 if (heim_oid_cmp(oid_id_pkcs7_envelopedData(), &rep->contentType)) {
927 krb5_set_error_string(context, "PKINIT: Invalid content type");
931 if (rep->content == NULL) {
932 krb5_set_error_string(context, "PKINIT: No content in reply");
936 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
939 rep->content->length,
946 length = content.length;
948 /* win2k uses ContentInfo */
949 if (type == COMPAT_WIN2K) {
952 ret = decode_ContentInfo(p, length, &ci, &size);
954 krb5_set_error_string(context,
955 "PKINIT: failed decoding ContentInfo: %d",
960 if (heim_oid_cmp(&ci.contentType, oid_id_pkcs7_signedData())) {
961 ret = EINVAL; /* XXX */
962 krb5_set_error_string(context, "PKINIT: Invalid content type");
965 if (ci.content == NULL) {
966 ret = EINVAL; /* XXX */
967 krb5_set_error_string(context, "PKINIT: Invalid content type");
970 krb5_data_free(&content);
971 content = *ci.content;
972 p = ci.content->data;
973 length = ci.content->length;
976 ret = _krb5_pk_verify_sign(context,
986 /* make sure that it is the kdc's certificate */
987 ret = pk_verify_host(context, realm, hi, ctx, host);
989 krb5_set_error_string(context, "PKINIT: failed verify host: %d", ret);
994 if (type == COMPAT_WIN2K) {
995 if (heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
996 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
997 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1001 if (heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1002 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1003 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1011 ret = get_reply_key(context, &content, req_buffer, key);
1012 if (ret != 0 && ctx->require_binding == 0)
1013 ret = get_reply_key_win(context, &content, nonce, key);
1016 ret = get_reply_key(context, &content, req_buffer, key);
1022 /* XXX compare given etype with key->etype */
1026 _krb5_pk_cert_free(host);
1027 free_oid(&contentType);
1028 krb5_data_free(&content);
1033 static krb5_error_code
1034 pk_rd_pa_reply_dh(krb5_context context,
1035 const ContentInfo *rep,
1037 krb5_pk_init_ctx ctx,
1039 const krb5_krbhst_info *hi,
1044 krb5_keyblock **key)
1046 unsigned char *p, *dh_gen_key = NULL;
1047 struct krb5_pk_cert *host = NULL;
1048 BIGNUM *kdc_dh_pubkey = NULL;
1049 KDCDHKeyInfo kdc_dh_info;
1050 heim_oid contentType = { 0, NULL };
1052 krb5_error_code ret;
1056 krb5_data_zero(&content);
1057 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1059 if (heim_oid_cmp(oid_id_pkcs7_signedData(), &rep->contentType)) {
1060 krb5_set_error_string(context, "PKINIT: Invalid content type");
1064 if (rep->content == NULL) {
1065 krb5_set_error_string(context, "PKINIT: No content in reply");
1069 ret = _krb5_pk_verify_sign(context,
1071 rep->content->length,
1079 /* make sure that it is the kdc's certificate */
1080 ret = pk_verify_host(context, realm, hi, ctx, host);
1084 if (heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1085 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1086 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1090 ret = decode_KDCDHKeyInfo(content.data,
1098 if (kdc_dh_info.nonce != nonce) {
1099 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1100 ret = KRB5KRB_AP_ERR_MODIFIED;
1104 if (kdc_dh_info.dhKeyExpiration) {
1106 krb5_set_error_string(context, "pkinit; got key expiration "
1107 "without server nonce");
1108 ret = KRB5KRB_ERR_GENERIC;
1112 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1114 ret = KRB5KRB_ERR_GENERIC;
1119 krb5_set_error_string(context, "pkinit: got server nonce "
1120 "without key expiration");
1121 ret = KRB5KRB_ERR_GENERIC;
1128 p = kdc_dh_info.subjectPublicKey.data;
1129 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1133 ret = decode_DHPublicKey(p, size, &k, NULL);
1135 krb5_set_error_string(context, "pkinit: can't decode "
1136 "without key expiration");
1140 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1141 free_DHPublicKey(&k);
1142 if (kdc_dh_pubkey == NULL) {
1143 ret = KRB5KRB_ERR_GENERIC;
1148 dh_gen_keylen = DH_size(ctx->dh);
1149 size = BN_num_bytes(ctx->dh->p);
1150 if (size < dh_gen_keylen)
1151 size = dh_gen_keylen;
1153 dh_gen_key = malloc(size);
1154 if (dh_gen_key == NULL) {
1155 krb5_set_error_string(context, "malloc: out of memory");
1159 memset(dh_gen_key, 0, size - dh_gen_keylen);
1161 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1162 kdc_dh_pubkey, ctx->dh);
1163 if (dh_gen_keylen == -1) {
1164 krb5_set_error_string(context,
1165 "PKINIT: Can't compute Diffie-Hellman key");
1166 ret = KRB5KRB_ERR_GENERIC;
1170 *key = malloc (sizeof (**key));
1172 krb5_set_error_string(context, "malloc: out of memory");
1177 ret = _krb5_pk_octetstring2key(context,
1179 dh_gen_key, dh_gen_keylen,
1183 krb5_set_error_string(context,
1184 "PKINIT: can't create key from DH key");
1192 BN_free(kdc_dh_pubkey);
1194 memset(dh_gen_key, 0, DH_size(ctx->dh));
1198 _krb5_pk_cert_free(host);
1200 krb5_data_free(&content);
1201 free_KDCDHKeyInfo(&kdc_dh_info);
1206 krb5_error_code KRB5_LIB_FUNCTION
1207 _krb5_pk_rd_pa_reply(krb5_context context,
1211 const krb5_krbhst_info *hi,
1213 const krb5_data *req_buffer,
1215 krb5_keyblock **key)
1217 krb5_pk_init_ctx ctx = c;
1218 krb5_error_code ret;
1222 /* Check for IETF PK-INIT first */
1223 if (pa->padata_type == KRB5_PADATA_PK_AS_REP) {
1226 memset(&rep, 0, sizeof(rep));
1228 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1229 pa->padata_value.length,
1235 switch (rep.element) {
1236 case choice_PA_PK_AS_REP_dhInfo:
1237 ret = decode_ContentInfo(rep.u.dhInfo.dhSignedData.data,
1238 rep.u.dhInfo.dhSignedData.length,
1242 krb5_set_error_string(context,
1243 "PKINIT: decoding failed DH "
1244 "ContentInfo: %d", ret);
1246 free_PA_PK_AS_REP(&rep);
1249 ret = pk_rd_pa_reply_dh(context, &ci, realm, ctx, etype, hi,
1251 rep.u.dhInfo.serverDHNonce,
1253 free_ContentInfo(&ci);
1254 free_PA_PK_AS_REP(&rep);
1257 case choice_PA_PK_AS_REP_encKeyPack:
1258 ret = decode_ContentInfo(rep.u.encKeyPack.data,
1259 rep.u.encKeyPack.length,
1262 free_PA_PK_AS_REP(&rep);
1264 krb5_set_error_string(context,
1265 "PKINIT: -25 decoding failed "
1266 "ContentInfo: %d", ret);
1269 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &ci, realm, ctx,
1270 etype, hi, nonce, req_buffer, pa, key);
1271 free_ContentInfo(&ci);
1274 free_PA_PK_AS_REP(&rep);
1275 krb5_set_error_string(context, "PKINIT: -27 reply "
1276 "invalid content type");
1284 /* Check for Windows encoding of the AS-REP pa data */
1286 PA_PK_AS_REP_Win2k w2krep;
1288 memset(&w2krep, 0, sizeof(w2krep));
1290 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1291 pa->padata_value.length,
1295 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1296 "pkinit reply %d", ret);
1300 krb5_clear_error_string(context);
1302 switch (w2krep.element) {
1303 case choice_PA_PK_AS_REP_Win2k_encKeyPack:
1304 ret = decode_ContentInfo(w2krep.u.encKeyPack.data,
1305 w2krep.u.encKeyPack.length,
1308 free_PA_PK_AS_REP_Win2k(&w2krep);
1310 krb5_set_error_string(context,
1311 "PKINIT: decoding failed "
1316 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &ci, realm, ctx,
1317 etype, hi, nonce, req_buffer, pa, key);
1318 free_ContentInfo(&ci);
1321 free_PA_PK_AS_REP_Win2k(&w2krep);
1322 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1334 krb5_context context;
1335 krb5_prompter_fct prompter;
1336 void *prompter_data;
1340 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1342 krb5_error_code ret;
1344 krb5_data password_data;
1345 struct prompter *p = data;
1347 password_data.data = prompter->reply.data;
1348 password_data.length = prompter->reply.length;
1349 prompt.prompt = "Enter your private key passphrase: ";
1351 prompt.reply = &password_data;
1352 if (prompter->hidden)
1353 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1355 prompt.type = KRB5_PROMPT_TYPE_PREAUTH; /* XXX */
1357 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1359 memset (prompter->reply.data, 0, prompter->reply.length);
1362 return strlen(prompter->reply.data);
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,
1380 char * const *revoke,
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);
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, NULL, &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 && *chain) {
1444 ret = hx509_certs_append(id->hx509ctx, id->certpool, NULL, *chain);
1446 krb5_set_error_string(context,
1447 "pkinit failed to load chain %s",
1455 ret = hx509_revoke_init(id->hx509ctx, &id->revoke);
1457 krb5_set_error_string(context, "revoke failed to init");
1462 ret = hx509_revoke_add_crl(id->hx509ctx, id->revoke, *revoke);
1464 krb5_set_error_string(context,
1465 "pkinit failed to load revoke %s",
1472 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1474 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1478 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1479 hx509_verify_attach_revoke(id->verify_ctx, id->revoke);
1483 hx509_verify_destroy_ctx(id->verify_ctx);
1484 hx509_certs_free(&id->certs);
1485 hx509_certs_free(&id->anchors);
1486 hx509_certs_free(&id->certpool);
1487 hx509_revoke_free(&id->revoke);
1488 hx509_context_free(&id->hx509ctx);
1493 hx509_lock_free(lock);
1498 static krb5_error_code
1499 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1500 struct krb5_dh_moduli **moduli)
1502 const struct krb5_dh_moduli *m;
1504 m = moduli[1]; /* XXX */
1506 m = moduli[0]; /* XXX */
1508 dh->p = integer_to_BN(context, "p", &m->p);
1511 dh->g = integer_to_BN(context, "g", &m->g);
1514 dh->q = integer_to_BN(context, "q", &m->q);
1524 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1525 const char *name, heim_integer *integer)
1529 p1 = strsep(p, " \t");
1531 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1532 file, name, lineno);
1535 ret = der_parse_hex_heim_integer(p1, integer);
1537 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1539 file, name, lineno);
1547 _krb5_parse_moduli_line(krb5_context context,
1551 struct krb5_dh_moduli **m)
1553 struct krb5_dh_moduli *m1;
1559 m1 = calloc(1, sizeof(*m1));
1561 krb5_set_error_string(context, "malloc - out of memory");
1565 while (isspace((unsigned char)*p))
1571 p1 = strsep(&p, " \t");
1573 krb5_set_error_string(context, "moduli file %s missing name "
1574 "on line %d", file, lineno);
1577 m1->name = strdup(p1);
1579 krb5_set_error_string(context, "malloc - out of memeory");
1584 p1 = strsep(&p, " \t");
1586 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1591 m1->bits = atoi(p1);
1592 if (m1->bits == 0) {
1593 krb5_set_error_string(context, "moduli file %s have un-parsable "
1594 "bits on line %d", file, lineno);
1598 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1601 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1604 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1613 free_heim_integer(&m1->p);
1614 free_heim_integer(&m1->g);
1615 free_heim_integer(&m1->q);
1621 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1624 for (i = 0; moduli[i] != NULL; i++) {
1625 free(moduli[i]->name);
1626 free_heim_integer(&moduli[i]->p);
1627 free_heim_integer(&moduli[i]->g);
1628 free_heim_integer(&moduli[i]->q);
1634 static const char *default_moduli =
1636 "RFC2412-MODP-group2 "
1640 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1641 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1642 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1643 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1644 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1645 "FFFFFFFF" "FFFFFFFF "
1649 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1650 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1651 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1652 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1653 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1654 "FFFFFFFF" "FFFFFFFF";
1658 _krb5_parse_moduli(krb5_context context, const char *file,
1659 struct krb5_dh_moduli ***moduli)
1661 /* name bits P G Q */
1662 krb5_error_code ret;
1663 struct krb5_dh_moduli **m = NULL, **m2;
1666 int lineno = 0, n = 0;
1670 m = calloc(1, sizeof(m[0]) * 2);
1672 krb5_set_error_string(context, "malloc: out of memory");
1676 strlcpy(buf, default_moduli, sizeof(buf));
1677 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1679 _krb5_free_moduli(m);
1687 f = fopen(file, "r");
1693 while(fgets(buf, sizeof(buf), f) != NULL) {
1694 struct krb5_dh_moduli *element;
1696 buf[strcspn(buf, "\n")] = '\0';
1699 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1701 krb5_set_error_string(context, "malloc: out of memory");
1702 _krb5_free_moduli(m);
1709 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1711 _krb5_free_moduli(m);
1714 if (element == NULL)
1726 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1727 heim_integer *p, heim_integer *g, heim_integer *q,
1728 struct krb5_dh_moduli **moduli,
1736 for (i = 0; moduli[i] != NULL; i++) {
1737 if (heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1738 heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1739 (q == NULL || heim_integer_cmp(&moduli[i]->q, q) == 0))
1741 if (bits && bits > moduli[i]->bits) {
1742 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1743 "no accepted, not enough bits generated",
1745 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1748 *name = strdup(moduli[i]->name);
1752 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1753 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1756 void KRB5_LIB_FUNCTION
1757 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1760 krb5_pk_init_ctx ctx;
1762 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1764 ctx = opt->opt_private->pk_init_ctx;
1769 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1770 hx509_certs_free(&ctx->id->certs);
1771 hx509_certs_free(&ctx->id->anchors);
1772 hx509_certs_free(&ctx->id->certpool);
1773 hx509_context_free(&ctx->id->hx509ctx);
1775 if (ctx->clientDHNonce) {
1776 krb5_free_data(NULL, ctx->clientDHNonce);
1777 ctx->clientDHNonce = NULL;
1780 _krb5_free_moduli(ctx->m);
1784 opt->opt_private->pk_init_ctx = NULL;
1788 krb5_error_code KRB5_LIB_FUNCTION
1789 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1790 krb5_get_init_creds_opt *opt,
1791 krb5_principal principal,
1792 const char *user_id,
1793 const char *x509_anchors,
1794 char * const * chain,
1795 char * const * revoke,
1797 krb5_prompter_fct prompter,
1798 void *prompter_data,
1802 krb5_error_code ret;
1804 if (opt->opt_private == NULL) {
1805 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1809 opt->opt_private->pk_init_ctx =
1810 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1811 if (opt->opt_private->pk_init_ctx == NULL) {
1812 krb5_set_error_string(context, "malloc: out of memory");
1815 opt->opt_private->pk_init_ctx->dh = NULL;
1816 opt->opt_private->pk_init_ctx->id = NULL;
1817 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1818 opt->opt_private->pk_init_ctx->require_binding = 0;
1819 opt->opt_private->pk_init_ctx->require_eku = 1;
1820 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1822 ret = _krb5_pk_load_id(context,
1823 &opt->opt_private->pk_init_ctx->id,
1832 free(opt->opt_private->pk_init_ctx);
1833 opt->opt_private->pk_init_ctx = NULL;
1837 if ((flags & 2) == 0) {
1838 const char *moduli_file;
1840 moduli_file = krb5_config_get_string(context, NULL,
1845 ret = _krb5_parse_moduli(context, moduli_file,
1846 &opt->opt_private->pk_init_ctx->m);
1848 _krb5_get_init_creds_opt_free_pkinit(opt);
1852 opt->opt_private->pk_init_ctx->dh = DH_new();
1853 if (opt->opt_private->pk_init_ctx->dh == NULL) {
1854 krb5_set_error_string(context, "malloc: out of memory");
1855 _krb5_get_init_creds_opt_free_pkinit(opt);
1859 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh, 0,
1860 opt->opt_private->pk_init_ctx->m);
1862 _krb5_get_init_creds_opt_free_pkinit(opt);
1866 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
1867 krb5_set_error_string(context, "pkinit: failed to generate DH key");
1868 _krb5_get_init_creds_opt_free_pkinit(opt);
1875 krb5_set_error_string(context, "no support for PKINIT compiled in");