adapt to updated hx509 api
[heimdal.git] / lib / krb5 / pkinit.c
blobd1e23e0a76825ccc2427b6b5f579656c7aa67ce2
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,
990 &contentType,
991 &content);
992 if (ret) {
993 pk_copy_error(context, ctx->id->hx509ctx, ret,
994 "Failed to unenvelope CMS data in PK-INIT reply");
995 return ret;
997 der_free_oid(&contentType);
999 #if 0 /* windows LH with interesting CMS packets, leaks memory */
1001 size_t ph = 1 + der_length_len (length);
1002 unsigned char *ptr = malloc(length + ph);
1003 size_t l;
1005 memcpy(ptr + ph, p, length);
1007 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
1008 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1009 if (ret)
1010 return ret;
1011 ptr += ph - l;
1012 length += l;
1013 p = ptr;
1015 #endif
1017 /* win2k uses ContentInfo */
1018 if (type == PKINIT_WIN2K) {
1019 heim_oid type;
1020 heim_octet_string out;
1022 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1023 if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1024 ret = EINVAL; /* XXX */
1025 krb5_set_error_string(context, "PKINIT: Invalid content type");
1026 der_free_oid(&type);
1027 der_free_octet_string(&out);
1028 goto out;
1030 der_free_oid(&type);
1031 krb5_data_free(&content);
1032 ret = krb5_data_copy(&content, out.data, out.length);
1033 der_free_octet_string(&out);
1034 if (ret) {
1035 krb5_set_error_string(context, "PKINIT: out of memory");
1036 goto out;
1040 ret = _krb5_pk_verify_sign(context,
1041 content.data,
1042 content.length,
1043 ctx->id,
1044 &contentType,
1045 &content,
1046 &host);
1047 if (ret)
1048 goto out;
1050 /* make sure that it is the kdc's certificate */
1051 ret = pk_verify_host(context, realm, hi, ctx, host);
1052 if (ret) {
1053 goto out;
1056 #if 0
1057 if (type == PKINIT_WIN2K) {
1058 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1059 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1060 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1061 goto out;
1063 } else {
1064 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1065 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1066 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1067 goto out;
1070 #endif
1072 switch(type) {
1073 case PKINIT_WIN2K:
1074 ret = get_reply_key(context, &content, req_buffer, key);
1075 if (ret != 0 && ctx->require_binding == 0)
1076 ret = get_reply_key_win(context, &content, nonce, key);
1077 break;
1078 case PKINIT_27:
1079 ret = get_reply_key(context, &content, req_buffer, key);
1080 break;
1082 if (ret)
1083 goto out;
1085 /* XXX compare given etype with key->etype */
1087 out:
1088 if (host)
1089 _krb5_pk_cert_free(host);
1090 der_free_oid(&contentType);
1091 krb5_data_free(&content);
1093 return ret;
1096 static krb5_error_code
1097 pk_rd_pa_reply_dh(krb5_context context,
1098 const heim_octet_string *indata,
1099 const heim_oid *dataType,
1100 const char *realm,
1101 krb5_pk_init_ctx ctx,
1102 krb5_enctype etype,
1103 const krb5_krbhst_info *hi,
1104 const DHNonce *c_n,
1105 const DHNonce *k_n,
1106 unsigned nonce,
1107 PA_DATA *pa,
1108 krb5_keyblock **key)
1110 unsigned char *p, *dh_gen_key = NULL;
1111 struct krb5_pk_cert *host = NULL;
1112 BIGNUM *kdc_dh_pubkey = NULL;
1113 KDCDHKeyInfo kdc_dh_info;
1114 heim_oid contentType = { 0, NULL };
1115 krb5_data content;
1116 krb5_error_code ret;
1117 int dh_gen_keylen;
1118 size_t size;
1120 krb5_data_zero(&content);
1121 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1123 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1124 krb5_set_error_string(context, "PKINIT: Invalid content type");
1125 return EINVAL;
1128 ret = _krb5_pk_verify_sign(context,
1129 indata->data,
1130 indata->length,
1131 ctx->id,
1132 &contentType,
1133 &content,
1134 &host);
1135 if (ret)
1136 goto out;
1138 /* make sure that it is the kdc's certificate */
1139 ret = pk_verify_host(context, realm, hi, ctx, host);
1140 if (ret)
1141 goto out;
1143 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1144 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1145 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1146 goto out;
1149 ret = decode_KDCDHKeyInfo(content.data,
1150 content.length,
1151 &kdc_dh_info,
1152 &size);
1154 if (ret) {
1155 krb5_set_error_string(context, "pkinit - "
1156 "failed to decode KDC DH Key Info");
1157 goto out;
1160 if (kdc_dh_info.nonce != nonce) {
1161 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1162 ret = KRB5KRB_AP_ERR_MODIFIED;
1163 goto out;
1166 if (kdc_dh_info.dhKeyExpiration) {
1167 if (k_n == NULL) {
1168 krb5_set_error_string(context, "pkinit; got key expiration "
1169 "without server nonce");
1170 ret = KRB5KRB_ERR_GENERIC;
1171 goto out;
1173 if (c_n == NULL) {
1174 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1175 "client nonce");
1176 ret = KRB5KRB_ERR_GENERIC;
1177 goto out;
1179 } else {
1180 if (k_n) {
1181 krb5_set_error_string(context, "pkinit: got server nonce "
1182 "without key expiration");
1183 ret = KRB5KRB_ERR_GENERIC;
1184 goto out;
1186 c_n = NULL;
1190 p = kdc_dh_info.subjectPublicKey.data;
1191 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1194 DHPublicKey k;
1195 ret = decode_DHPublicKey(p, size, &k, NULL);
1196 if (ret) {
1197 krb5_set_error_string(context, "pkinit: can't decode "
1198 "without key expiration");
1199 goto out;
1202 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1203 free_DHPublicKey(&k);
1204 if (kdc_dh_pubkey == NULL) {
1205 ret = KRB5KRB_ERR_GENERIC;
1206 goto out;
1210 dh_gen_keylen = DH_size(ctx->dh);
1211 size = BN_num_bytes(ctx->dh->p);
1212 if (size < dh_gen_keylen)
1213 size = dh_gen_keylen;
1215 dh_gen_key = malloc(size);
1216 if (dh_gen_key == NULL) {
1217 krb5_set_error_string(context, "malloc: out of memory");
1218 ret = ENOMEM;
1219 goto out;
1221 memset(dh_gen_key, 0, size - dh_gen_keylen);
1223 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1224 kdc_dh_pubkey, ctx->dh);
1225 if (dh_gen_keylen == -1) {
1226 krb5_set_error_string(context,
1227 "PKINIT: Can't compute Diffie-Hellman key");
1228 ret = KRB5KRB_ERR_GENERIC;
1229 goto out;
1232 *key = malloc (sizeof (**key));
1233 if (*key == NULL) {
1234 krb5_set_error_string(context, "malloc: out of memory");
1235 ret = ENOMEM;
1236 goto out;
1239 ret = _krb5_pk_octetstring2key(context,
1240 etype,
1241 dh_gen_key, dh_gen_keylen,
1242 c_n, k_n,
1243 *key);
1244 if (ret) {
1245 krb5_set_error_string(context,
1246 "PKINIT: can't create key from DH key");
1247 free(*key);
1248 *key = NULL;
1249 goto out;
1252 out:
1253 if (kdc_dh_pubkey)
1254 BN_free(kdc_dh_pubkey);
1255 if (dh_gen_key) {
1256 memset(dh_gen_key, 0, DH_size(ctx->dh));
1257 free(dh_gen_key);
1259 if (host)
1260 _krb5_pk_cert_free(host);
1261 if (content.data)
1262 krb5_data_free(&content);
1263 der_free_oid(&contentType);
1264 free_KDCDHKeyInfo(&kdc_dh_info);
1266 return ret;
1269 krb5_error_code KRB5_LIB_FUNCTION
1270 _krb5_pk_rd_pa_reply(krb5_context context,
1271 const char *realm,
1272 void *c,
1273 krb5_enctype etype,
1274 const krb5_krbhst_info *hi,
1275 unsigned nonce,
1276 const krb5_data *req_buffer,
1277 PA_DATA *pa,
1278 krb5_keyblock **key)
1280 krb5_pk_init_ctx ctx = c;
1281 krb5_error_code ret;
1282 size_t size;
1284 /* Check for IETF PK-INIT first */
1285 if (ctx->type == PKINIT_27) {
1286 PA_PK_AS_REP rep;
1287 heim_octet_string os, data;
1288 heim_oid oid;
1290 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1291 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1292 return EINVAL;
1295 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1296 pa->padata_value.length,
1297 &rep,
1298 &size);
1299 if (ret) {
1300 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1301 return ret;
1304 switch (rep.element) {
1305 case choice_PA_PK_AS_REP_dhInfo:
1306 os = rep.u.dhInfo.dhSignedData;
1307 break;
1308 case choice_PA_PK_AS_REP_encKeyPack:
1309 os = rep.u.encKeyPack;
1310 break;
1311 default:
1312 free_PA_PK_AS_REP(&rep);
1313 krb5_set_error_string(context, "PKINIT: -27 reply "
1314 "invalid content type");
1315 return EINVAL;
1318 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1319 if (ret) {
1320 free_PA_PK_AS_REP(&rep);
1321 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1322 return ret;
1325 switch (rep.element) {
1326 case choice_PA_PK_AS_REP_dhInfo:
1327 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1328 ctx->clientDHNonce,
1329 rep.u.dhInfo.serverDHNonce,
1330 nonce, pa, key);
1331 break;
1332 case choice_PA_PK_AS_REP_encKeyPack:
1333 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1334 ctx, etype, hi, nonce, req_buffer, pa, key);
1335 break;
1336 default:
1337 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1339 der_free_octet_string(&data);
1340 der_free_oid(&oid);
1341 free_PA_PK_AS_REP(&rep);
1343 } else if (ctx->type == PKINIT_WIN2K) {
1344 PA_PK_AS_REP_Win2k w2krep;
1346 /* Check for Windows encoding of the AS-REP pa data */
1348 #if 0 /* should this be ? */
1349 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1350 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1351 return EINVAL;
1353 #endif
1355 memset(&w2krep, 0, sizeof(w2krep));
1357 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1358 pa->padata_value.length,
1359 &w2krep,
1360 &size);
1361 if (ret) {
1362 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1363 "pkinit reply %d", (int)ret);
1364 return ret;
1367 krb5_clear_error_string(context);
1369 switch (w2krep.element) {
1370 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1371 heim_octet_string data;
1372 heim_oid oid;
1374 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1375 &oid, &data, NULL);
1376 free_PA_PK_AS_REP_Win2k(&w2krep);
1377 if (ret) {
1378 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1379 return ret;
1382 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1383 ctx, etype, hi, nonce, req_buffer, pa, key);
1384 der_free_octet_string(&data);
1385 der_free_oid(&oid);
1387 break;
1389 default:
1390 free_PA_PK_AS_REP_Win2k(&w2krep);
1391 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1392 "content type");
1393 ret = EINVAL;
1394 break;
1397 } else {
1398 krb5_set_error_string(context, "PKINIT: unknown reply type");
1399 ret = EINVAL;
1402 return ret;
1405 struct prompter {
1406 krb5_context context;
1407 krb5_prompter_fct prompter;
1408 void *prompter_data;
1411 static int
1412 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1414 krb5_error_code ret;
1415 krb5_prompt prompt;
1416 krb5_data password_data;
1417 struct prompter *p = data;
1419 password_data.data = prompter->reply.data;
1420 password_data.length = prompter->reply.length;
1422 prompt.prompt = prompter->prompt;
1423 prompt.hidden = hx509_prompt_hidden(prompter->type);
1424 prompt.reply = &password_data;
1426 switch (prompter->type) {
1427 case HX509_PROMPT_TYPE_INFO:
1428 prompt.type = KRB5_PROMPT_TYPE_INFO;
1429 break;
1430 case HX509_PROMPT_TYPE_PASSWORD:
1431 case HX509_PROMPT_TYPE_QUESTION:
1432 default:
1433 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1434 break;
1437 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1438 if (ret) {
1439 memset (prompter->reply.data, 0, prompter->reply.length);
1440 return 1;
1442 return 0;
1446 void KRB5_LIB_FUNCTION
1447 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1448 int boolean)
1450 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1454 krb5_error_code KRB5_LIB_FUNCTION
1455 _krb5_pk_load_id(krb5_context context,
1456 struct krb5_pk_identity **ret_id,
1457 const char *user_id,
1458 const char *anchor_id,
1459 char * const *chain_list,
1460 char * const *revoke_list,
1461 krb5_prompter_fct prompter,
1462 void *prompter_data,
1463 char *password)
1465 struct krb5_pk_identity *id = NULL;
1466 hx509_lock lock = NULL;
1467 struct prompter p;
1468 int ret;
1470 *ret_id = NULL;
1472 if (anchor_id == NULL) {
1473 krb5_set_error_string(context, "PKINIT: No anchor given");
1474 return HEIM_PKINIT_NO_VALID_CA;
1477 if (user_id == NULL) {
1478 krb5_set_error_string(context,
1479 "PKINIT: No user certificate given");
1480 return HEIM_PKINIT_NO_PRIVATE_KEY;
1483 /* load cert */
1485 id = calloc(1, sizeof(*id));
1486 if (id == NULL) {
1487 krb5_set_error_string(context, "malloc: out of memory");
1488 return ENOMEM;
1491 ret = hx509_context_init(&id->hx509ctx);
1492 if (ret)
1493 goto out;
1495 ret = hx509_lock_init(id->hx509ctx, &lock);
1496 if (password && password[0])
1497 hx509_lock_add_password(lock, password);
1499 if (prompter) {
1500 p.context = context;
1501 p.prompter = prompter;
1502 p.prompter_data = prompter_data;
1504 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1505 if (ret)
1506 goto out;
1509 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1510 if (ret) {
1511 pk_copy_error(context, id->hx509ctx, ret,
1512 "Failed to init cert certs");
1513 goto out;
1516 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1517 if (ret) {
1518 pk_copy_error(context, id->hx509ctx, ret,
1519 "Failed to init anchors");
1520 goto out;
1523 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1524 0, NULL, &id->certpool);
1525 if (ret) {
1526 pk_copy_error(context, id->hx509ctx, ret,
1527 "Failed to init chain");
1528 goto out;
1531 while (chain_list && *chain_list) {
1532 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1533 NULL, *chain_list);
1534 if (ret) {
1535 pk_copy_error(context, id->hx509ctx, ret,
1536 "Failed to laod chain %s",
1537 *chain_list);
1538 goto out;
1540 chain_list++;
1543 if (revoke_list) {
1544 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1545 if (ret) {
1546 pk_copy_error(context, id->hx509ctx, ret,
1547 "Failed init revoke list");
1548 goto out;
1551 while (*revoke_list) {
1552 ret = hx509_revoke_add_crl(id->hx509ctx,
1553 id->revokectx,
1554 *revoke_list);
1555 if (ret) {
1556 pk_copy_error(context, id->hx509ctx, ret,
1557 "Failed load revoke list");
1558 goto out;
1560 revoke_list++;
1562 } else
1563 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1565 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1566 if (ret) {
1567 pk_copy_error(context, id->hx509ctx, ret,
1568 "Failed init verify context");
1569 goto out;
1572 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1573 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1575 out:
1576 if (ret) {
1577 hx509_verify_destroy_ctx(id->verify_ctx);
1578 hx509_certs_free(&id->certs);
1579 hx509_certs_free(&id->anchors);
1580 hx509_certs_free(&id->certpool);
1581 hx509_revoke_free(&id->revokectx);
1582 hx509_context_free(&id->hx509ctx);
1583 free(id);
1584 } else
1585 *ret_id = id;
1587 hx509_lock_free(lock);
1589 return ret;
1592 static krb5_error_code
1593 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1594 struct krb5_dh_moduli **moduli)
1596 const struct krb5_dh_moduli *m;
1598 if (bits == 0) {
1599 m = moduli[1]; /* XXX */
1600 if (m == NULL)
1601 m = moduli[0]; /* XXX */
1602 } else {
1603 int i;
1604 for (i = 0; moduli[i] != NULL; i++) {
1605 if (bits < moduli[i]->bits)
1606 break;
1608 if (moduli[i] == NULL) {
1609 krb5_set_error_string(context,
1610 "Did not find a DH group parameter "
1611 "matching requirement of %lu bits",
1612 bits);
1613 return EINVAL;
1615 m = moduli[i];
1618 dh->p = integer_to_BN(context, "p", &m->p);
1619 if (dh->p == NULL)
1620 return ENOMEM;
1621 dh->g = integer_to_BN(context, "g", &m->g);
1622 if (dh->g == NULL)
1623 return ENOMEM;
1624 dh->q = integer_to_BN(context, "q", &m->q);
1625 if (dh->q == NULL)
1626 return ENOMEM;
1628 return 0;
1635 static void
1636 pk_copy_error(krb5_context context,
1637 hx509_context hx509ctx,
1638 int hxret,
1639 const char *fmt,
1640 ...)
1642 va_list va;
1643 char *s, *f;
1645 va_start(va, fmt);
1646 vasprintf(&f, fmt, va);
1647 va_end(va);
1648 if (f == NULL) {
1649 krb5_clear_error_string(context);
1650 return;
1653 s = hx509_get_error_string(hx509ctx, hxret);
1654 if (s == NULL) {
1655 krb5_clear_error_string(context);
1656 free(f);
1657 return;
1659 krb5_set_error_string(context, "%s: %s", f, s);
1660 free(s);
1661 free(f);
1664 #endif /* PKINIT */
1666 static int
1667 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1668 const char *name, heim_integer *integer)
1670 int ret;
1671 char *p1;
1672 p1 = strsep(p, " \t");
1673 if (p1 == NULL) {
1674 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1675 file, name, lineno);
1676 return EINVAL;
1678 ret = der_parse_hex_heim_integer(p1, integer);
1679 if (ret) {
1680 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1681 "on line %d",
1682 file, name, lineno);
1683 return ret;
1686 return 0;
1689 krb5_error_code
1690 _krb5_parse_moduli_line(krb5_context context,
1691 const char *file,
1692 int lineno,
1693 char *p,
1694 struct krb5_dh_moduli **m)
1696 struct krb5_dh_moduli *m1;
1697 char *p1;
1698 int ret;
1700 *m = NULL;
1702 m1 = calloc(1, sizeof(*m1));
1703 if (m1 == NULL) {
1704 krb5_set_error_string(context, "malloc - out of memory");
1705 return ENOMEM;
1708 while (isspace((unsigned char)*p))
1709 p++;
1710 if (*p == '#')
1711 return 0;
1712 ret = EINVAL;
1714 p1 = strsep(&p, " \t");
1715 if (p1 == NULL) {
1716 krb5_set_error_string(context, "moduli file %s missing name "
1717 "on line %d", file, lineno);
1718 goto out;
1720 m1->name = strdup(p1);
1721 if (p1 == NULL) {
1722 krb5_set_error_string(context, "malloc - out of memeory");
1723 ret = ENOMEM;
1724 goto out;
1727 p1 = strsep(&p, " \t");
1728 if (p1 == NULL) {
1729 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1730 file, lineno);
1731 goto out;
1734 m1->bits = atoi(p1);
1735 if (m1->bits == 0) {
1736 krb5_set_error_string(context, "moduli file %s have un-parsable "
1737 "bits on line %d", file, lineno);
1738 goto out;
1741 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1742 if (ret)
1743 goto out;
1744 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1745 if (ret)
1746 goto out;
1747 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1748 if (ret)
1749 goto out;
1751 *m = m1;
1753 return 0;
1754 out:
1755 free(m1->name);
1756 der_free_heim_integer(&m1->p);
1757 der_free_heim_integer(&m1->g);
1758 der_free_heim_integer(&m1->q);
1759 free(m1);
1760 return ret;
1763 void
1764 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1766 int i;
1767 for (i = 0; moduli[i] != NULL; i++) {
1768 free(moduli[i]->name);
1769 der_free_heim_integer(&moduli[i]->p);
1770 der_free_heim_integer(&moduli[i]->g);
1771 der_free_heim_integer(&moduli[i]->q);
1772 free(moduli[i]);
1774 free(moduli);
1777 static const char *default_moduli_RFC2412_MODP_group2 =
1778 /* name */
1779 "RFC2412-MODP-group2 "
1780 /* bits */
1781 "1024 "
1782 /* p */
1783 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1784 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1785 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1786 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1787 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1788 "FFFFFFFF" "FFFFFFFF "
1789 /* g */
1790 "02 "
1791 /* q */
1792 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1793 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1794 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1795 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1796 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1797 "FFFFFFFF" "FFFFFFFF";
1799 static const char *default_moduli_rfc3526_MODP_group14 =
1800 /* name */
1801 "rfc3526-MODP-group14 "
1802 /* bits */
1803 "1760 "
1804 /* p */
1805 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1806 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1807 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1808 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1809 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1810 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1811 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1812 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1813 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1814 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1815 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1816 /* g */
1817 "02 "
1818 /* q */
1819 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1820 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1821 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1822 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1823 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1824 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1825 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1826 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1827 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1828 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1829 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1831 krb5_error_code
1832 _krb5_parse_moduli(krb5_context context, const char *file,
1833 struct krb5_dh_moduli ***moduli)
1835 /* name bits P G Q */
1836 krb5_error_code ret;
1837 struct krb5_dh_moduli **m = NULL, **m2;
1838 char buf[4096];
1839 FILE *f;
1840 int lineno = 0, n = 0;
1842 *moduli = NULL;
1844 m = calloc(1, sizeof(m[0]) * 3);
1845 if (m == NULL) {
1846 krb5_set_error_string(context, "malloc: out of memory");
1847 return ENOMEM;
1850 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1851 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1852 if (ret) {
1853 _krb5_free_moduli(m);
1854 return ret;
1856 n++;
1858 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1859 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
1860 if (ret) {
1861 _krb5_free_moduli(m);
1862 return ret;
1864 n++;
1867 if (file == NULL)
1868 file = MODULI_FILE;
1870 f = fopen(file, "r");
1871 if (f == NULL) {
1872 *moduli = m;
1873 return 0;
1876 while(fgets(buf, sizeof(buf), f) != NULL) {
1877 struct krb5_dh_moduli *element;
1879 buf[strcspn(buf, "\n")] = '\0';
1880 lineno++;
1882 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1883 if (m2 == NULL) {
1884 krb5_set_error_string(context, "malloc: out of memory");
1885 _krb5_free_moduli(m);
1886 return ENOMEM;
1888 m = m2;
1890 m[n] = NULL;
1892 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1893 if (ret) {
1894 _krb5_free_moduli(m);
1895 return ret;
1897 if (element == NULL)
1898 continue;
1900 m[n] = element;
1901 m[n + 1] = NULL;
1902 n++;
1904 *moduli = m;
1905 return 0;
1908 krb5_error_code
1909 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1910 heim_integer *p, heim_integer *g, heim_integer *q,
1911 struct krb5_dh_moduli **moduli,
1912 char **name)
1914 int i;
1916 if (name)
1917 *name = NULL;
1919 for (i = 0; moduli[i] != NULL; i++) {
1920 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1921 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1922 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1924 if (bits && bits > moduli[i]->bits) {
1925 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1926 "no accepted, not enough bits generated",
1927 moduli[i]->name);
1928 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1930 if (name)
1931 *name = strdup(moduli[i]->name);
1932 return 0;
1935 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1936 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1939 void KRB5_LIB_FUNCTION
1940 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1942 #ifdef PKINIT
1943 krb5_pk_init_ctx ctx;
1945 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1946 return;
1947 ctx = opt->opt_private->pk_init_ctx;
1948 if (ctx->dh)
1949 DH_free(ctx->dh);
1950 ctx->dh = NULL;
1951 if (ctx->id) {
1952 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1953 hx509_certs_free(&ctx->id->certs);
1954 hx509_certs_free(&ctx->id->anchors);
1955 hx509_certs_free(&ctx->id->certpool);
1956 hx509_context_free(&ctx->id->hx509ctx);
1958 if (ctx->clientDHNonce) {
1959 krb5_free_data(NULL, ctx->clientDHNonce);
1960 ctx->clientDHNonce = NULL;
1962 if (ctx->m)
1963 _krb5_free_moduli(ctx->m);
1964 free(ctx->id);
1965 ctx->id = NULL;
1967 free(opt->opt_private->pk_init_ctx);
1968 opt->opt_private->pk_init_ctx = NULL;
1969 #endif
1972 krb5_error_code KRB5_LIB_FUNCTION
1973 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1974 krb5_get_init_creds_opt *opt,
1975 krb5_principal principal,
1976 const char *user_id,
1977 const char *x509_anchors,
1978 char * const * pool,
1979 char * const * pki_revoke,
1980 int flags,
1981 krb5_prompter_fct prompter,
1982 void *prompter_data,
1983 char *password)
1985 #ifdef PKINIT
1986 krb5_error_code ret;
1987 char *anchors = NULL;
1989 if (opt->opt_private == NULL) {
1990 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1991 return EINVAL;
1994 opt->opt_private->pk_init_ctx =
1995 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1996 if (opt->opt_private->pk_init_ctx == NULL) {
1997 krb5_set_error_string(context, "malloc: out of memory");
1998 return ENOMEM;
2000 opt->opt_private->pk_init_ctx->dh = NULL;
2001 opt->opt_private->pk_init_ctx->id = NULL;
2002 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
2003 opt->opt_private->pk_init_ctx->require_binding = 0;
2004 opt->opt_private->pk_init_ctx->require_eku = 1;
2005 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2006 opt->opt_private->pk_init_ctx->peer = NULL;
2008 /* XXX implement krb5_appdefault_strings */
2009 if (pool == NULL)
2010 pool = krb5_config_get_strings(context, NULL,
2011 "appdefaults",
2012 "pkinit_pool",
2013 NULL);
2015 if (pki_revoke == NULL)
2016 pki_revoke = krb5_config_get_strings(context, NULL,
2017 "appdefaults",
2018 "pkinit_revoke",
2019 NULL);
2021 if (x509_anchors == NULL) {
2022 krb5_appdefault_string(context, "kinit",
2023 krb5_principal_get_realm(context, principal),
2024 "pkinit_anchors", NULL, &anchors);
2025 x509_anchors = anchors;
2028 ret = _krb5_pk_load_id(context,
2029 &opt->opt_private->pk_init_ctx->id,
2030 user_id,
2031 x509_anchors,
2032 pool,
2033 pki_revoke,
2034 prompter,
2035 prompter_data,
2036 password);
2037 if (ret) {
2038 free(opt->opt_private->pk_init_ctx);
2039 opt->opt_private->pk_init_ctx = NULL;
2040 return ret;
2043 if ((flags & 2) == 0) {
2044 const char *moduli_file;
2045 unsigned long dh_min_bits;
2047 moduli_file = krb5_config_get_string(context, NULL,
2048 "libdefaults",
2049 "moduli",
2050 NULL);
2052 dh_min_bits =
2053 krb5_config_get_int_default(context, NULL, 0,
2054 "libdefaults",
2055 "pkinit_dh_min_bits",
2056 NULL);
2058 ret = _krb5_parse_moduli(context, moduli_file,
2059 &opt->opt_private->pk_init_ctx->m);
2060 if (ret) {
2061 _krb5_get_init_creds_opt_free_pkinit(opt);
2062 return ret;
2065 opt->opt_private->pk_init_ctx->dh = DH_new();
2066 if (opt->opt_private->pk_init_ctx->dh == NULL) {
2067 krb5_set_error_string(context, "malloc: out of memory");
2068 _krb5_get_init_creds_opt_free_pkinit(opt);
2069 return ENOMEM;
2072 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2073 dh_min_bits,
2074 opt->opt_private->pk_init_ctx->m);
2075 if (ret) {
2076 _krb5_get_init_creds_opt_free_pkinit(opt);
2077 return ret;
2080 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2081 krb5_set_error_string(context, "pkinit: failed to generate DH key");
2082 _krb5_get_init_creds_opt_free_pkinit(opt);
2083 return ENOMEM;
2087 return 0;
2088 #else
2089 krb5_set_error_string(context, "no support for PKINIT compiled in");
2090 return EINVAL;
2091 #endif