use DES_set_key_unchecked().
[heimdal.git] / lib / krb5 / pkinit.c
blobecb28ffa382d98cbba19b931e5e9aae837e7835f
1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 RCSID("$Id$");
38 struct krb5_dh_moduli {
39 char *name;
40 unsigned long bits;
41 heim_integer p;
42 heim_integer g;
43 heim_integer q;
46 #ifdef PKINIT
48 #include <cms_asn1.h>
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
53 #include <asn1_err.h>
55 #include <der.h>
57 struct krb5_pk_cert {
58 hx509_cert cert;
61 struct krb5_pk_init_ctx_data {
62 struct krb5_pk_identity *id;
63 DH *dh;
64 krb5_data *clientDHNonce;
65 struct krb5_dh_moduli **m;
66 hx509_peer_info peer;
67 enum krb5_pk_type type;
68 unsigned int require_binding:1;
69 unsigned int require_eku:1;
70 unsigned int require_krbtgt_otherName:1;
71 unsigned int require_hostname_match:1;
72 unsigned int trustedCertifiers:1;
75 static void
76 pk_copy_error(krb5_context context,
77 hx509_context hx509ctx,
78 int hxret,
79 const char *fmt,
80 ...)
81 __attribute__ ((format (printf, 4, 5)));
87 void KRB5_LIB_FUNCTION
88 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
90 if (cert->cert) {
91 hx509_cert_free(cert->cert);
93 free(cert);
96 static krb5_error_code
97 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
99 integer->length = BN_num_bytes(bn);
100 integer->data = malloc(integer->length);
101 if (integer->data == NULL) {
102 krb5_clear_error_string(context);
103 return ENOMEM;
105 BN_bn2bin(bn, integer->data);
106 integer->negative = BN_is_negative(bn);
107 return 0;
110 static BIGNUM *
111 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
113 BIGNUM *bn;
115 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
116 if (bn == NULL) {
117 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
118 return NULL;
120 BN_set_negative(bn, f->negative);
121 return bn;
124 struct certfind {
125 const char *type;
126 const heim_oid *oid;
130 * Try searchin the key by to use by first looking for for PK-INIT
131 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
134 static krb5_error_code
135 find_cert(krb5_context context, struct krb5_pk_identity *id,
136 hx509_query *q, hx509_cert *cert)
138 struct certfind cf[3] = {
139 { "PKINIT EKU" },
140 { "MS EKU" },
141 { "no" }
143 int i, ret;
145 cf[0].oid = oid_id_pkekuoid();
146 cf[1].oid = oid_id_pkinit_ms_eku();
147 cf[2].oid = NULL;
149 for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
150 ret = hx509_query_match_eku(q, cf[i].oid);
151 if (ret) {
152 pk_copy_error(context, id->hx509ctx, ret,
153 "Failed setting %s OID", cf[i].type);
154 return ret;
157 ret = hx509_certs_find(id->hx509ctx, id->certs, q, cert);
158 if (ret == 0)
159 break;
160 pk_copy_error(context, id->hx509ctx, ret,
161 "Failed cert for finding %s OID", cf[i].type);
163 return ret;
167 static krb5_error_code
168 create_signature(krb5_context context,
169 const heim_oid *eContentType,
170 krb5_data *eContent,
171 struct krb5_pk_identity *id,
172 hx509_peer_info peer,
173 krb5_data *sd_data)
175 hx509_cert cert = NULL;
176 hx509_query *q = NULL;
177 int ret;
179 ret = hx509_query_alloc(id->hx509ctx, &q);
180 if (ret) {
181 pk_copy_error(context, id->hx509ctx, ret,
182 "Allocate query to find signing certificate");
183 return ret;
186 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
187 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
189 ret = find_cert(context, id, q, &cert);
190 hx509_query_free(id->hx509ctx, q);
191 if (ret)
192 return ret;
194 ret = hx509_cms_create_signed_1(id->hx509ctx,
196 eContentType,
197 eContent->data,
198 eContent->length,
199 NULL,
200 cert,
201 peer,
202 NULL,
203 id->certs,
204 sd_data);
205 hx509_cert_free(cert);
206 if (ret) {
207 pk_copy_error(context, id->hx509ctx, ret,
208 "Create CMS signedData");
209 return ret;
212 return 0;
215 static int
216 cert2epi(hx509_context context, void *ctx, hx509_cert c)
218 ExternalPrincipalIdentifiers *ids = ctx;
219 ExternalPrincipalIdentifier id;
220 hx509_name subject = NULL;
221 void *p;
222 int ret;
224 memset(&id, 0, sizeof(id));
226 ret = hx509_cert_get_subject(c, &subject);
227 if (ret)
228 return ret;
230 if (hx509_name_is_null_p(subject) != 0) {
232 id.subjectName = calloc(1, sizeof(*id.subjectName));
233 if (id.subjectName == NULL) {
234 hx509_name_free(&subject);
235 free_ExternalPrincipalIdentifier(&id);
236 return ENOMEM;
239 ret = hx509_name_binary(subject, id.subjectName);
240 if (ret) {
241 hx509_name_free(&subject);
242 free_ExternalPrincipalIdentifier(&id);
243 return ret;
246 hx509_name_free(&subject);
249 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
250 if (id.issuerAndSerialNumber == NULL) {
251 free_ExternalPrincipalIdentifier(&id);
252 return ENOMEM;
256 IssuerAndSerialNumber iasn;
257 hx509_name issuer;
258 size_t size;
260 memset(&iasn, 0, sizeof(iasn));
262 ret = hx509_cert_get_issuer(c, &issuer);
263 if (ret) {
264 free_ExternalPrincipalIdentifier(&id);
265 return ret;
268 ret = hx509_name_to_Name(issuer, &iasn.issuer);
269 hx509_name_free(&issuer);
270 if (ret) {
271 free_ExternalPrincipalIdentifier(&id);
272 return ret;
275 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
276 if (ret) {
277 free_IssuerAndSerialNumber(&iasn);
278 free_ExternalPrincipalIdentifier(&id);
279 return ret;
282 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
283 id.issuerAndSerialNumber->data,
284 id.issuerAndSerialNumber->length,
285 &iasn, &size, ret);
286 free_IssuerAndSerialNumber(&iasn);
287 if (ret)
288 return ret;
289 if (id.issuerAndSerialNumber->length != size)
290 abort();
293 id.subjectKeyIdentifier = NULL;
295 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
296 if (p == NULL) {
297 free_ExternalPrincipalIdentifier(&id);
298 return ENOMEM;
301 ids->val = p;
302 ids->val[ids->len] = id;
303 ids->len++;
305 return 0;
308 static krb5_error_code
309 build_edi(krb5_context context,
310 hx509_context hx509ctx,
311 hx509_certs certs,
312 ExternalPrincipalIdentifiers *ids)
314 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
317 static krb5_error_code
318 build_auth_pack(krb5_context context,
319 unsigned nonce,
320 krb5_pk_init_ctx ctx,
321 DH *dh,
322 const KDC_REQ_BODY *body,
323 AuthPack *a)
325 size_t buf_size, len;
326 krb5_error_code ret;
327 void *buf;
328 krb5_timestamp sec;
329 int32_t usec;
330 Checksum checksum;
332 krb5_clear_error_string(context);
334 memset(&checksum, 0, sizeof(checksum));
336 krb5_us_timeofday(context, &sec, &usec);
337 a->pkAuthenticator.ctime = sec;
338 a->pkAuthenticator.nonce = nonce;
340 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
341 if (ret)
342 return ret;
343 if (buf_size != len)
344 krb5_abortx(context, "internal error in ASN.1 encoder");
346 ret = krb5_create_checksum(context,
347 NULL,
349 CKSUMTYPE_SHA1,
350 buf,
351 len,
352 &checksum);
353 free(buf);
354 if (ret)
355 return ret;
357 ALLOC(a->pkAuthenticator.paChecksum, 1);
358 if (a->pkAuthenticator.paChecksum == NULL) {
359 krb5_set_error_string(context, "malloc: out of memory");
360 return ENOMEM;
363 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
364 checksum.checksum.data, checksum.checksum.length);
365 free_Checksum(&checksum);
366 if (ret)
367 return ret;
369 if (dh) {
370 DomainParameters dp;
371 heim_integer dh_pub_key;
372 krb5_data dhbuf;
373 size_t size;
375 if (1 /* support_cached_dh */) {
376 ALLOC(a->clientDHNonce, 1);
377 if (a->clientDHNonce == NULL) {
378 krb5_clear_error_string(context);
379 return ENOMEM;
381 ret = krb5_data_alloc(a->clientDHNonce, 40);
382 if (a->clientDHNonce == NULL) {
383 krb5_clear_error_string(context);
384 return ENOMEM;
386 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
387 ret = krb5_copy_data(context, a->clientDHNonce,
388 &ctx->clientDHNonce);
389 if (ret)
390 return ret;
393 ALLOC(a->clientPublicValue, 1);
394 if (a->clientPublicValue == NULL)
395 return ENOMEM;
396 ret = der_copy_oid(oid_id_dhpublicnumber(),
397 &a->clientPublicValue->algorithm.algorithm);
398 if (ret)
399 return ret;
401 memset(&dp, 0, sizeof(dp));
403 ret = BN_to_integer(context, dh->p, &dp.p);
404 if (ret) {
405 free_DomainParameters(&dp);
406 return ret;
408 ret = BN_to_integer(context, dh->g, &dp.g);
409 if (ret) {
410 free_DomainParameters(&dp);
411 return ret;
413 ret = BN_to_integer(context, dh->q, &dp.q);
414 if (ret) {
415 free_DomainParameters(&dp);
416 return ret;
418 dp.j = NULL;
419 dp.validationParms = NULL;
421 a->clientPublicValue->algorithm.parameters =
422 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
423 if (a->clientPublicValue->algorithm.parameters == NULL) {
424 free_DomainParameters(&dp);
425 return ret;
428 ASN1_MALLOC_ENCODE(DomainParameters,
429 a->clientPublicValue->algorithm.parameters->data,
430 a->clientPublicValue->algorithm.parameters->length,
431 &dp, &size, ret);
432 free_DomainParameters(&dp);
433 if (ret)
434 return ret;
435 if (size != a->clientPublicValue->algorithm.parameters->length)
436 krb5_abortx(context, "Internal ASN1 encoder error");
438 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
439 if (ret)
440 return ret;
442 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
443 &dh_pub_key, &size, ret);
444 der_free_heim_integer(&dh_pub_key);
445 if (ret)
446 return ret;
447 if (size != dhbuf.length)
448 krb5_abortx(context, "asn1 internal error");
450 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
451 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
455 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
456 if (a->supportedCMSTypes == NULL)
457 return ENOMEM;
459 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
460 &a->supportedCMSTypes->val,
461 &a->supportedCMSTypes->len);
462 if (ret)
463 return ret;
466 return ret;
469 krb5_error_code KRB5_LIB_FUNCTION
470 _krb5_pk_mk_ContentInfo(krb5_context context,
471 const krb5_data *buf,
472 const heim_oid *oid,
473 struct ContentInfo *content_info)
475 krb5_error_code ret;
477 ret = der_copy_oid(oid, &content_info->contentType);
478 if (ret)
479 return ret;
480 ALLOC(content_info->content, 1);
481 if (content_info->content == NULL)
482 return ENOMEM;
483 content_info->content->data = malloc(buf->length);
484 if (content_info->content->data == NULL)
485 return ENOMEM;
486 memcpy(content_info->content->data, buf->data, buf->length);
487 content_info->content->length = buf->length;
488 return 0;
491 static krb5_error_code
492 pk_mk_padata(krb5_context context,
493 krb5_pk_init_ctx ctx,
494 const KDC_REQ_BODY *req_body,
495 unsigned nonce,
496 METHOD_DATA *md)
498 struct ContentInfo content_info;
499 krb5_error_code ret;
500 const heim_oid *oid;
501 size_t size;
502 krb5_data buf, sd_buf;
503 int pa_type;
505 krb5_data_zero(&buf);
506 krb5_data_zero(&sd_buf);
507 memset(&content_info, 0, sizeof(content_info));
509 if (ctx->type == PKINIT_WIN2K) {
510 AuthPack_Win2k ap;
511 krb5_timestamp sec;
512 int32_t usec;
514 memset(&ap, 0, sizeof(ap));
516 /* fill in PKAuthenticator */
517 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
518 if (ret) {
519 free_AuthPack_Win2k(&ap);
520 krb5_clear_error_string(context);
521 goto out;
523 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
524 if (ret) {
525 free_AuthPack_Win2k(&ap);
526 krb5_clear_error_string(context);
527 goto out;
530 krb5_us_timeofday(context, &sec, &usec);
531 ap.pkAuthenticator.ctime = sec;
532 ap.pkAuthenticator.cusec = usec;
533 ap.pkAuthenticator.nonce = nonce;
535 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
536 &ap, &size, ret);
537 free_AuthPack_Win2k(&ap);
538 if (ret) {
539 krb5_set_error_string(context, "AuthPack_Win2k: %d",
540 (int)ret);
541 goto out;
543 if (buf.length != size)
544 krb5_abortx(context, "internal ASN1 encoder error");
546 oid = oid_id_pkcs7_data();
547 } else if (ctx->type == PKINIT_27) {
548 AuthPack ap;
550 memset(&ap, 0, sizeof(ap));
552 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
553 if (ret) {
554 free_AuthPack(&ap);
555 goto out;
558 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
559 free_AuthPack(&ap);
560 if (ret) {
561 krb5_set_error_string(context, "AuthPack: %d", (int)ret);
562 goto out;
564 if (buf.length != size)
565 krb5_abortx(context, "internal ASN1 encoder error");
567 oid = oid_id_pkauthdata();
568 } else
569 krb5_abortx(context, "internal pkinit error");
571 ret = create_signature(context, oid, &buf, ctx->id,
572 ctx->peer, &sd_buf);
573 krb5_data_free(&buf);
574 if (ret)
575 goto out;
577 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
578 krb5_data_free(&sd_buf);
579 if (ret) {
580 krb5_set_error_string(context,
581 "ContentInfo wrapping of signedData failed");
582 goto out;
585 if (ctx->type == PKINIT_WIN2K) {
586 PA_PK_AS_REQ_Win2k winreq;
588 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
590 memset(&winreq, 0, sizeof(winreq));
592 winreq.signed_auth_pack = buf;
594 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
595 &winreq, &size, ret);
596 free_PA_PK_AS_REQ_Win2k(&winreq);
598 } else if (ctx->type == PKINIT_27) {
599 PA_PK_AS_REQ req;
601 pa_type = KRB5_PADATA_PK_AS_REQ;
603 memset(&req, 0, sizeof(req));
604 req.signedAuthPack = buf;
606 if (ctx->trustedCertifiers) {
608 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
609 if (req.trustedCertifiers == NULL) {
610 krb5_set_error_string(context, "malloc: out of memory");
611 free_PA_PK_AS_REQ(&req);
612 goto out;
614 ret = build_edi(context, ctx->id->hx509ctx,
615 ctx->id->anchors, req.trustedCertifiers);
616 if (ret) {
617 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
618 free_PA_PK_AS_REQ(&req);
619 goto out;
622 req.kdcPkId = NULL;
624 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
625 &req, &size, ret);
627 free_PA_PK_AS_REQ(&req);
629 } else
630 krb5_abortx(context, "internal pkinit error");
631 if (ret) {
632 krb5_set_error_string(context, "PA-PK-AS-REQ %d", (int)ret);
633 goto out;
635 if (buf.length != size)
636 krb5_abortx(context, "Internal ASN1 encoder error");
638 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
639 if (ret)
640 free(buf.data);
642 if (ret == 0 && ctx->type == PKINIT_WIN2K)
643 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
645 out:
646 free_ContentInfo(&content_info);
648 return ret;
652 krb5_error_code KRB5_LIB_FUNCTION
653 _krb5_pk_mk_padata(krb5_context context,
654 void *c,
655 const KDC_REQ_BODY *req_body,
656 unsigned nonce,
657 METHOD_DATA *md)
659 krb5_pk_init_ctx ctx = c;
660 int win2k_compat;
662 win2k_compat = krb5_config_get_bool_default(context, NULL,
663 FALSE,
664 "realms",
665 req_body->realm,
666 "pkinit_win2k",
667 NULL);
669 if (win2k_compat) {
670 ctx->require_binding =
671 krb5_config_get_bool_default(context, NULL,
672 FALSE,
673 "realms",
674 req_body->realm,
675 "pkinit_win2k_require_binding",
676 NULL);
677 ctx->type = PKINIT_WIN2K;
678 } else
679 ctx->type = PKINIT_27;
681 ctx->require_eku =
682 krb5_config_get_bool_default(context, NULL,
683 TRUE,
684 "realms",
685 req_body->realm,
686 "pkinit_require_eku",
687 NULL);
688 ctx->require_krbtgt_otherName =
689 krb5_config_get_bool_default(context, NULL,
690 TRUE,
691 "realms",
692 req_body->realm,
693 "pkinit_require_krbtgt_otherName",
694 NULL);
696 ctx->require_hostname_match =
697 krb5_config_get_bool_default(context, NULL,
698 FALSE,
699 "realms",
700 req_body->realm,
701 "pkinit_require_hostname_match",
702 NULL);
704 ctx->trustedCertifiers =
705 krb5_config_get_bool_default(context, NULL,
706 TRUE,
707 "realms",
708 req_body->realm,
709 "pkinit_trustedCertifiers",
710 NULL);
712 return pk_mk_padata(context, ctx, req_body, nonce, md);
715 krb5_error_code KRB5_LIB_FUNCTION
716 _krb5_pk_verify_sign(krb5_context context,
717 const void *data,
718 size_t length,
719 struct krb5_pk_identity *id,
720 heim_oid *contentType,
721 krb5_data *content,
722 struct krb5_pk_cert **signer)
724 hx509_certs signer_certs;
725 int ret;
727 *signer = NULL;
729 ret = hx509_cms_verify_signed(id->hx509ctx,
730 id->verify_ctx,
731 data,
732 length,
733 NULL,
734 id->certpool,
735 contentType,
736 content,
737 &signer_certs);
738 if (ret) {
739 pk_copy_error(context, id->hx509ctx, ret,
740 "CMS verify signed failed");
741 return ret;
744 *signer = calloc(1, sizeof(**signer));
745 if (*signer == NULL) {
746 krb5_clear_error_string(context);
747 ret = ENOMEM;
748 goto out;
751 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
752 if (ret) {
753 pk_copy_error(context, id->hx509ctx, ret,
754 "Failed to get on of the signer certs");
755 goto out;
758 out:
759 hx509_certs_free(&signer_certs);
760 if (ret) {
761 if (*signer) {
762 hx509_cert_free((*signer)->cert);
763 free(*signer);
764 *signer = NULL;
768 return ret;
771 static krb5_error_code
772 get_reply_key_win(krb5_context context,
773 const krb5_data *content,
774 unsigned nonce,
775 krb5_keyblock **key)
777 ReplyKeyPack_Win2k key_pack;
778 krb5_error_code ret;
779 size_t size;
781 ret = decode_ReplyKeyPack_Win2k(content->data,
782 content->length,
783 &key_pack,
784 &size);
785 if (ret) {
786 krb5_set_error_string(context, "PKINIT decoding reply key failed");
787 free_ReplyKeyPack_Win2k(&key_pack);
788 return ret;
791 if (key_pack.nonce != nonce) {
792 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
793 free_ReplyKeyPack_Win2k(&key_pack);
794 return KRB5KRB_AP_ERR_MODIFIED;
797 *key = malloc (sizeof (**key));
798 if (*key == NULL) {
799 krb5_set_error_string(context, "PKINIT failed allocating reply key");
800 free_ReplyKeyPack_Win2k(&key_pack);
801 krb5_set_error_string(context, "malloc: out of memory");
802 return ENOMEM;
805 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
806 free_ReplyKeyPack_Win2k(&key_pack);
807 if (ret) {
808 krb5_set_error_string(context, "PKINIT failed copying reply key");
809 free(*key);
810 *key = NULL;
813 return ret;
816 static krb5_error_code
817 get_reply_key(krb5_context context,
818 const krb5_data *content,
819 const krb5_data *req_buffer,
820 krb5_keyblock **key)
822 ReplyKeyPack key_pack;
823 krb5_error_code ret;
824 size_t size;
826 ret = decode_ReplyKeyPack(content->data,
827 content->length,
828 &key_pack,
829 &size);
830 if (ret) {
831 krb5_set_error_string(context, "PKINIT decoding reply key failed");
832 free_ReplyKeyPack(&key_pack);
833 return ret;
837 krb5_crypto crypto;
840 * XXX Verify kp.replyKey is a allowed enctype in the
841 * configuration file
844 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
845 if (ret) {
846 free_ReplyKeyPack(&key_pack);
847 return ret;
850 ret = krb5_verify_checksum(context, crypto, 6,
851 req_buffer->data, req_buffer->length,
852 &key_pack.asChecksum);
853 krb5_crypto_destroy(context, crypto);
854 if (ret) {
855 free_ReplyKeyPack(&key_pack);
856 return ret;
860 *key = malloc (sizeof (**key));
861 if (*key == NULL) {
862 krb5_set_error_string(context, "PKINIT failed allocating reply key");
863 free_ReplyKeyPack(&key_pack);
864 krb5_set_error_string(context, "malloc: out of memory");
865 return ENOMEM;
868 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
869 free_ReplyKeyPack(&key_pack);
870 if (ret) {
871 krb5_set_error_string(context, "PKINIT failed copying reply key");
872 free(*key);
873 *key = NULL;
876 return ret;
880 static krb5_error_code
881 pk_verify_host(krb5_context context,
882 const char *realm,
883 const krb5_krbhst_info *hi,
884 struct krb5_pk_init_ctx_data *ctx,
885 struct krb5_pk_cert *host)
887 krb5_error_code ret = 0;
889 if (ctx->require_eku) {
890 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
891 oid_id_pkkdcekuoid(), 0);
892 if (ret) {
893 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
894 return ret;
897 if (ctx->require_krbtgt_otherName) {
898 hx509_octet_string_list list;
899 int i;
901 ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
902 host->cert,
903 oid_id_pkinit_san(),
904 &list);
905 if (ret) {
906 krb5_set_error_string(context, "Failed to find the PK-INIT "
907 "subjectAltName in the KDC certificate");
909 return ret;
912 for (i = 0; i < list.len; i++) {
913 KRB5PrincipalName r;
915 ret = decode_KRB5PrincipalName(list.val[i].data,
916 list.val[i].length,
918 NULL);
919 if (ret) {
920 krb5_set_error_string(context, "Failed to decode the PK-INIT "
921 "subjectAltName in the KDC certificate");
923 break;
926 if (r.principalName.name_string.len != 2 ||
927 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
928 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
929 strcmp(r.realm, realm) != 0)
931 krb5_set_error_string(context, "KDC have wrong realm name in "
932 "the certificate");
933 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
936 free_KRB5PrincipalName(&r);
937 if (ret)
938 break;
940 hx509_free_octet_string_list(&list);
942 if (ret)
943 return ret;
945 if (hi) {
946 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
947 ctx->require_hostname_match,
948 HX509_HN_HOSTNAME,
949 hi->hostname,
950 hi->ai->ai_addr, hi->ai->ai_addrlen);
952 if (ret)
953 krb5_set_error_string(context, "Address mismatch in "
954 "the KDC certificate");
956 return ret;
959 static krb5_error_code
960 pk_rd_pa_reply_enckey(krb5_context context,
961 int type,
962 const heim_octet_string *indata,
963 const heim_oid *dataType,
964 const char *realm,
965 krb5_pk_init_ctx ctx,
966 krb5_enctype etype,
967 const krb5_krbhst_info *hi,
968 unsigned nonce,
969 const krb5_data *req_buffer,
970 PA_DATA *pa,
971 krb5_keyblock **key)
973 krb5_error_code ret;
974 struct krb5_pk_cert *host = NULL;
975 krb5_data content;
976 heim_oid contentType = { 0, NULL };
978 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
979 krb5_set_error_string(context, "PKINIT: Invalid content type");
980 return EINVAL;
983 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
984 ctx->id->certs,
985 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
986 indata->data,
987 indata->length,
988 NULL,
989 &contentType,
990 &content);
991 if (ret) {
992 pk_copy_error(context, ctx->id->hx509ctx, ret,
993 "Failed to unenvelope CMS data in PK-INIT reply");
994 return ret;
996 der_free_oid(&contentType);
998 #if 0 /* windows LH with interesting CMS packets, leaks memory */
1000 size_t ph = 1 + der_length_len (length);
1001 unsigned char *ptr = malloc(length + ph);
1002 size_t l;
1004 memcpy(ptr + ph, p, length);
1006 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
1007 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1008 if (ret)
1009 return ret;
1010 ptr += ph - l;
1011 length += l;
1012 p = ptr;
1014 #endif
1016 /* win2k uses ContentInfo */
1017 if (type == PKINIT_WIN2K) {
1018 heim_oid type;
1019 heim_octet_string out;
1021 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1022 if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1023 ret = EINVAL; /* XXX */
1024 krb5_set_error_string(context, "PKINIT: Invalid content type");
1025 der_free_oid(&type);
1026 der_free_octet_string(&out);
1027 goto out;
1029 der_free_oid(&type);
1030 krb5_data_free(&content);
1031 ret = krb5_data_copy(&content, out.data, out.length);
1032 der_free_octet_string(&out);
1033 if (ret) {
1034 krb5_set_error_string(context, "PKINIT: out of memory");
1035 goto out;
1039 ret = _krb5_pk_verify_sign(context,
1040 content.data,
1041 content.length,
1042 ctx->id,
1043 &contentType,
1044 &content,
1045 &host);
1046 if (ret)
1047 goto out;
1049 /* make sure that it is the kdc's certificate */
1050 ret = pk_verify_host(context, realm, hi, ctx, host);
1051 if (ret) {
1052 goto out;
1055 #if 0
1056 if (type == PKINIT_WIN2K) {
1057 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1058 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1059 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1060 goto out;
1062 } else {
1063 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1064 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1065 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1066 goto out;
1069 #endif
1071 switch(type) {
1072 case PKINIT_WIN2K:
1073 ret = get_reply_key(context, &content, req_buffer, key);
1074 if (ret != 0 && ctx->require_binding == 0)
1075 ret = get_reply_key_win(context, &content, nonce, key);
1076 break;
1077 case PKINIT_27:
1078 ret = get_reply_key(context, &content, req_buffer, key);
1079 break;
1081 if (ret)
1082 goto out;
1084 /* XXX compare given etype with key->etype */
1086 out:
1087 if (host)
1088 _krb5_pk_cert_free(host);
1089 der_free_oid(&contentType);
1090 krb5_data_free(&content);
1092 return ret;
1095 static krb5_error_code
1096 pk_rd_pa_reply_dh(krb5_context context,
1097 const heim_octet_string *indata,
1098 const heim_oid *dataType,
1099 const char *realm,
1100 krb5_pk_init_ctx ctx,
1101 krb5_enctype etype,
1102 const krb5_krbhst_info *hi,
1103 const DHNonce *c_n,
1104 const DHNonce *k_n,
1105 unsigned nonce,
1106 PA_DATA *pa,
1107 krb5_keyblock **key)
1109 unsigned char *p, *dh_gen_key = NULL;
1110 struct krb5_pk_cert *host = NULL;
1111 BIGNUM *kdc_dh_pubkey = NULL;
1112 KDCDHKeyInfo kdc_dh_info;
1113 heim_oid contentType = { 0, NULL };
1114 krb5_data content;
1115 krb5_error_code ret;
1116 int dh_gen_keylen;
1117 size_t size;
1119 krb5_data_zero(&content);
1120 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1122 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1123 krb5_set_error_string(context, "PKINIT: Invalid content type");
1124 return EINVAL;
1127 ret = _krb5_pk_verify_sign(context,
1128 indata->data,
1129 indata->length,
1130 ctx->id,
1131 &contentType,
1132 &content,
1133 &host);
1134 if (ret)
1135 goto out;
1137 /* make sure that it is the kdc's certificate */
1138 ret = pk_verify_host(context, realm, hi, ctx, host);
1139 if (ret)
1140 goto out;
1142 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1143 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1144 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1145 goto out;
1148 ret = decode_KDCDHKeyInfo(content.data,
1149 content.length,
1150 &kdc_dh_info,
1151 &size);
1153 if (ret) {
1154 krb5_set_error_string(context, "pkinit - "
1155 "failed to decode KDC DH Key Info");
1156 goto out;
1159 if (kdc_dh_info.nonce != nonce) {
1160 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1161 ret = KRB5KRB_AP_ERR_MODIFIED;
1162 goto out;
1165 if (kdc_dh_info.dhKeyExpiration) {
1166 if (k_n == NULL) {
1167 krb5_set_error_string(context, "pkinit; got key expiration "
1168 "without server nonce");
1169 ret = KRB5KRB_ERR_GENERIC;
1170 goto out;
1172 if (c_n == NULL) {
1173 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1174 "client nonce");
1175 ret = KRB5KRB_ERR_GENERIC;
1176 goto out;
1178 } else {
1179 if (k_n) {
1180 krb5_set_error_string(context, "pkinit: got server nonce "
1181 "without key expiration");
1182 ret = KRB5KRB_ERR_GENERIC;
1183 goto out;
1185 c_n = NULL;
1189 p = kdc_dh_info.subjectPublicKey.data;
1190 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1193 DHPublicKey k;
1194 ret = decode_DHPublicKey(p, size, &k, NULL);
1195 if (ret) {
1196 krb5_set_error_string(context, "pkinit: can't decode "
1197 "without key expiration");
1198 goto out;
1201 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1202 free_DHPublicKey(&k);
1203 if (kdc_dh_pubkey == NULL) {
1204 ret = KRB5KRB_ERR_GENERIC;
1205 goto out;
1209 dh_gen_keylen = DH_size(ctx->dh);
1210 size = BN_num_bytes(ctx->dh->p);
1211 if (size < dh_gen_keylen)
1212 size = dh_gen_keylen;
1214 dh_gen_key = malloc(size);
1215 if (dh_gen_key == NULL) {
1216 krb5_set_error_string(context, "malloc: out of memory");
1217 ret = ENOMEM;
1218 goto out;
1220 memset(dh_gen_key, 0, size - dh_gen_keylen);
1222 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1223 kdc_dh_pubkey, ctx->dh);
1224 if (dh_gen_keylen == -1) {
1225 krb5_set_error_string(context,
1226 "PKINIT: Can't compute Diffie-Hellman key");
1227 ret = KRB5KRB_ERR_GENERIC;
1228 goto out;
1231 *key = malloc (sizeof (**key));
1232 if (*key == NULL) {
1233 krb5_set_error_string(context, "malloc: out of memory");
1234 ret = ENOMEM;
1235 goto out;
1238 ret = _krb5_pk_octetstring2key(context,
1239 etype,
1240 dh_gen_key, dh_gen_keylen,
1241 c_n, k_n,
1242 *key);
1243 if (ret) {
1244 krb5_set_error_string(context,
1245 "PKINIT: can't create key from DH key");
1246 free(*key);
1247 *key = NULL;
1248 goto out;
1251 out:
1252 if (kdc_dh_pubkey)
1253 BN_free(kdc_dh_pubkey);
1254 if (dh_gen_key) {
1255 memset(dh_gen_key, 0, DH_size(ctx->dh));
1256 free(dh_gen_key);
1258 if (host)
1259 _krb5_pk_cert_free(host);
1260 if (content.data)
1261 krb5_data_free(&content);
1262 der_free_oid(&contentType);
1263 free_KDCDHKeyInfo(&kdc_dh_info);
1265 return ret;
1268 krb5_error_code KRB5_LIB_FUNCTION
1269 _krb5_pk_rd_pa_reply(krb5_context context,
1270 const char *realm,
1271 void *c,
1272 krb5_enctype etype,
1273 const krb5_krbhst_info *hi,
1274 unsigned nonce,
1275 const krb5_data *req_buffer,
1276 PA_DATA *pa,
1277 krb5_keyblock **key)
1279 krb5_pk_init_ctx ctx = c;
1280 krb5_error_code ret;
1281 size_t size;
1283 /* Check for IETF PK-INIT first */
1284 if (ctx->type == PKINIT_27) {
1285 PA_PK_AS_REP rep;
1286 heim_octet_string os, data;
1287 heim_oid oid;
1289 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1290 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1291 return EINVAL;
1294 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1295 pa->padata_value.length,
1296 &rep,
1297 &size);
1298 if (ret) {
1299 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1300 return ret;
1303 switch (rep.element) {
1304 case choice_PA_PK_AS_REP_dhInfo:
1305 os = rep.u.dhInfo.dhSignedData;
1306 break;
1307 case choice_PA_PK_AS_REP_encKeyPack:
1308 os = rep.u.encKeyPack;
1309 break;
1310 default:
1311 free_PA_PK_AS_REP(&rep);
1312 krb5_set_error_string(context, "PKINIT: -27 reply "
1313 "invalid content type");
1314 return EINVAL;
1317 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1318 if (ret) {
1319 free_PA_PK_AS_REP(&rep);
1320 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1321 return ret;
1324 switch (rep.element) {
1325 case choice_PA_PK_AS_REP_dhInfo:
1326 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1327 ctx->clientDHNonce,
1328 rep.u.dhInfo.serverDHNonce,
1329 nonce, pa, key);
1330 break;
1331 case choice_PA_PK_AS_REP_encKeyPack:
1332 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1333 ctx, etype, hi, nonce, req_buffer, pa, key);
1334 break;
1335 default:
1336 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1338 der_free_octet_string(&data);
1339 der_free_oid(&oid);
1340 free_PA_PK_AS_REP(&rep);
1342 } else if (ctx->type == PKINIT_WIN2K) {
1343 PA_PK_AS_REP_Win2k w2krep;
1345 /* Check for Windows encoding of the AS-REP pa data */
1347 #if 0 /* should this be ? */
1348 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1349 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1350 return EINVAL;
1352 #endif
1354 memset(&w2krep, 0, sizeof(w2krep));
1356 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1357 pa->padata_value.length,
1358 &w2krep,
1359 &size);
1360 if (ret) {
1361 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1362 "pkinit reply %d", (int)ret);
1363 return ret;
1366 krb5_clear_error_string(context);
1368 switch (w2krep.element) {
1369 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1370 heim_octet_string data;
1371 heim_oid oid;
1373 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1374 &oid, &data, NULL);
1375 free_PA_PK_AS_REP_Win2k(&w2krep);
1376 if (ret) {
1377 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1378 return ret;
1381 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1382 ctx, etype, hi, nonce, req_buffer, pa, key);
1383 der_free_octet_string(&data);
1384 der_free_oid(&oid);
1386 break;
1388 default:
1389 free_PA_PK_AS_REP_Win2k(&w2krep);
1390 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1391 "content type");
1392 ret = EINVAL;
1393 break;
1396 } else {
1397 krb5_set_error_string(context, "PKINIT: unknown reply type");
1398 ret = EINVAL;
1401 return ret;
1404 struct prompter {
1405 krb5_context context;
1406 krb5_prompter_fct prompter;
1407 void *prompter_data;
1410 static int
1411 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1413 krb5_error_code ret;
1414 krb5_prompt prompt;
1415 krb5_data password_data;
1416 struct prompter *p = data;
1418 password_data.data = prompter->reply.data;
1419 password_data.length = prompter->reply.length;
1421 prompt.prompt = prompter->prompt;
1422 prompt.hidden = hx509_prompt_hidden(prompter->type);
1423 prompt.reply = &password_data;
1425 switch (prompter->type) {
1426 case HX509_PROMPT_TYPE_INFO:
1427 prompt.type = KRB5_PROMPT_TYPE_INFO;
1428 break;
1429 case HX509_PROMPT_TYPE_PASSWORD:
1430 case HX509_PROMPT_TYPE_QUESTION:
1431 default:
1432 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1433 break;
1436 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1437 if (ret) {
1438 memset (prompter->reply.data, 0, prompter->reply.length);
1439 return 1;
1441 return 0;
1445 void KRB5_LIB_FUNCTION
1446 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1447 int boolean)
1449 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1453 krb5_error_code KRB5_LIB_FUNCTION
1454 _krb5_pk_load_id(krb5_context context,
1455 struct krb5_pk_identity **ret_id,
1456 const char *user_id,
1457 const char *anchor_id,
1458 char * const *chain_list,
1459 char * const *revoke_list,
1460 krb5_prompter_fct prompter,
1461 void *prompter_data,
1462 char *password)
1464 struct krb5_pk_identity *id = NULL;
1465 hx509_lock lock = NULL;
1466 struct prompter p;
1467 int ret;
1469 *ret_id = NULL;
1471 if (anchor_id == NULL) {
1472 krb5_set_error_string(context, "PKINIT: No anchor given");
1473 return HEIM_PKINIT_NO_VALID_CA;
1476 if (user_id == NULL) {
1477 krb5_set_error_string(context,
1478 "PKINIT: No user certificate given");
1479 return HEIM_PKINIT_NO_PRIVATE_KEY;
1482 /* load cert */
1484 id = calloc(1, sizeof(*id));
1485 if (id == NULL) {
1486 krb5_set_error_string(context, "malloc: out of memory");
1487 return ENOMEM;
1490 ret = hx509_context_init(&id->hx509ctx);
1491 if (ret)
1492 goto out;
1494 ret = hx509_lock_init(id->hx509ctx, &lock);
1495 if (password && password[0])
1496 hx509_lock_add_password(lock, password);
1498 if (prompter) {
1499 p.context = context;
1500 p.prompter = prompter;
1501 p.prompter_data = prompter_data;
1503 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1504 if (ret)
1505 goto out;
1508 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1509 if (ret) {
1510 pk_copy_error(context, id->hx509ctx, ret,
1511 "Failed to init cert certs");
1512 goto out;
1515 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1516 if (ret) {
1517 pk_copy_error(context, id->hx509ctx, ret,
1518 "Failed to init anchors");
1519 goto out;
1522 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1523 0, NULL, &id->certpool);
1524 if (ret) {
1525 pk_copy_error(context, id->hx509ctx, ret,
1526 "Failed to init chain");
1527 goto out;
1530 while (chain_list && *chain_list) {
1531 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1532 NULL, *chain_list);
1533 if (ret) {
1534 pk_copy_error(context, id->hx509ctx, ret,
1535 "Failed to laod chain %s",
1536 *chain_list);
1537 goto out;
1539 chain_list++;
1542 if (revoke_list) {
1543 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1544 if (ret) {
1545 pk_copy_error(context, id->hx509ctx, ret,
1546 "Failed init revoke list");
1547 goto out;
1550 while (*revoke_list) {
1551 ret = hx509_revoke_add_crl(id->hx509ctx,
1552 id->revokectx,
1553 *revoke_list);
1554 if (ret) {
1555 pk_copy_error(context, id->hx509ctx, ret,
1556 "Failed load revoke list");
1557 goto out;
1559 revoke_list++;
1561 } else
1562 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1564 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1565 if (ret) {
1566 pk_copy_error(context, id->hx509ctx, ret,
1567 "Failed init verify context");
1568 goto out;
1571 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1572 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1574 out:
1575 if (ret) {
1576 hx509_verify_destroy_ctx(id->verify_ctx);
1577 hx509_certs_free(&id->certs);
1578 hx509_certs_free(&id->anchors);
1579 hx509_certs_free(&id->certpool);
1580 hx509_revoke_free(&id->revokectx);
1581 hx509_context_free(&id->hx509ctx);
1582 free(id);
1583 } else
1584 *ret_id = id;
1586 hx509_lock_free(lock);
1588 return ret;
1591 static krb5_error_code
1592 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1593 struct krb5_dh_moduli **moduli)
1595 const struct krb5_dh_moduli *m;
1597 if (bits == 0) {
1598 m = moduli[1]; /* XXX */
1599 if (m == NULL)
1600 m = moduli[0]; /* XXX */
1601 } else {
1602 int i;
1603 for (i = 0; moduli[i] != NULL; i++) {
1604 if (bits < moduli[i]->bits)
1605 break;
1607 if (moduli[i] == NULL) {
1608 krb5_set_error_string(context,
1609 "Did not find a DH group parameter "
1610 "matching requirement of %lu bits",
1611 bits);
1612 return EINVAL;
1614 m = moduli[i];
1617 dh->p = integer_to_BN(context, "p", &m->p);
1618 if (dh->p == NULL)
1619 return ENOMEM;
1620 dh->g = integer_to_BN(context, "g", &m->g);
1621 if (dh->g == NULL)
1622 return ENOMEM;
1623 dh->q = integer_to_BN(context, "q", &m->q);
1624 if (dh->q == NULL)
1625 return ENOMEM;
1627 return 0;
1634 static void
1635 pk_copy_error(krb5_context context,
1636 hx509_context hx509ctx,
1637 int hxret,
1638 const char *fmt,
1639 ...)
1641 va_list va;
1642 char *s, *f;
1644 va_start(va, fmt);
1645 vasprintf(&f, fmt, va);
1646 va_end(va);
1647 if (f == NULL) {
1648 krb5_clear_error_string(context);
1649 return;
1652 s = hx509_get_error_string(hx509ctx, hxret);
1653 if (s == NULL) {
1654 krb5_clear_error_string(context);
1655 free(f);
1656 return;
1658 krb5_set_error_string(context, "%s: %s", f, s);
1659 free(s);
1660 free(f);
1663 #endif /* PKINIT */
1665 static int
1666 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1667 const char *name, heim_integer *integer)
1669 int ret;
1670 char *p1;
1671 p1 = strsep(p, " \t");
1672 if (p1 == NULL) {
1673 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1674 file, name, lineno);
1675 return EINVAL;
1677 ret = der_parse_hex_heim_integer(p1, integer);
1678 if (ret) {
1679 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1680 "on line %d",
1681 file, name, lineno);
1682 return ret;
1685 return 0;
1688 krb5_error_code
1689 _krb5_parse_moduli_line(krb5_context context,
1690 const char *file,
1691 int lineno,
1692 char *p,
1693 struct krb5_dh_moduli **m)
1695 struct krb5_dh_moduli *m1;
1696 char *p1;
1697 int ret;
1699 *m = NULL;
1701 m1 = calloc(1, sizeof(*m1));
1702 if (m1 == NULL) {
1703 krb5_set_error_string(context, "malloc - out of memory");
1704 return ENOMEM;
1707 while (isspace((unsigned char)*p))
1708 p++;
1709 if (*p == '#')
1710 return 0;
1711 ret = EINVAL;
1713 p1 = strsep(&p, " \t");
1714 if (p1 == NULL) {
1715 krb5_set_error_string(context, "moduli file %s missing name "
1716 "on line %d", file, lineno);
1717 goto out;
1719 m1->name = strdup(p1);
1720 if (p1 == NULL) {
1721 krb5_set_error_string(context, "malloc - out of memeory");
1722 ret = ENOMEM;
1723 goto out;
1726 p1 = strsep(&p, " \t");
1727 if (p1 == NULL) {
1728 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1729 file, lineno);
1730 goto out;
1733 m1->bits = atoi(p1);
1734 if (m1->bits == 0) {
1735 krb5_set_error_string(context, "moduli file %s have un-parsable "
1736 "bits on line %d", file, lineno);
1737 goto out;
1740 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1741 if (ret)
1742 goto out;
1743 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1744 if (ret)
1745 goto out;
1746 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1747 if (ret)
1748 goto out;
1750 *m = m1;
1752 return 0;
1753 out:
1754 free(m1->name);
1755 der_free_heim_integer(&m1->p);
1756 der_free_heim_integer(&m1->g);
1757 der_free_heim_integer(&m1->q);
1758 free(m1);
1759 return ret;
1762 void
1763 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1765 int i;
1766 for (i = 0; moduli[i] != NULL; i++) {
1767 free(moduli[i]->name);
1768 der_free_heim_integer(&moduli[i]->p);
1769 der_free_heim_integer(&moduli[i]->g);
1770 der_free_heim_integer(&moduli[i]->q);
1771 free(moduli[i]);
1773 free(moduli);
1776 static const char *default_moduli_RFC2412_MODP_group2 =
1777 /* name */
1778 "RFC2412-MODP-group2 "
1779 /* bits */
1780 "1024 "
1781 /* p */
1782 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1783 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1784 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1785 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1786 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1787 "FFFFFFFF" "FFFFFFFF "
1788 /* g */
1789 "02 "
1790 /* q */
1791 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1792 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1793 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1794 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1795 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1796 "FFFFFFFF" "FFFFFFFF";
1798 static const char *default_moduli_rfc3526_MODP_group14 =
1799 /* name */
1800 "rfc3526-MODP-group14 "
1801 /* bits */
1802 "1760 "
1803 /* p */
1804 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1805 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1806 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1807 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1808 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1809 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1810 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1811 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1812 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1813 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1814 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1815 /* g */
1816 "02 "
1817 /* q */
1818 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1819 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1820 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1821 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1822 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1823 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1824 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1825 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1826 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1827 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1828 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1830 krb5_error_code
1831 _krb5_parse_moduli(krb5_context context, const char *file,
1832 struct krb5_dh_moduli ***moduli)
1834 /* name bits P G Q */
1835 krb5_error_code ret;
1836 struct krb5_dh_moduli **m = NULL, **m2;
1837 char buf[4096];
1838 FILE *f;
1839 int lineno = 0, n = 0;
1841 *moduli = NULL;
1843 m = calloc(1, sizeof(m[0]) * 3);
1844 if (m == NULL) {
1845 krb5_set_error_string(context, "malloc: out of memory");
1846 return ENOMEM;
1849 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1850 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1851 if (ret) {
1852 _krb5_free_moduli(m);
1853 return ret;
1855 n++;
1857 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1858 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
1859 if (ret) {
1860 _krb5_free_moduli(m);
1861 return ret;
1863 n++;
1866 if (file == NULL)
1867 file = MODULI_FILE;
1869 f = fopen(file, "r");
1870 if (f == NULL) {
1871 *moduli = m;
1872 return 0;
1875 while(fgets(buf, sizeof(buf), f) != NULL) {
1876 struct krb5_dh_moduli *element;
1878 buf[strcspn(buf, "\n")] = '\0';
1879 lineno++;
1881 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1882 if (m2 == NULL) {
1883 krb5_set_error_string(context, "malloc: out of memory");
1884 _krb5_free_moduli(m);
1885 return ENOMEM;
1887 m = m2;
1889 m[n] = NULL;
1891 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1892 if (ret) {
1893 _krb5_free_moduli(m);
1894 return ret;
1896 if (element == NULL)
1897 continue;
1899 m[n] = element;
1900 m[n + 1] = NULL;
1901 n++;
1903 *moduli = m;
1904 return 0;
1907 krb5_error_code
1908 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1909 heim_integer *p, heim_integer *g, heim_integer *q,
1910 struct krb5_dh_moduli **moduli,
1911 char **name)
1913 int i;
1915 if (name)
1916 *name = NULL;
1918 for (i = 0; moduli[i] != NULL; i++) {
1919 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1920 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1921 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1923 if (bits && bits > moduli[i]->bits) {
1924 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1925 "no accepted, not enough bits generated",
1926 moduli[i]->name);
1927 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1929 if (name)
1930 *name = strdup(moduli[i]->name);
1931 return 0;
1934 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1935 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1938 void KRB5_LIB_FUNCTION
1939 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1941 #ifdef PKINIT
1942 krb5_pk_init_ctx ctx;
1944 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1945 return;
1946 ctx = opt->opt_private->pk_init_ctx;
1947 if (ctx->dh)
1948 DH_free(ctx->dh);
1949 ctx->dh = NULL;
1950 if (ctx->id) {
1951 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1952 hx509_certs_free(&ctx->id->certs);
1953 hx509_certs_free(&ctx->id->anchors);
1954 hx509_certs_free(&ctx->id->certpool);
1955 hx509_context_free(&ctx->id->hx509ctx);
1957 if (ctx->clientDHNonce) {
1958 krb5_free_data(NULL, ctx->clientDHNonce);
1959 ctx->clientDHNonce = NULL;
1961 if (ctx->m)
1962 _krb5_free_moduli(ctx->m);
1963 free(ctx->id);
1964 ctx->id = NULL;
1966 free(opt->opt_private->pk_init_ctx);
1967 opt->opt_private->pk_init_ctx = NULL;
1968 #endif
1971 krb5_error_code KRB5_LIB_FUNCTION
1972 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1973 krb5_get_init_creds_opt *opt,
1974 krb5_principal principal,
1975 const char *user_id,
1976 const char *x509_anchors,
1977 char * const * pool,
1978 char * const * pki_revoke,
1979 int flags,
1980 krb5_prompter_fct prompter,
1981 void *prompter_data,
1982 char *password)
1984 #ifdef PKINIT
1985 krb5_error_code ret;
1986 char *anchors = NULL;
1988 if (opt->opt_private == NULL) {
1989 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1990 return EINVAL;
1993 opt->opt_private->pk_init_ctx =
1994 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1995 if (opt->opt_private->pk_init_ctx == NULL) {
1996 krb5_set_error_string(context, "malloc: out of memory");
1997 return ENOMEM;
1999 opt->opt_private->pk_init_ctx->dh = NULL;
2000 opt->opt_private->pk_init_ctx->id = NULL;
2001 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
2002 opt->opt_private->pk_init_ctx->require_binding = 0;
2003 opt->opt_private->pk_init_ctx->require_eku = 1;
2004 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2005 opt->opt_private->pk_init_ctx->peer = NULL;
2007 /* XXX implement krb5_appdefault_strings */
2008 if (pool == NULL)
2009 pool = krb5_config_get_strings(context, NULL,
2010 "appdefaults",
2011 "pkinit_pool",
2012 NULL);
2014 if (pki_revoke == NULL)
2015 pki_revoke = krb5_config_get_strings(context, NULL,
2016 "appdefaults",
2017 "pkinit_revoke",
2018 NULL);
2020 if (x509_anchors == NULL) {
2021 krb5_appdefault_string(context, "kinit",
2022 krb5_principal_get_realm(context, principal),
2023 "pkinit_anchors", NULL, &anchors);
2024 x509_anchors = anchors;
2027 ret = _krb5_pk_load_id(context,
2028 &opt->opt_private->pk_init_ctx->id,
2029 user_id,
2030 x509_anchors,
2031 pool,
2032 pki_revoke,
2033 prompter,
2034 prompter_data,
2035 password);
2036 if (ret) {
2037 free(opt->opt_private->pk_init_ctx);
2038 opt->opt_private->pk_init_ctx = NULL;
2039 return ret;
2042 if ((flags & 2) == 0) {
2043 const char *moduli_file;
2044 unsigned long dh_min_bits;
2046 moduli_file = krb5_config_get_string(context, NULL,
2047 "libdefaults",
2048 "moduli",
2049 NULL);
2051 dh_min_bits =
2052 krb5_config_get_int_default(context, NULL, 0,
2053 "libdefaults",
2054 "pkinit_dh_min_bits",
2055 NULL);
2057 ret = _krb5_parse_moduli(context, moduli_file,
2058 &opt->opt_private->pk_init_ctx->m);
2059 if (ret) {
2060 _krb5_get_init_creds_opt_free_pkinit(opt);
2061 return ret;
2064 opt->opt_private->pk_init_ctx->dh = DH_new();
2065 if (opt->opt_private->pk_init_ctx->dh == NULL) {
2066 krb5_set_error_string(context, "malloc: out of memory");
2067 _krb5_get_init_creds_opt_free_pkinit(opt);
2068 return ENOMEM;
2071 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2072 dh_min_bits,
2073 opt->opt_private->pk_init_ctx->m);
2074 if (ret) {
2075 _krb5_get_init_creds_opt_free_pkinit(opt);
2076 return ret;
2079 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2080 krb5_set_error_string(context, "pkinit: failed to generate DH key");
2081 _krb5_get_init_creds_opt_free_pkinit(opt);
2082 return ENOMEM;
2086 return 0;
2087 #else
2088 krb5_set_error_string(context, "no support for PKINIT compiled in");
2089 return EINVAL;
2090 #endif