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 21004 2007-06-08 01:53:10Z lha $");
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 unsigned int require_binding:1;
87 unsigned int require_eku:1;
88 unsigned int require_krbtgt_otherName:1;
89 unsigned int require_hostname_match:1;
90 unsigned int trustedCertifiers:1;
94 _krb5_pk_copy_error(krb5_context context,
95 hx509_context hx509ctx,
99 __attribute__ ((format (printf, 4, 5)));
105 void KRB5_LIB_FUNCTION
106 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
109 hx509_cert_free(cert->cert);
114 static krb5_error_code
115 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
117 integer->length = BN_num_bytes(bn);
118 integer->data = malloc(integer->length);
119 if (integer->data == NULL) {
120 krb5_clear_error_string(context);
123 BN_bn2bin(bn, integer->data);
124 integer->negative = BN_is_negative(bn);
129 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
133 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
135 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
138 BN_set_negative(bn, f->negative);
143 static krb5_error_code
144 _krb5_pk_create_sign(krb5_context context,
145 const heim_oid *eContentType,
147 struct krb5_pk_identity *id,
148 hx509_peer_info peer,
155 ret = hx509_query_alloc(id->hx509ctx, &q);
157 _krb5_pk_copy_error(context, id->hx509ctx, ret,
158 "Allocate query to find signing certificate");
162 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
163 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
165 ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
166 hx509_query_free(id->hx509ctx, q);
168 _krb5_pk_copy_error(context, id->hx509ctx, ret,
169 "Find certificate to signed CMS data");
173 ret = hx509_cms_create_signed_1(id->hx509ctx,
185 _krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
186 hx509_cert_free(cert);
192 cert2epi(hx509_context context, void *ctx, hx509_cert c)
194 ExternalPrincipalIdentifiers *ids = ctx;
195 ExternalPrincipalIdentifier id;
196 hx509_name subject = NULL;
200 memset(&id, 0, sizeof(id));
202 ret = hx509_cert_get_subject(c, &subject);
206 if (hx509_name_is_null_p(subject) != 0) {
208 id.subjectName = calloc(1, sizeof(*id.subjectName));
209 if (id.subjectName == NULL) {
210 hx509_name_free(&subject);
211 free_ExternalPrincipalIdentifier(&id);
215 ret = hx509_name_to_der_name(subject, &id.subjectName->data,
216 &id.subjectName->length);
218 hx509_name_free(&subject);
219 free_ExternalPrincipalIdentifier(&id);
223 hx509_name_free(&subject);
226 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
227 if (id.issuerAndSerialNumber == NULL) {
228 free_ExternalPrincipalIdentifier(&id);
233 IssuerAndSerialNumber iasn;
237 memset(&iasn, 0, sizeof(iasn));
239 ret = hx509_cert_get_issuer(c, &issuer);
241 free_ExternalPrincipalIdentifier(&id);
245 ret = hx509_name_to_Name(issuer, &iasn.issuer);
246 hx509_name_free(&issuer);
248 free_ExternalPrincipalIdentifier(&id);
252 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
254 free_IssuerAndSerialNumber(&iasn);
255 free_ExternalPrincipalIdentifier(&id);
259 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
260 id.issuerAndSerialNumber->data,
261 id.issuerAndSerialNumber->length,
263 free_IssuerAndSerialNumber(&iasn);
266 if (id.issuerAndSerialNumber->length != size)
270 id.subjectKeyIdentifier = NULL;
272 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
274 free_ExternalPrincipalIdentifier(&id);
279 ids->val[ids->len] = id;
285 static krb5_error_code
286 build_edi(krb5_context context,
287 hx509_context hx509ctx,
289 ExternalPrincipalIdentifiers *ids)
291 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
294 static krb5_error_code
295 build_auth_pack(krb5_context context,
297 krb5_pk_init_ctx ctx,
299 const KDC_REQ_BODY *body,
302 size_t buf_size, len;
309 krb5_clear_error_string(context);
311 memset(&checksum, 0, sizeof(checksum));
313 krb5_us_timeofday(context, &sec, &usec);
314 a->pkAuthenticator.ctime = sec;
315 a->pkAuthenticator.nonce = nonce;
317 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
321 krb5_abortx(context, "internal error in ASN.1 encoder");
323 ret = krb5_create_checksum(context,
334 ALLOC(a->pkAuthenticator.paChecksum, 1);
335 if (a->pkAuthenticator.paChecksum == NULL) {
336 krb5_set_error_string(context, "malloc: out of memory");
340 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
341 checksum.checksum.data, checksum.checksum.length);
342 free_Checksum(&checksum);
348 heim_integer dh_pub_key;
352 if (1 /* support_cached_dh */) {
353 ALLOC(a->clientDHNonce, 1);
354 if (a->clientDHNonce == NULL) {
355 krb5_clear_error_string(context);
358 ret = krb5_data_alloc(a->clientDHNonce, 40);
359 if (a->clientDHNonce == NULL) {
360 krb5_clear_error_string(context);
363 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
364 ret = krb5_copy_data(context, a->clientDHNonce,
365 &ctx->clientDHNonce);
370 ALLOC(a->clientPublicValue, 1);
371 if (a->clientPublicValue == NULL)
373 ret = der_copy_oid(oid_id_dhpublicnumber(),
374 &a->clientPublicValue->algorithm.algorithm);
378 memset(&dp, 0, sizeof(dp));
380 ret = BN_to_integer(context, dh->p, &dp.p);
382 free_DomainParameters(&dp);
385 ret = BN_to_integer(context, dh->g, &dp.g);
387 free_DomainParameters(&dp);
390 ret = BN_to_integer(context, dh->q, &dp.q);
392 free_DomainParameters(&dp);
396 dp.validationParms = NULL;
398 a->clientPublicValue->algorithm.parameters =
399 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
400 if (a->clientPublicValue->algorithm.parameters == NULL) {
401 free_DomainParameters(&dp);
405 ASN1_MALLOC_ENCODE(DomainParameters,
406 a->clientPublicValue->algorithm.parameters->data,
407 a->clientPublicValue->algorithm.parameters->length,
409 free_DomainParameters(&dp);
412 if (size != a->clientPublicValue->algorithm.parameters->length)
413 krb5_abortx(context, "Internal ASN1 encoder error");
415 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
419 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
420 &dh_pub_key, &size, ret);
421 der_free_heim_integer(&dh_pub_key);
424 if (size != dhbuf.length)
425 krb5_abortx(context, "asn1 internal error");
427 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
428 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
432 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
433 if (a->supportedCMSTypes == NULL)
436 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
437 &a->supportedCMSTypes->val,
438 &a->supportedCMSTypes->len);
446 krb5_error_code KRB5_LIB_FUNCTION
447 _krb5_pk_mk_ContentInfo(krb5_context context,
448 const krb5_data *buf,
450 struct ContentInfo *content_info)
454 ret = der_copy_oid(oid, &content_info->contentType);
457 ALLOC(content_info->content, 1);
458 if (content_info->content == NULL)
460 content_info->content->data = malloc(buf->length);
461 if (content_info->content->data == NULL)
463 memcpy(content_info->content->data, buf->data, buf->length);
464 content_info->content->length = buf->length;
468 static krb5_error_code
469 pk_mk_padata(krb5_context context,
470 krb5_pk_init_ctx ctx,
471 const KDC_REQ_BODY *req_body,
475 struct ContentInfo content_info;
479 krb5_data buf, sd_buf;
482 krb5_data_zero(&buf);
483 krb5_data_zero(&sd_buf);
484 memset(&content_info, 0, sizeof(content_info));
486 if (ctx->type == COMPAT_WIN2K) {
491 memset(&ap, 0, sizeof(ap));
493 /* fill in PKAuthenticator */
494 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
496 free_AuthPack_Win2k(&ap);
497 krb5_clear_error_string(context);
500 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
502 free_AuthPack_Win2k(&ap);
503 krb5_clear_error_string(context);
507 krb5_us_timeofday(context, &sec, &usec);
508 ap.pkAuthenticator.ctime = sec;
509 ap.pkAuthenticator.cusec = usec;
510 ap.pkAuthenticator.nonce = nonce;
512 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
514 free_AuthPack_Win2k(&ap);
516 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
519 if (buf.length != size)
520 krb5_abortx(context, "internal ASN1 encoder error");
522 oid = oid_id_pkcs7_data();
523 } else if (ctx->type == COMPAT_IETF) {
526 memset(&ap, 0, sizeof(ap));
528 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
534 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
537 krb5_set_error_string(context, "AuthPack: %d", ret);
540 if (buf.length != size)
541 krb5_abortx(context, "internal ASN1 encoder error");
543 oid = oid_id_pkauthdata();
545 krb5_abortx(context, "internal pkinit error");
547 ret = _krb5_pk_create_sign(context,
553 krb5_data_free(&buf);
557 ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, oid_id_pkcs7_signedData(),
559 krb5_data_free(&sd_buf);
563 ASN1_MALLOC_ENCODE(ContentInfo, buf.data, buf.length,
564 &content_info, &size, ret);
567 if (buf.length != size)
568 krb5_abortx(context, "Internal ASN1 encoder error");
570 if (ctx->type == COMPAT_WIN2K) {
571 PA_PK_AS_REQ_Win2k winreq;
573 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
575 memset(&winreq, 0, sizeof(winreq));
577 winreq.signed_auth_pack = buf;
579 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
580 &winreq, &size, ret);
581 free_PA_PK_AS_REQ_Win2k(&winreq);
583 } else if (ctx->type == COMPAT_IETF) {
586 pa_type = KRB5_PADATA_PK_AS_REQ;
588 memset(&req, 0, sizeof(req));
589 req.signedAuthPack = buf;
591 if (ctx->trustedCertifiers) {
593 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
594 if (req.trustedCertifiers == NULL) {
595 krb5_set_error_string(context, "malloc: out of memory");
596 free_PA_PK_AS_REQ(&req);
599 ret = build_edi(context, ctx->id->hx509ctx,
600 ctx->id->anchors, req.trustedCertifiers);
602 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
603 free_PA_PK_AS_REQ(&req);
609 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
612 free_PA_PK_AS_REQ(&req);
615 krb5_abortx(context, "internal pkinit error");
617 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
620 if (buf.length != size)
621 krb5_abortx(context, "Internal ASN1 encoder error");
623 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
627 if (ret == 0 && ctx->type == COMPAT_WIN2K)
628 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
631 free_ContentInfo(&content_info);
637 krb5_error_code KRB5_LIB_FUNCTION
638 _krb5_pk_mk_padata(krb5_context context,
640 const KDC_REQ_BODY *req_body,
644 krb5_pk_init_ctx ctx = c;
647 win2k_compat = krb5_config_get_bool_default(context, NULL,
653 if (context->pkinit_flags & KRB5_PKINIT_WIN2K)
657 ctx->require_binding =
658 krb5_config_get_bool_default(context, NULL,
662 "pkinit_win2k_require_binding",
664 ctx->type = COMPAT_WIN2K;
666 ctx->type = COMPAT_IETF;
669 krb5_config_get_bool_default(context, NULL,
673 "pkinit_require_eku",
675 ctx->require_krbtgt_otherName =
676 krb5_config_get_bool_default(context, NULL,
680 "pkinit_require_krbtgt_otherName",
683 ctx->require_hostname_match =
684 krb5_config_get_bool_default(context, NULL,
688 "pkinit_require_hostname_match",
691 ctx->trustedCertifiers =
692 krb5_config_get_bool_default(context, NULL,
696 "pkinit_trustedCertifiers",
699 return pk_mk_padata(context, ctx, req_body, nonce, md);
702 krb5_error_code KRB5_LIB_FUNCTION
703 _krb5_pk_verify_sign(krb5_context context,
706 struct krb5_pk_identity *id,
707 heim_oid *contentType,
709 struct krb5_pk_cert **signer)
711 hx509_certs signer_certs;
716 ret = hx509_cms_verify_signed(id->hx509ctx,
726 _krb5_pk_copy_error(context, id->hx509ctx, ret,
727 "CMS verify signed failed");
731 *signer = calloc(1, sizeof(**signer));
732 if (*signer == NULL) {
733 krb5_clear_error_string(context);
738 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
740 _krb5_pk_copy_error(context, id->hx509ctx, ret,
741 "Failed to get on of the signer certs");
746 hx509_certs_free(&signer_certs);
749 hx509_cert_free((*signer)->cert);
758 static krb5_error_code
759 get_reply_key_win(krb5_context context,
760 const krb5_data *content,
764 ReplyKeyPack_Win2k key_pack;
768 ret = decode_ReplyKeyPack_Win2k(content->data,
773 krb5_set_error_string(context, "PKINIT decoding reply key failed");
774 free_ReplyKeyPack_Win2k(&key_pack);
778 if (key_pack.nonce != nonce) {
779 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
780 free_ReplyKeyPack_Win2k(&key_pack);
781 return KRB5KRB_AP_ERR_MODIFIED;
784 *key = malloc (sizeof (**key));
786 krb5_set_error_string(context, "PKINIT failed allocating reply key");
787 free_ReplyKeyPack_Win2k(&key_pack);
788 krb5_set_error_string(context, "malloc: out of memory");
792 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
793 free_ReplyKeyPack_Win2k(&key_pack);
795 krb5_set_error_string(context, "PKINIT failed copying reply key");
802 static krb5_error_code
803 get_reply_key(krb5_context context,
804 const krb5_data *content,
805 const krb5_data *req_buffer,
808 ReplyKeyPack key_pack;
812 ret = decode_ReplyKeyPack(content->data,
817 krb5_set_error_string(context, "PKINIT decoding reply key failed");
818 free_ReplyKeyPack(&key_pack);
826 * XXX Verify kp.replyKey is a allowed enctype in the
830 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
832 free_ReplyKeyPack(&key_pack);
836 ret = krb5_verify_checksum(context, crypto, 6,
837 req_buffer->data, req_buffer->length,
838 &key_pack.asChecksum);
839 krb5_crypto_destroy(context, crypto);
841 free_ReplyKeyPack(&key_pack);
846 *key = malloc (sizeof (**key));
848 krb5_set_error_string(context, "PKINIT failed allocating reply key");
849 free_ReplyKeyPack(&key_pack);
850 krb5_set_error_string(context, "malloc: out of memory");
854 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
855 free_ReplyKeyPack(&key_pack);
857 krb5_set_error_string(context, "PKINIT failed copying reply key");
865 static krb5_error_code
866 pk_verify_host(krb5_context context,
868 const krb5_krbhst_info *hi,
869 struct krb5_pk_init_ctx_data *ctx,
870 struct krb5_pk_cert *host)
872 krb5_error_code ret = 0;
874 if (ctx->require_eku) {
875 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
876 oid_id_pkkdcekuoid(), 0);
878 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
882 if (ctx->require_krbtgt_otherName) {
883 hx509_octet_string_list list;
886 ret = hx509_cert_find_subjectAltName_otherName(host->cert,
890 krb5_set_error_string(context, "Failed to find the PK-INIT "
891 "subjectAltName in the KDC certificate");
896 for (i = 0; i < list.len; i++) {
899 ret = decode_KRB5PrincipalName(list.val[i].data,
904 krb5_set_error_string(context, "Failed to decode the PK-INIT "
905 "subjectAltName in the KDC certificate");
910 if (r.principalName.name_string.len != 2 ||
911 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
912 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
913 strcmp(r.realm, realm) != 0)
915 krb5_set_error_string(context, "KDC have wrong realm name in "
917 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
920 free_KRB5PrincipalName(&r);
924 hx509_free_octet_string_list(&list);
930 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
931 ctx->require_hostname_match,
933 hi->ai->ai_addr, hi->ai->ai_addrlen);
936 krb5_set_error_string(context, "Address mismatch in "
937 "the KDC certificate");
942 static krb5_error_code
943 pk_rd_pa_reply_enckey(krb5_context context,
945 const ContentInfo *rep,
947 krb5_pk_init_ctx ctx,
949 const krb5_krbhst_info *hi,
951 const krb5_data *req_buffer,
956 struct krb5_pk_cert *host = NULL;
961 heim_oid contentType = { 0, NULL };
963 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), &rep->contentType)) {
964 krb5_set_error_string(context, "PKINIT: Invalid content type");
968 if (rep->content == NULL) {
969 krb5_set_error_string(context, "PKINIT: No content in reply");
973 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
975 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
977 rep->content->length,
982 _krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
983 "Failed to unenvelope CMS data in PK-INIT reply");
988 length = content.length;
990 /* win2k uses ContentInfo */
991 if (type == COMPAT_WIN2K) {
994 ret = decode_ContentInfo(p, length, &ci, &size);
996 krb5_set_error_string(context,
997 "PKINIT: failed decoding ContentInfo: %d",
1002 if (der_heim_oid_cmp(&ci.contentType, oid_id_pkcs7_signedData())) {
1003 ret = EINVAL; /* XXX */
1004 krb5_set_error_string(context, "PKINIT: Invalid content type");
1007 if (ci.content == NULL) {
1008 ret = EINVAL; /* XXX */
1009 krb5_set_error_string(context, "PKINIT: Invalid content type");
1012 krb5_data_free(&content);
1013 content = *ci.content;
1014 p = ci.content->data;
1015 length = ci.content->length;
1018 ret = _krb5_pk_verify_sign(context,
1028 /* make sure that it is the kdc's certificate */
1029 ret = pk_verify_host(context, realm, hi, ctx, host);
1035 if (type == COMPAT_WIN2K) {
1036 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1037 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1038 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1042 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1043 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1044 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1052 ret = get_reply_key(context, &content, req_buffer, key);
1053 if (ret != 0 && ctx->require_binding == 0)
1054 ret = get_reply_key_win(context, &content, nonce, key);
1057 ret = get_reply_key(context, &content, req_buffer, key);
1063 /* XXX compare given etype with key->etype */
1067 _krb5_pk_cert_free(host);
1068 der_free_oid(&contentType);
1069 krb5_data_free(&content);
1074 static krb5_error_code
1075 pk_rd_pa_reply_dh(krb5_context context,
1076 const ContentInfo *rep,
1078 krb5_pk_init_ctx ctx,
1080 const krb5_krbhst_info *hi,
1085 krb5_keyblock **key)
1087 unsigned char *p, *dh_gen_key = NULL;
1088 struct krb5_pk_cert *host = NULL;
1089 BIGNUM *kdc_dh_pubkey = NULL;
1090 KDCDHKeyInfo kdc_dh_info;
1091 heim_oid contentType = { 0, NULL };
1093 krb5_error_code ret;
1097 krb5_data_zero(&content);
1098 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1100 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), &rep->contentType)) {
1101 krb5_set_error_string(context, "PKINIT: Invalid content type");
1105 if (rep->content == NULL) {
1106 krb5_set_error_string(context, "PKINIT: No content in reply");
1110 ret = _krb5_pk_verify_sign(context,
1112 rep->content->length,
1120 /* make sure that it is the kdc's certificate */
1121 ret = pk_verify_host(context, realm, hi, ctx, host);
1125 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1126 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1127 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1131 ret = decode_KDCDHKeyInfo(content.data,
1137 krb5_set_error_string(context, "pkinit - "
1138 "failed to decode KDC DH Key Info");
1142 if (kdc_dh_info.nonce != nonce) {
1143 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1144 ret = KRB5KRB_AP_ERR_MODIFIED;
1148 if (kdc_dh_info.dhKeyExpiration) {
1150 krb5_set_error_string(context, "pkinit; got key expiration "
1151 "without server nonce");
1152 ret = KRB5KRB_ERR_GENERIC;
1156 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1158 ret = KRB5KRB_ERR_GENERIC;
1163 krb5_set_error_string(context, "pkinit: got server nonce "
1164 "without key expiration");
1165 ret = KRB5KRB_ERR_GENERIC;
1172 p = kdc_dh_info.subjectPublicKey.data;
1173 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1177 ret = decode_DHPublicKey(p, size, &k, NULL);
1179 krb5_set_error_string(context, "pkinit: can't decode "
1180 "without key expiration");
1184 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1185 free_DHPublicKey(&k);
1186 if (kdc_dh_pubkey == NULL) {
1187 ret = KRB5KRB_ERR_GENERIC;
1192 dh_gen_keylen = DH_size(ctx->dh);
1193 size = BN_num_bytes(ctx->dh->p);
1194 if (size < dh_gen_keylen)
1195 size = dh_gen_keylen;
1197 dh_gen_key = malloc(size);
1198 if (dh_gen_key == NULL) {
1199 krb5_set_error_string(context, "malloc: out of memory");
1203 memset(dh_gen_key, 0, size - dh_gen_keylen);
1205 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1206 kdc_dh_pubkey, ctx->dh);
1207 if (dh_gen_keylen == -1) {
1208 krb5_set_error_string(context,
1209 "PKINIT: Can't compute Diffie-Hellman key");
1210 ret = KRB5KRB_ERR_GENERIC;
1214 *key = malloc (sizeof (**key));
1216 krb5_set_error_string(context, "malloc: out of memory");
1221 ret = _krb5_pk_octetstring2key(context,
1223 dh_gen_key, dh_gen_keylen,
1227 krb5_set_error_string(context,
1228 "PKINIT: can't create key from DH key");
1236 BN_free(kdc_dh_pubkey);
1238 memset(dh_gen_key, 0, DH_size(ctx->dh));
1242 _krb5_pk_cert_free(host);
1244 krb5_data_free(&content);
1245 der_free_oid(&contentType);
1246 free_KDCDHKeyInfo(&kdc_dh_info);
1251 krb5_error_code KRB5_LIB_FUNCTION
1252 _krb5_pk_rd_pa_reply(krb5_context context,
1256 const krb5_krbhst_info *hi,
1258 const krb5_data *req_buffer,
1260 krb5_keyblock **key)
1262 krb5_pk_init_ctx ctx = c;
1263 krb5_error_code ret;
1267 /* Check for IETF PK-INIT first */
1268 if (ctx->type == COMPAT_IETF) {
1271 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1272 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1276 memset(&rep, 0, sizeof(rep));
1278 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1279 pa->padata_value.length,
1283 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1287 switch (rep.element) {
1288 case choice_PA_PK_AS_REP_dhInfo:
1289 ret = decode_ContentInfo(rep.u.dhInfo.dhSignedData.data,
1290 rep.u.dhInfo.dhSignedData.length,
1294 krb5_set_error_string(context,
1295 "PKINIT: decoding failed DH "
1296 "ContentInfo: %d", ret);
1298 free_PA_PK_AS_REP(&rep);
1301 ret = pk_rd_pa_reply_dh(context, &ci, realm, ctx, etype, hi,
1303 rep.u.dhInfo.serverDHNonce,
1305 free_ContentInfo(&ci);
1306 free_PA_PK_AS_REP(&rep);
1309 case choice_PA_PK_AS_REP_encKeyPack:
1310 ret = decode_ContentInfo(rep.u.encKeyPack.data,
1311 rep.u.encKeyPack.length,
1314 free_PA_PK_AS_REP(&rep);
1316 krb5_set_error_string(context,
1317 "PKINIT: -25 decoding failed "
1318 "ContentInfo: %d", ret);
1321 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &ci, realm, ctx,
1322 etype, hi, nonce, req_buffer, pa, key);
1323 free_ContentInfo(&ci);
1326 free_PA_PK_AS_REP(&rep);
1327 krb5_set_error_string(context, "PKINIT: -27 reply "
1328 "invalid content type");
1333 } else if (ctx->type == COMPAT_WIN2K) {
1334 PA_PK_AS_REP_Win2k w2krep;
1336 /* Check for Windows encoding of the AS-REP pa data */
1338 #if 0 /* should this be ? */
1339 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1340 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1345 memset(&w2krep, 0, sizeof(w2krep));
1347 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1348 pa->padata_value.length,
1352 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1353 "pkinit reply %d", ret);
1357 krb5_clear_error_string(context);
1359 switch (w2krep.element) {
1360 case choice_PA_PK_AS_REP_Win2k_encKeyPack:
1361 ret = decode_ContentInfo(w2krep.u.encKeyPack.data,
1362 w2krep.u.encKeyPack.length,
1365 free_PA_PK_AS_REP_Win2k(&w2krep);
1367 krb5_set_error_string(context,
1368 "PKINIT: decoding failed "
1373 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &ci, realm, ctx,
1374 etype, hi, nonce, req_buffer, pa, key);
1375 free_ContentInfo(&ci);
1378 free_PA_PK_AS_REP_Win2k(&w2krep);
1379 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1386 krb5_set_error_string(context, "PKINIT: unknown reply type");
1394 krb5_context context;
1395 krb5_prompter_fct prompter;
1396 void *prompter_data;
1400 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1402 krb5_error_code ret;
1404 krb5_data password_data;
1405 struct prompter *p = data;
1407 password_data.data = prompter->reply.data;
1408 password_data.length = prompter->reply.length;
1410 prompt.prompt = prompter->prompt;
1411 prompt.hidden = hx509_prompt_hidden(prompter->type);
1412 prompt.reply = &password_data;
1414 switch (prompter->type) {
1415 case HX509_PROMPT_TYPE_INFO:
1416 prompt.type = KRB5_PROMPT_TYPE_INFO;
1418 case HX509_PROMPT_TYPE_PASSWORD:
1419 case HX509_PROMPT_TYPE_QUESTION:
1421 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1425 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1427 memset (prompter->reply.data, 0, prompter->reply.length);
1434 void KRB5_LIB_FUNCTION
1435 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1438 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1442 krb5_error_code KRB5_LIB_FUNCTION
1443 _krb5_pk_load_id(krb5_context context,
1444 struct krb5_pk_identity **ret_id,
1445 const char *user_id,
1446 const char *anchor_id,
1447 char * const *chain_list,
1448 char * const *revoke_list,
1449 krb5_prompter_fct prompter,
1450 void *prompter_data,
1453 struct krb5_pk_identity *id = NULL;
1454 hx509_lock lock = NULL;
1460 if (anchor_id == NULL) {
1461 krb5_set_error_string(context, "PKINIT: No anchor given");
1462 return HEIM_PKINIT_NO_VALID_CA;
1465 if (user_id == NULL) {
1466 krb5_set_error_string(context,
1467 "PKINIT: No user certificate given");
1468 return HEIM_PKINIT_NO_PRIVATE_KEY;
1473 id = calloc(1, sizeof(*id));
1475 krb5_set_error_string(context, "malloc: out of memory");
1480 ret = hx509_context_init(&id->hx509ctx);
1484 ret = hx509_lock_init(id->hx509ctx, &lock);
1485 if (password && password[0])
1486 hx509_lock_add_password(lock, password);
1489 p.context = context;
1490 p.prompter = prompter;
1491 p.prompter_data = prompter_data;
1493 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1498 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1500 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1501 "Failed to init cert certs");
1505 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1507 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1508 "Failed to init anchors");
1512 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1513 0, NULL, &id->certpool);
1515 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1516 "Failed to init chain");
1520 while (chain_list && *chain_list) {
1521 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1524 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1525 "Failed to laod chain %s",
1533 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1535 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1536 "Failed init revoke list");
1540 while (*revoke_list) {
1541 ret = hx509_revoke_add_crl(id->hx509ctx,
1545 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1546 "Failed load revoke list");
1552 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1554 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1556 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1557 "Failed init verify context");
1561 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1562 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1566 hx509_verify_destroy_ctx(id->verify_ctx);
1567 hx509_certs_free(&id->certs);
1568 hx509_certs_free(&id->anchors);
1569 hx509_certs_free(&id->certpool);
1570 hx509_revoke_free(&id->revokectx);
1571 hx509_context_free(&id->hx509ctx);
1576 hx509_lock_free(lock);
1581 static krb5_error_code
1582 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1583 struct krb5_dh_moduli **moduli)
1585 const struct krb5_dh_moduli *m;
1588 m = moduli[1]; /* XXX */
1590 m = moduli[0]; /* XXX */
1593 for (i = 0; moduli[i] != NULL; i++) {
1594 if (bits < moduli[i]->bits)
1597 if (moduli[i] == NULL) {
1598 krb5_set_error_string(context,
1599 "Did not find a DH group parameter "
1600 "matching requirement of %lu bits",
1607 dh->p = integer_to_BN(context, "p", &m->p);
1610 dh->g = integer_to_BN(context, "g", &m->g);
1613 dh->q = integer_to_BN(context, "q", &m->q);
1623 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1624 const char *name, heim_integer *integer)
1628 p1 = strsep(p, " \t");
1630 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1631 file, name, lineno);
1634 ret = der_parse_hex_heim_integer(p1, integer);
1636 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1638 file, name, lineno);
1646 _krb5_parse_moduli_line(krb5_context context,
1650 struct krb5_dh_moduli **m)
1652 struct krb5_dh_moduli *m1;
1658 m1 = calloc(1, sizeof(*m1));
1660 krb5_set_error_string(context, "malloc - out of memory");
1664 while (isspace((unsigned char)*p))
1670 p1 = strsep(&p, " \t");
1672 krb5_set_error_string(context, "moduli file %s missing name "
1673 "on line %d", file, lineno);
1676 m1->name = strdup(p1);
1678 krb5_set_error_string(context, "malloc - out of memeory");
1683 p1 = strsep(&p, " \t");
1685 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1690 m1->bits = atoi(p1);
1691 if (m1->bits == 0) {
1692 krb5_set_error_string(context, "moduli file %s have un-parsable "
1693 "bits on line %d", file, lineno);
1697 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1700 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1703 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1712 der_free_heim_integer(&m1->p);
1713 der_free_heim_integer(&m1->g);
1714 der_free_heim_integer(&m1->q);
1720 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1723 for (i = 0; moduli[i] != NULL; i++) {
1724 free(moduli[i]->name);
1725 der_free_heim_integer(&moduli[i]->p);
1726 der_free_heim_integer(&moduli[i]->g);
1727 der_free_heim_integer(&moduli[i]->q);
1733 static const char *default_moduli =
1735 "RFC2412-MODP-group2 "
1739 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1740 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1741 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1742 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1743 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1744 "FFFFFFFF" "FFFFFFFF "
1748 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1749 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1750 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1751 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1752 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1753 "FFFFFFFF" "FFFFFFFF";
1757 _krb5_parse_moduli(krb5_context context, const char *file,
1758 struct krb5_dh_moduli ***moduli)
1760 /* name bits P G Q */
1761 krb5_error_code ret;
1762 struct krb5_dh_moduli **m = NULL, **m2;
1765 int lineno = 0, n = 0;
1769 m = calloc(1, sizeof(m[0]) * 2);
1771 krb5_set_error_string(context, "malloc: out of memory");
1775 strlcpy(buf, default_moduli, sizeof(buf));
1776 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1778 _krb5_free_moduli(m);
1786 f = fopen(file, "r");
1792 while(fgets(buf, sizeof(buf), f) != NULL) {
1793 struct krb5_dh_moduli *element;
1795 buf[strcspn(buf, "\n")] = '\0';
1798 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1800 krb5_set_error_string(context, "malloc: out of memory");
1801 _krb5_free_moduli(m);
1808 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1810 _krb5_free_moduli(m);
1813 if (element == NULL)
1825 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1826 heim_integer *p, heim_integer *g, heim_integer *q,
1827 struct krb5_dh_moduli **moduli,
1835 for (i = 0; moduli[i] != NULL; i++) {
1836 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1837 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1838 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1840 if (bits && bits > moduli[i]->bits) {
1841 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1842 "no accepted, not enough bits generated",
1844 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1847 *name = strdup(moduli[i]->name);
1851 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1852 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1855 void KRB5_LIB_FUNCTION
1856 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1859 krb5_pk_init_ctx ctx;
1861 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1863 ctx = opt->opt_private->pk_init_ctx;
1868 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1869 hx509_certs_free(&ctx->id->certs);
1870 hx509_certs_free(&ctx->id->anchors);
1871 hx509_certs_free(&ctx->id->certpool);
1872 hx509_context_free(&ctx->id->hx509ctx);
1874 if (ctx->clientDHNonce) {
1875 krb5_free_data(NULL, ctx->clientDHNonce);
1876 ctx->clientDHNonce = NULL;
1879 _krb5_free_moduli(ctx->m);
1883 free(opt->opt_private->pk_init_ctx);
1884 opt->opt_private->pk_init_ctx = NULL;
1888 krb5_error_code KRB5_LIB_FUNCTION
1889 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1890 krb5_get_init_creds_opt *opt,
1891 krb5_principal principal,
1892 const char *user_id,
1893 const char *x509_anchors,
1894 char * const * pool,
1895 char * const * pki_revoke,
1897 krb5_prompter_fct prompter,
1898 void *prompter_data,
1902 krb5_error_code ret;
1903 char *anchors = NULL;
1905 if (opt->opt_private == NULL) {
1906 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1910 opt->opt_private->pk_init_ctx =
1911 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1912 if (opt->opt_private->pk_init_ctx == NULL) {
1913 krb5_set_error_string(context, "malloc: out of memory");
1916 opt->opt_private->pk_init_ctx->dh = NULL;
1917 opt->opt_private->pk_init_ctx->id = NULL;
1918 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1919 opt->opt_private->pk_init_ctx->require_binding = 0;
1920 opt->opt_private->pk_init_ctx->require_eku = 1;
1921 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1922 opt->opt_private->pk_init_ctx->peer = NULL;
1924 /* XXX implement krb5_appdefault_strings */
1926 pool = krb5_config_get_strings(context, NULL,
1931 if (pki_revoke == NULL)
1932 pki_revoke = krb5_config_get_strings(context, NULL,
1937 if (x509_anchors == NULL) {
1938 krb5_appdefault_string(context, "kinit",
1939 krb5_principal_get_realm(context, principal),
1940 "pkinit_anchors", NULL, &anchors);
1941 x509_anchors = anchors;
1944 ret = _krb5_pk_load_id(context,
1945 &opt->opt_private->pk_init_ctx->id,
1954 free(opt->opt_private->pk_init_ctx);
1955 opt->opt_private->pk_init_ctx = NULL;
1959 if ((flags & 2) == 0) {
1960 const char *moduli_file;
1961 unsigned long dh_min_bits;
1963 moduli_file = krb5_config_get_string(context, NULL,
1969 krb5_config_get_int_default(context, NULL, 0,
1971 "pkinit_dh_min_bits",
1974 ret = _krb5_parse_moduli(context, moduli_file,
1975 &opt->opt_private->pk_init_ctx->m);
1977 _krb5_get_init_creds_opt_free_pkinit(opt);
1981 opt->opt_private->pk_init_ctx->dh = DH_new();
1982 if (opt->opt_private->pk_init_ctx->dh == NULL) {
1983 krb5_set_error_string(context, "malloc: out of memory");
1984 _krb5_get_init_creds_opt_free_pkinit(opt);
1988 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
1990 opt->opt_private->pk_init_ctx->m);
1992 _krb5_get_init_creds_opt_free_pkinit(opt);
1996 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
1997 krb5_set_error_string(context, "pkinit: failed to generate DH key");
1998 _krb5_get_init_creds_opt_free_pkinit(opt);
2005 krb5_set_error_string(context, "no support for PKINIT compiled in");
2015 _krb5_pk_copy_error(krb5_context context,
2016 hx509_context hx509ctx,
2025 vasprintf(&f, fmt, va);
2028 krb5_clear_error_string(context);
2032 s = hx509_get_error_string(hx509ctx, hxret);
2034 krb5_clear_error_string(context);
2038 krb5_set_error_string(context, "%s: %s", f, s);