test debug
[heimdal.git] / lib / krb5 / pkinit.c
blob1c8621fd43e532e935190a99e486c1a353686557
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 <heim_asn1.h>
49 #include <rfc2459_asn1.h>
50 #include <cms_asn1.h>
51 #include <pkcs8_asn1.h>
52 #include <pkcs9_asn1.h>
53 #include <pkcs12_asn1.h>
54 #include <pkinit_asn1.h>
55 #include <asn1_err.h>
57 #include <der.h>
59 #include <hx509.h>
61 enum {
62 COMPAT_WIN2K = 1,
63 COMPAT_IETF = 2
66 struct krb5_pk_identity {
67 hx509_context hx509ctx;
68 hx509_verify_ctx verify_ctx;
69 hx509_certs certs;
70 hx509_certs anchors;
71 hx509_certs certpool;
72 hx509_revoke_ctx revokectx;
75 struct krb5_pk_cert {
76 hx509_cert cert;
79 struct krb5_pk_init_ctx_data {
80 struct krb5_pk_identity *id;
81 DH *dh;
82 krb5_data *clientDHNonce;
83 struct krb5_dh_moduli **m;
84 hx509_peer_info peer;
85 int type;
86 unsigned int require_binding:1;
87 unsigned int require_eku:1;
88 unsigned int require_krbtgt_otherName:1;
89 unsigned int require_hostname_match:1;
90 unsigned int trustedCertifiers:1;
93 static void
94 _krb5_pk_copy_error(krb5_context context,
95 hx509_context hx509ctx,
96 int hxret,
97 const char *fmt,
98 ...)
99 __attribute__ ((format (printf, 4, 5)));
105 void KRB5_LIB_FUNCTION
106 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
108 if (cert->cert) {
109 hx509_cert_free(cert->cert);
111 free(cert);
114 static krb5_error_code
115 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
117 integer->length = BN_num_bytes(bn);
118 integer->data = malloc(integer->length);
119 if (integer->data == NULL) {
120 krb5_clear_error_string(context);
121 return ENOMEM;
123 BN_bn2bin(bn, integer->data);
124 integer->negative = BN_is_negative(bn);
125 return 0;
128 static BIGNUM *
129 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
131 BIGNUM *bn;
133 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
134 if (bn == NULL) {
135 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
136 return NULL;
138 BN_set_negative(bn, f->negative);
139 return bn;
143 static krb5_error_code
144 _krb5_pk_create_sign(krb5_context context,
145 const heim_oid *eContentType,
146 krb5_data *eContent,
147 struct krb5_pk_identity *id,
148 hx509_peer_info peer,
149 krb5_data *sd_data)
151 hx509_cert cert;
152 hx509_query *q;
153 int ret;
155 ret = hx509_query_alloc(id->hx509ctx, &q);
156 if (ret) {
157 _krb5_pk_copy_error(context, id->hx509ctx, ret,
158 "Allocate query to find signing certificate");
159 return ret;
162 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
163 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
165 ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
166 hx509_query_free(id->hx509ctx, q);
167 if (ret) {
168 _krb5_pk_copy_error(context, id->hx509ctx, ret,
169 "Find certificate to signed CMS data");
170 return ret;
173 ret = hx509_cms_create_signed_1(id->hx509ctx,
175 eContentType,
176 eContent->data,
177 eContent->length,
178 NULL,
179 cert,
180 peer,
181 NULL,
182 id->certs,
183 sd_data);
184 if (ret)
185 _krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
186 hx509_cert_free(cert);
188 return ret;
191 static int
192 cert2epi(hx509_context context, void *ctx, hx509_cert c)
194 ExternalPrincipalIdentifiers *ids = ctx;
195 ExternalPrincipalIdentifier id;
196 hx509_name subject = NULL;
197 void *p;
198 int ret;
200 memset(&id, 0, sizeof(id));
202 ret = hx509_cert_get_subject(c, &subject);
203 if (ret)
204 return ret;
206 if (hx509_name_is_null_p(subject) != 0) {
208 id.subjectName = calloc(1, sizeof(*id.subjectName));
209 if (id.subjectName == NULL) {
210 hx509_name_free(&subject);
211 free_ExternalPrincipalIdentifier(&id);
212 return ENOMEM;
215 ret = hx509_name_binary(subject, id.subjectName);
216 if (ret) {
217 hx509_name_free(&subject);
218 free_ExternalPrincipalIdentifier(&id);
219 return ret;
222 hx509_name_free(&subject);
225 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
226 if (id.issuerAndSerialNumber == NULL) {
227 free_ExternalPrincipalIdentifier(&id);
228 return ENOMEM;
232 IssuerAndSerialNumber iasn;
233 hx509_name issuer;
234 size_t size;
236 memset(&iasn, 0, sizeof(iasn));
238 ret = hx509_cert_get_issuer(c, &issuer);
239 if (ret) {
240 free_ExternalPrincipalIdentifier(&id);
241 return ret;
244 ret = hx509_name_to_Name(issuer, &iasn.issuer);
245 hx509_name_free(&issuer);
246 if (ret) {
247 free_ExternalPrincipalIdentifier(&id);
248 return ret;
251 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
252 if (ret) {
253 free_IssuerAndSerialNumber(&iasn);
254 free_ExternalPrincipalIdentifier(&id);
255 return ret;
258 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
259 id.issuerAndSerialNumber->data,
260 id.issuerAndSerialNumber->length,
261 &iasn, &size, ret);
262 free_IssuerAndSerialNumber(&iasn);
263 if (ret)
264 return ret;
265 if (id.issuerAndSerialNumber->length != size)
266 abort();
269 id.subjectKeyIdentifier = NULL;
271 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
272 if (p == NULL) {
273 free_ExternalPrincipalIdentifier(&id);
274 return ENOMEM;
277 ids->val = p;
278 ids->val[ids->len] = id;
279 ids->len++;
281 return 0;
284 static krb5_error_code
285 build_edi(krb5_context context,
286 hx509_context hx509ctx,
287 hx509_certs certs,
288 ExternalPrincipalIdentifiers *ids)
290 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
293 static krb5_error_code
294 build_auth_pack(krb5_context context,
295 unsigned nonce,
296 krb5_pk_init_ctx ctx,
297 DH *dh,
298 const KDC_REQ_BODY *body,
299 AuthPack *a)
301 size_t buf_size, len;
302 krb5_error_code ret;
303 void *buf;
304 krb5_timestamp sec;
305 int32_t usec;
306 Checksum checksum;
308 krb5_clear_error_string(context);
310 memset(&checksum, 0, sizeof(checksum));
312 krb5_us_timeofday(context, &sec, &usec);
313 a->pkAuthenticator.ctime = sec;
314 a->pkAuthenticator.nonce = nonce;
316 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
317 if (ret)
318 return ret;
319 if (buf_size != len)
320 krb5_abortx(context, "internal error in ASN.1 encoder");
322 ret = krb5_create_checksum(context,
323 NULL,
325 CKSUMTYPE_SHA1,
326 buf,
327 len,
328 &checksum);
329 free(buf);
330 if (ret)
331 return ret;
333 ALLOC(a->pkAuthenticator.paChecksum, 1);
334 if (a->pkAuthenticator.paChecksum == NULL) {
335 krb5_set_error_string(context, "malloc: out of memory");
336 return ENOMEM;
339 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
340 checksum.checksum.data, checksum.checksum.length);
341 free_Checksum(&checksum);
342 if (ret)
343 return ret;
345 if (dh) {
346 DomainParameters dp;
347 heim_integer dh_pub_key;
348 krb5_data dhbuf;
349 size_t size;
351 if (1 /* support_cached_dh */) {
352 ALLOC(a->clientDHNonce, 1);
353 if (a->clientDHNonce == NULL) {
354 krb5_clear_error_string(context);
355 return ENOMEM;
357 ret = krb5_data_alloc(a->clientDHNonce, 40);
358 if (a->clientDHNonce == NULL) {
359 krb5_clear_error_string(context);
360 return ENOMEM;
362 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
363 ret = krb5_copy_data(context, a->clientDHNonce,
364 &ctx->clientDHNonce);
365 if (ret)
366 return ret;
369 ALLOC(a->clientPublicValue, 1);
370 if (a->clientPublicValue == NULL)
371 return ENOMEM;
372 ret = der_copy_oid(oid_id_dhpublicnumber(),
373 &a->clientPublicValue->algorithm.algorithm);
374 if (ret)
375 return ret;
377 memset(&dp, 0, sizeof(dp));
379 ret = BN_to_integer(context, dh->p, &dp.p);
380 if (ret) {
381 free_DomainParameters(&dp);
382 return ret;
384 ret = BN_to_integer(context, dh->g, &dp.g);
385 if (ret) {
386 free_DomainParameters(&dp);
387 return ret;
389 ret = BN_to_integer(context, dh->q, &dp.q);
390 if (ret) {
391 free_DomainParameters(&dp);
392 return ret;
394 dp.j = NULL;
395 dp.validationParms = NULL;
397 a->clientPublicValue->algorithm.parameters =
398 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
399 if (a->clientPublicValue->algorithm.parameters == NULL) {
400 free_DomainParameters(&dp);
401 return ret;
404 ASN1_MALLOC_ENCODE(DomainParameters,
405 a->clientPublicValue->algorithm.parameters->data,
406 a->clientPublicValue->algorithm.parameters->length,
407 &dp, &size, ret);
408 free_DomainParameters(&dp);
409 if (ret)
410 return ret;
411 if (size != a->clientPublicValue->algorithm.parameters->length)
412 krb5_abortx(context, "Internal ASN1 encoder error");
414 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
415 if (ret)
416 return ret;
418 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
419 &dh_pub_key, &size, ret);
420 der_free_heim_integer(&dh_pub_key);
421 if (ret)
422 return ret;
423 if (size != dhbuf.length)
424 krb5_abortx(context, "asn1 internal error");
426 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
427 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
431 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
432 if (a->supportedCMSTypes == NULL)
433 return ENOMEM;
435 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
436 &a->supportedCMSTypes->val,
437 &a->supportedCMSTypes->len);
438 if (ret)
439 return ret;
442 return ret;
445 krb5_error_code KRB5_LIB_FUNCTION
446 _krb5_pk_mk_ContentInfo(krb5_context context,
447 const krb5_data *buf,
448 const heim_oid *oid,
449 struct ContentInfo *content_info)
451 krb5_error_code ret;
453 ret = der_copy_oid(oid, &content_info->contentType);
454 if (ret)
455 return ret;
456 ALLOC(content_info->content, 1);
457 if (content_info->content == NULL)
458 return ENOMEM;
459 content_info->content->data = malloc(buf->length);
460 if (content_info->content->data == NULL)
461 return ENOMEM;
462 memcpy(content_info->content->data, buf->data, buf->length);
463 content_info->content->length = buf->length;
464 return 0;
467 static krb5_error_code
468 pk_mk_padata(krb5_context context,
469 krb5_pk_init_ctx ctx,
470 const KDC_REQ_BODY *req_body,
471 unsigned nonce,
472 METHOD_DATA *md)
474 struct ContentInfo content_info;
475 krb5_error_code ret;
476 const heim_oid *oid;
477 size_t size;
478 krb5_data buf, sd_buf;
479 int pa_type;
481 krb5_data_zero(&buf);
482 krb5_data_zero(&sd_buf);
483 memset(&content_info, 0, sizeof(content_info));
485 if (ctx->type == COMPAT_WIN2K) {
486 AuthPack_Win2k ap;
487 krb5_timestamp sec;
488 int32_t usec;
490 memset(&ap, 0, sizeof(ap));
492 /* fill in PKAuthenticator */
493 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
494 if (ret) {
495 free_AuthPack_Win2k(&ap);
496 krb5_clear_error_string(context);
497 goto out;
499 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
500 if (ret) {
501 free_AuthPack_Win2k(&ap);
502 krb5_clear_error_string(context);
503 goto out;
506 krb5_us_timeofday(context, &sec, &usec);
507 ap.pkAuthenticator.ctime = sec;
508 ap.pkAuthenticator.cusec = usec;
509 ap.pkAuthenticator.nonce = nonce;
511 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
512 &ap, &size, ret);
513 free_AuthPack_Win2k(&ap);
514 if (ret) {
515 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
516 goto out;
518 if (buf.length != size)
519 krb5_abortx(context, "internal ASN1 encoder error");
521 oid = oid_id_pkcs7_data();
522 } else if (ctx->type == COMPAT_IETF) {
523 AuthPack ap;
525 memset(&ap, 0, sizeof(ap));
527 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
528 if (ret) {
529 free_AuthPack(&ap);
530 goto out;
533 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
534 free_AuthPack(&ap);
535 if (ret) {
536 krb5_set_error_string(context, "AuthPack: %d", ret);
537 goto out;
539 if (buf.length != size)
540 krb5_abortx(context, "internal ASN1 encoder error");
542 oid = oid_id_pkauthdata();
543 } else
544 krb5_abortx(context, "internal pkinit error");
546 ret = _krb5_pk_create_sign(context,
547 oid,
548 &buf,
549 ctx->id,
550 ctx->peer,
551 &sd_buf);
552 krb5_data_free(&buf);
553 if (ret)
554 goto out;
556 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
557 krb5_data_free(&sd_buf);
558 if (ret) {
559 krb5_set_error_string(context,
560 "ContentInfo wrapping of signedData failed");
561 goto out;
564 if (ctx->type == COMPAT_WIN2K) {
565 PA_PK_AS_REQ_Win2k winreq;
567 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
569 memset(&winreq, 0, sizeof(winreq));
571 winreq.signed_auth_pack = buf;
573 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
574 &winreq, &size, ret);
575 free_PA_PK_AS_REQ_Win2k(&winreq);
577 } else if (ctx->type == COMPAT_IETF) {
578 PA_PK_AS_REQ req;
580 pa_type = KRB5_PADATA_PK_AS_REQ;
582 memset(&req, 0, sizeof(req));
583 req.signedAuthPack = buf;
585 if (ctx->trustedCertifiers) {
587 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
588 if (req.trustedCertifiers == NULL) {
589 krb5_set_error_string(context, "malloc: out of memory");
590 free_PA_PK_AS_REQ(&req);
591 goto out;
593 ret = build_edi(context, ctx->id->hx509ctx,
594 ctx->id->anchors, req.trustedCertifiers);
595 if (ret) {
596 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
597 free_PA_PK_AS_REQ(&req);
598 goto out;
601 req.kdcPkId = NULL;
603 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
604 &req, &size, ret);
606 free_PA_PK_AS_REQ(&req);
608 } else
609 krb5_abortx(context, "internal pkinit error");
610 if (ret) {
611 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
612 goto out;
614 if (buf.length != size)
615 krb5_abortx(context, "Internal ASN1 encoder error");
617 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
618 if (ret)
619 free(buf.data);
621 if (ret == 0 && ctx->type == COMPAT_WIN2K)
622 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
624 out:
625 free_ContentInfo(&content_info);
627 return ret;
631 krb5_error_code KRB5_LIB_FUNCTION
632 _krb5_pk_mk_padata(krb5_context context,
633 void *c,
634 const KDC_REQ_BODY *req_body,
635 unsigned nonce,
636 METHOD_DATA *md)
638 krb5_pk_init_ctx ctx = c;
639 int win2k_compat;
641 win2k_compat = krb5_config_get_bool_default(context, NULL,
642 FALSE,
643 "realms",
644 req_body->realm,
645 "pkinit_win2k",
646 NULL);
648 if (win2k_compat) {
649 ctx->require_binding =
650 krb5_config_get_bool_default(context, NULL,
651 FALSE,
652 "realms",
653 req_body->realm,
654 "pkinit_win2k_require_binding",
655 NULL);
656 ctx->type = COMPAT_WIN2K;
657 } else
658 ctx->type = COMPAT_IETF;
660 ctx->require_eku =
661 krb5_config_get_bool_default(context, NULL,
662 TRUE,
663 "realms",
664 req_body->realm,
665 "pkinit_require_eku",
666 NULL);
667 ctx->require_krbtgt_otherName =
668 krb5_config_get_bool_default(context, NULL,
669 TRUE,
670 "realms",
671 req_body->realm,
672 "pkinit_require_krbtgt_otherName",
673 NULL);
675 ctx->require_hostname_match =
676 krb5_config_get_bool_default(context, NULL,
677 FALSE,
678 "realms",
679 req_body->realm,
680 "pkinit_require_hostname_match",
681 NULL);
683 ctx->trustedCertifiers =
684 krb5_config_get_bool_default(context, NULL,
685 TRUE,
686 "realms",
687 req_body->realm,
688 "pkinit_trustedCertifiers",
689 NULL);
691 return pk_mk_padata(context, ctx, req_body, nonce, md);
694 krb5_error_code KRB5_LIB_FUNCTION
695 _krb5_pk_verify_sign(krb5_context context,
696 const void *data,
697 size_t length,
698 struct krb5_pk_identity *id,
699 heim_oid *contentType,
700 krb5_data *content,
701 struct krb5_pk_cert **signer)
703 hx509_certs signer_certs;
704 int ret;
706 *signer = NULL;
708 ret = hx509_cms_verify_signed(id->hx509ctx,
709 id->verify_ctx,
710 data,
711 length,
712 NULL,
713 id->certpool,
714 contentType,
715 content,
716 &signer_certs);
717 if (ret) {
718 _krb5_pk_copy_error(context, id->hx509ctx, ret,
719 "CMS verify signed failed");
720 return ret;
723 *signer = calloc(1, sizeof(**signer));
724 if (*signer == NULL) {
725 krb5_clear_error_string(context);
726 ret = ENOMEM;
727 goto out;
730 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
731 if (ret) {
732 _krb5_pk_copy_error(context, id->hx509ctx, ret,
733 "Failed to get on of the signer certs");
734 goto out;
737 out:
738 hx509_certs_free(&signer_certs);
739 if (ret) {
740 if (*signer) {
741 hx509_cert_free((*signer)->cert);
742 free(*signer);
743 *signer = NULL;
747 return ret;
750 static krb5_error_code
751 get_reply_key_win(krb5_context context,
752 const krb5_data *content,
753 unsigned nonce,
754 krb5_keyblock **key)
756 ReplyKeyPack_Win2k key_pack;
757 krb5_error_code ret;
758 size_t size;
760 ret = decode_ReplyKeyPack_Win2k(content->data,
761 content->length,
762 &key_pack,
763 &size);
764 if (ret) {
765 krb5_set_error_string(context, "PKINIT decoding reply key failed");
766 free_ReplyKeyPack_Win2k(&key_pack);
767 return ret;
770 if (key_pack.nonce != nonce) {
771 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
772 free_ReplyKeyPack_Win2k(&key_pack);
773 return KRB5KRB_AP_ERR_MODIFIED;
776 *key = malloc (sizeof (**key));
777 if (*key == NULL) {
778 krb5_set_error_string(context, "PKINIT failed allocating reply key");
779 free_ReplyKeyPack_Win2k(&key_pack);
780 krb5_set_error_string(context, "malloc: out of memory");
781 return ENOMEM;
784 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
785 free_ReplyKeyPack_Win2k(&key_pack);
786 if (ret) {
787 krb5_set_error_string(context, "PKINIT failed copying reply key");
788 free(*key);
789 *key = NULL;
792 return ret;
795 static krb5_error_code
796 get_reply_key(krb5_context context,
797 const krb5_data *content,
798 const krb5_data *req_buffer,
799 krb5_keyblock **key)
801 ReplyKeyPack key_pack;
802 krb5_error_code ret;
803 size_t size;
805 ret = decode_ReplyKeyPack(content->data,
806 content->length,
807 &key_pack,
808 &size);
809 if (ret) {
810 krb5_set_error_string(context, "PKINIT decoding reply key failed");
811 free_ReplyKeyPack(&key_pack);
812 return ret;
816 krb5_crypto crypto;
819 * XXX Verify kp.replyKey is a allowed enctype in the
820 * configuration file
823 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
824 if (ret) {
825 free_ReplyKeyPack(&key_pack);
826 return ret;
829 ret = krb5_verify_checksum(context, crypto, 6,
830 req_buffer->data, req_buffer->length,
831 &key_pack.asChecksum);
832 krb5_crypto_destroy(context, crypto);
833 if (ret) {
834 free_ReplyKeyPack(&key_pack);
835 return ret;
839 *key = malloc (sizeof (**key));
840 if (*key == NULL) {
841 krb5_set_error_string(context, "PKINIT failed allocating reply key");
842 free_ReplyKeyPack(&key_pack);
843 krb5_set_error_string(context, "malloc: out of memory");
844 return ENOMEM;
847 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
848 free_ReplyKeyPack(&key_pack);
849 if (ret) {
850 krb5_set_error_string(context, "PKINIT failed copying reply key");
851 free(*key);
852 *key = NULL;
855 return ret;
859 static krb5_error_code
860 pk_verify_host(krb5_context context,
861 const char *realm,
862 const krb5_krbhst_info *hi,
863 struct krb5_pk_init_ctx_data *ctx,
864 struct krb5_pk_cert *host)
866 krb5_error_code ret = 0;
868 if (ctx->require_eku) {
869 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
870 oid_id_pkkdcekuoid(), 0);
871 if (ret) {
872 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
873 return ret;
876 if (ctx->require_krbtgt_otherName) {
877 hx509_octet_string_list list;
878 int i;
880 ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
881 host->cert,
882 oid_id_pkinit_san(),
883 &list);
884 if (ret) {
885 krb5_set_error_string(context, "Failed to find the PK-INIT "
886 "subjectAltName in the KDC certificate");
888 return ret;
891 for (i = 0; i < list.len; i++) {
892 KRB5PrincipalName r;
894 ret = decode_KRB5PrincipalName(list.val[i].data,
895 list.val[i].length,
897 NULL);
898 if (ret) {
899 krb5_set_error_string(context, "Failed to decode the PK-INIT "
900 "subjectAltName in the KDC certificate");
902 break;
905 if (r.principalName.name_string.len != 2 ||
906 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
907 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
908 strcmp(r.realm, realm) != 0)
910 krb5_set_error_string(context, "KDC have wrong realm name in "
911 "the certificate");
912 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
915 free_KRB5PrincipalName(&r);
916 if (ret)
917 break;
919 hx509_free_octet_string_list(&list);
921 if (ret)
922 return ret;
924 if (hi) {
925 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
926 ctx->require_hostname_match,
927 HX509_HN_HOSTNAME,
928 hi->hostname,
929 hi->ai->ai_addr, hi->ai->ai_addrlen);
931 if (ret)
932 krb5_set_error_string(context, "Address mismatch in "
933 "the KDC certificate");
935 return ret;
938 static krb5_error_code
939 pk_rd_pa_reply_enckey(krb5_context context,
940 int type,
941 const heim_octet_string *indata,
942 const heim_oid *dataType,
943 const char *realm,
944 krb5_pk_init_ctx ctx,
945 krb5_enctype etype,
946 const krb5_krbhst_info *hi,
947 unsigned nonce,
948 const krb5_data *req_buffer,
949 PA_DATA *pa,
950 krb5_keyblock **key)
952 krb5_error_code ret;
953 struct krb5_pk_cert *host = NULL;
954 krb5_data content;
955 heim_oid contentType = { 0, NULL };
957 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
958 krb5_set_error_string(context, "PKINIT: Invalid content type");
959 return EINVAL;
962 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
963 ctx->id->certs,
964 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
965 indata->data,
966 indata->length,
967 NULL,
968 &contentType,
969 &content);
970 if (ret) {
971 _krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
972 "Failed to unenvelope CMS data in PK-INIT reply");
973 return ret;
975 der_free_oid(&contentType);
977 #if 0 /* windows LH with interesting CMS packets, leaks memory */
979 size_t ph = 1 + der_length_len (length);
980 unsigned char *ptr = malloc(length + ph);
981 size_t l;
983 memcpy(ptr + ph, p, length);
985 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
986 ASN1_C_UNIV, CONS, UT_Sequence, &l);
987 if (ret)
988 return ret;
989 ptr += ph - l;
990 length += l;
991 p = ptr;
993 #endif
995 /* win2k uses ContentInfo */
996 if (type == COMPAT_WIN2K) {
997 heim_oid type;
998 heim_octet_string out;
1000 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1001 if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1002 ret = EINVAL; /* XXX */
1003 krb5_set_error_string(context, "PKINIT: Invalid content type");
1004 der_free_oid(&type);
1005 der_free_octet_string(&out);
1006 goto out;
1008 der_free_oid(&type);
1009 krb5_data_free(&content);
1010 ret = krb5_data_copy(&content, out.data, out.length);
1011 der_free_octet_string(&out);
1012 if (ret) {
1013 krb5_set_error_string(context, "PKINIT: out of memory");
1014 goto out;
1018 ret = _krb5_pk_verify_sign(context,
1019 content.data,
1020 content.length,
1021 ctx->id,
1022 &contentType,
1023 &content,
1024 &host);
1025 if (ret)
1026 goto out;
1028 /* make sure that it is the kdc's certificate */
1029 ret = pk_verify_host(context, realm, hi, ctx, host);
1030 if (ret) {
1031 goto out;
1034 #if 0
1035 if (type == COMPAT_WIN2K) {
1036 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1037 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1038 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1039 goto out;
1041 } else {
1042 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1043 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1044 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1045 goto out;
1048 #endif
1050 switch(type) {
1051 case COMPAT_WIN2K:
1052 ret = get_reply_key(context, &content, req_buffer, key);
1053 if (ret != 0 && ctx->require_binding == 0)
1054 ret = get_reply_key_win(context, &content, nonce, key);
1055 break;
1056 case COMPAT_IETF:
1057 ret = get_reply_key(context, &content, req_buffer, key);
1058 break;
1060 if (ret)
1061 goto out;
1063 /* XXX compare given etype with key->etype */
1065 out:
1066 if (host)
1067 _krb5_pk_cert_free(host);
1068 der_free_oid(&contentType);
1069 krb5_data_free(&content);
1071 return ret;
1074 static krb5_error_code
1075 pk_rd_pa_reply_dh(krb5_context context,
1076 const heim_octet_string *indata,
1077 const heim_oid *dataType,
1078 const char *realm,
1079 krb5_pk_init_ctx ctx,
1080 krb5_enctype etype,
1081 const krb5_krbhst_info *hi,
1082 const DHNonce *c_n,
1083 const DHNonce *k_n,
1084 unsigned nonce,
1085 PA_DATA *pa,
1086 krb5_keyblock **key)
1088 unsigned char *p, *dh_gen_key = NULL;
1089 struct krb5_pk_cert *host = NULL;
1090 BIGNUM *kdc_dh_pubkey = NULL;
1091 KDCDHKeyInfo kdc_dh_info;
1092 heim_oid contentType = { 0, NULL };
1093 krb5_data content;
1094 krb5_error_code ret;
1095 int dh_gen_keylen;
1096 size_t size;
1098 krb5_data_zero(&content);
1099 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1101 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1102 krb5_set_error_string(context, "PKINIT: Invalid content type");
1103 return EINVAL;
1106 ret = _krb5_pk_verify_sign(context,
1107 indata->data,
1108 indata->length,
1109 ctx->id,
1110 &contentType,
1111 &content,
1112 &host);
1113 if (ret)
1114 goto out;
1116 /* make sure that it is the kdc's certificate */
1117 ret = pk_verify_host(context, realm, hi, ctx, host);
1118 if (ret)
1119 goto out;
1121 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1122 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1123 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1124 goto out;
1127 ret = decode_KDCDHKeyInfo(content.data,
1128 content.length,
1129 &kdc_dh_info,
1130 &size);
1132 if (ret) {
1133 krb5_set_error_string(context, "pkinit - "
1134 "failed to decode KDC DH Key Info");
1135 goto out;
1138 if (kdc_dh_info.nonce != nonce) {
1139 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1140 ret = KRB5KRB_AP_ERR_MODIFIED;
1141 goto out;
1144 if (kdc_dh_info.dhKeyExpiration) {
1145 if (k_n == NULL) {
1146 krb5_set_error_string(context, "pkinit; got key expiration "
1147 "without server nonce");
1148 ret = KRB5KRB_ERR_GENERIC;
1149 goto out;
1151 if (c_n == NULL) {
1152 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1153 "client nonce");
1154 ret = KRB5KRB_ERR_GENERIC;
1155 goto out;
1157 } else {
1158 if (k_n) {
1159 krb5_set_error_string(context, "pkinit: got server nonce "
1160 "without key expiration");
1161 ret = KRB5KRB_ERR_GENERIC;
1162 goto out;
1164 c_n = NULL;
1168 p = kdc_dh_info.subjectPublicKey.data;
1169 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1172 DHPublicKey k;
1173 ret = decode_DHPublicKey(p, size, &k, NULL);
1174 if (ret) {
1175 krb5_set_error_string(context, "pkinit: can't decode "
1176 "without key expiration");
1177 goto out;
1180 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1181 free_DHPublicKey(&k);
1182 if (kdc_dh_pubkey == NULL) {
1183 ret = KRB5KRB_ERR_GENERIC;
1184 goto out;
1188 dh_gen_keylen = DH_size(ctx->dh);
1189 size = BN_num_bytes(ctx->dh->p);
1190 if (size < dh_gen_keylen)
1191 size = dh_gen_keylen;
1193 dh_gen_key = malloc(size);
1194 if (dh_gen_key == NULL) {
1195 krb5_set_error_string(context, "malloc: out of memory");
1196 ret = ENOMEM;
1197 goto out;
1199 memset(dh_gen_key, 0, size - dh_gen_keylen);
1201 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1202 kdc_dh_pubkey, ctx->dh);
1203 if (dh_gen_keylen == -1) {
1204 krb5_set_error_string(context,
1205 "PKINIT: Can't compute Diffie-Hellman key");
1206 ret = KRB5KRB_ERR_GENERIC;
1207 goto out;
1210 *key = malloc (sizeof (**key));
1211 if (*key == NULL) {
1212 krb5_set_error_string(context, "malloc: out of memory");
1213 ret = ENOMEM;
1214 goto out;
1217 ret = _krb5_pk_octetstring2key(context,
1218 etype,
1219 dh_gen_key, dh_gen_keylen,
1220 c_n, k_n,
1221 *key);
1222 if (ret) {
1223 krb5_set_error_string(context,
1224 "PKINIT: can't create key from DH key");
1225 free(*key);
1226 *key = NULL;
1227 goto out;
1230 out:
1231 if (kdc_dh_pubkey)
1232 BN_free(kdc_dh_pubkey);
1233 if (dh_gen_key) {
1234 memset(dh_gen_key, 0, DH_size(ctx->dh));
1235 free(dh_gen_key);
1237 if (host)
1238 _krb5_pk_cert_free(host);
1239 if (content.data)
1240 krb5_data_free(&content);
1241 der_free_oid(&contentType);
1242 free_KDCDHKeyInfo(&kdc_dh_info);
1244 return ret;
1247 krb5_error_code KRB5_LIB_FUNCTION
1248 _krb5_pk_rd_pa_reply(krb5_context context,
1249 const char *realm,
1250 void *c,
1251 krb5_enctype etype,
1252 const krb5_krbhst_info *hi,
1253 unsigned nonce,
1254 const krb5_data *req_buffer,
1255 PA_DATA *pa,
1256 krb5_keyblock **key)
1258 krb5_pk_init_ctx ctx = c;
1259 krb5_error_code ret;
1260 size_t size;
1262 /* Check for IETF PK-INIT first */
1263 if (ctx->type == COMPAT_IETF) {
1264 PA_PK_AS_REP rep;
1265 heim_octet_string os, data;
1266 heim_oid oid;
1268 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1269 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1270 return EINVAL;
1273 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1274 pa->padata_value.length,
1275 &rep,
1276 &size);
1277 if (ret) {
1278 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1279 return ret;
1282 switch (rep.element) {
1283 case choice_PA_PK_AS_REP_dhInfo:
1284 os = rep.u.dhInfo.dhSignedData;
1285 break;
1286 case choice_PA_PK_AS_REP_encKeyPack:
1287 os = rep.u.encKeyPack;
1288 break;
1289 default:
1290 free_PA_PK_AS_REP(&rep);
1291 krb5_set_error_string(context, "PKINIT: -27 reply "
1292 "invalid content type");
1293 return EINVAL;
1296 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1297 if (ret) {
1298 free_PA_PK_AS_REP(&rep);
1299 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1300 return ret;
1303 switch (rep.element) {
1304 case choice_PA_PK_AS_REP_dhInfo:
1305 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1306 ctx->clientDHNonce,
1307 rep.u.dhInfo.serverDHNonce,
1308 nonce, pa, key);
1309 break;
1310 case choice_PA_PK_AS_REP_encKeyPack:
1311 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,
1312 ctx, etype, hi, nonce, req_buffer, pa, key);
1313 break;
1314 default:
1315 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1317 der_free_octet_string(&data);
1318 der_free_oid(&oid);
1319 free_PA_PK_AS_REP(&rep);
1321 } else if (ctx->type == COMPAT_WIN2K) {
1322 PA_PK_AS_REP_Win2k w2krep;
1324 /* Check for Windows encoding of the AS-REP pa data */
1326 #if 0 /* should this be ? */
1327 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1328 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1329 return EINVAL;
1331 #endif
1333 memset(&w2krep, 0, sizeof(w2krep));
1335 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1336 pa->padata_value.length,
1337 &w2krep,
1338 &size);
1339 if (ret) {
1340 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1341 "pkinit reply %d", ret);
1342 return ret;
1345 krb5_clear_error_string(context);
1347 switch (w2krep.element) {
1348 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1349 heim_octet_string data;
1350 heim_oid oid;
1352 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1353 &oid, &data, NULL);
1354 free_PA_PK_AS_REP_Win2k(&w2krep);
1355 if (ret) {
1356 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1357 return ret;
1360 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1361 ctx, etype, hi, nonce, req_buffer, pa, key);
1362 der_free_octet_string(&data);
1363 der_free_oid(&oid);
1365 break;
1367 default:
1368 free_PA_PK_AS_REP_Win2k(&w2krep);
1369 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1370 "content type");
1371 ret = EINVAL;
1372 break;
1375 } else {
1376 krb5_set_error_string(context, "PKINIT: unknown reply type");
1377 ret = EINVAL;
1380 return ret;
1383 struct prompter {
1384 krb5_context context;
1385 krb5_prompter_fct prompter;
1386 void *prompter_data;
1389 static int
1390 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1392 krb5_error_code ret;
1393 krb5_prompt prompt;
1394 krb5_data password_data;
1395 struct prompter *p = data;
1397 password_data.data = prompter->reply.data;
1398 password_data.length = prompter->reply.length;
1400 prompt.prompt = prompter->prompt;
1401 prompt.hidden = hx509_prompt_hidden(prompter->type);
1402 prompt.reply = &password_data;
1404 switch (prompter->type) {
1405 case HX509_PROMPT_TYPE_INFO:
1406 prompt.type = KRB5_PROMPT_TYPE_INFO;
1407 break;
1408 case HX509_PROMPT_TYPE_PASSWORD:
1409 case HX509_PROMPT_TYPE_QUESTION:
1410 default:
1411 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1412 break;
1415 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1416 if (ret) {
1417 memset (prompter->reply.data, 0, prompter->reply.length);
1418 return 1;
1420 return 0;
1424 void KRB5_LIB_FUNCTION
1425 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1426 int boolean)
1428 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1432 krb5_error_code KRB5_LIB_FUNCTION
1433 _krb5_pk_load_id(krb5_context context,
1434 struct krb5_pk_identity **ret_id,
1435 const char *user_id,
1436 const char *anchor_id,
1437 char * const *chain_list,
1438 char * const *revoke_list,
1439 krb5_prompter_fct prompter,
1440 void *prompter_data,
1441 char *password)
1443 struct krb5_pk_identity *id = NULL;
1444 hx509_lock lock = NULL;
1445 struct prompter p;
1446 int ret;
1448 *ret_id = NULL;
1450 if (anchor_id == NULL) {
1451 krb5_set_error_string(context, "PKINIT: No anchor given");
1452 return HEIM_PKINIT_NO_VALID_CA;
1455 if (user_id == NULL) {
1456 krb5_set_error_string(context,
1457 "PKINIT: No user certificate given");
1458 return HEIM_PKINIT_NO_PRIVATE_KEY;
1461 /* load cert */
1463 id = calloc(1, sizeof(*id));
1464 if (id == NULL) {
1465 krb5_set_error_string(context, "malloc: out of memory");
1466 return ENOMEM;
1469 ret = hx509_context_init(&id->hx509ctx);
1470 if (ret)
1471 goto out;
1473 ret = hx509_lock_init(id->hx509ctx, &lock);
1474 if (password && password[0])
1475 hx509_lock_add_password(lock, password);
1477 if (prompter) {
1478 p.context = context;
1479 p.prompter = prompter;
1480 p.prompter_data = prompter_data;
1482 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1483 if (ret)
1484 goto out;
1487 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1488 if (ret) {
1489 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1490 "Failed to init cert certs");
1491 goto out;
1494 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1495 if (ret) {
1496 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1497 "Failed to init anchors");
1498 goto out;
1501 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1502 0, NULL, &id->certpool);
1503 if (ret) {
1504 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1505 "Failed to init chain");
1506 goto out;
1509 while (chain_list && *chain_list) {
1510 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1511 NULL, *chain_list);
1512 if (ret) {
1513 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1514 "Failed to laod chain %s",
1515 *chain_list);
1516 goto out;
1518 chain_list++;
1521 if (revoke_list) {
1522 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1523 if (ret) {
1524 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1525 "Failed init revoke list");
1526 goto out;
1529 while (*revoke_list) {
1530 ret = hx509_revoke_add_crl(id->hx509ctx,
1531 id->revokectx,
1532 *revoke_list);
1533 if (ret) {
1534 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1535 "Failed load revoke list");
1536 goto out;
1538 revoke_list++;
1540 } else
1541 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1543 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1544 if (ret) {
1545 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1546 "Failed init verify context");
1547 goto out;
1550 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1551 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1553 out:
1554 if (ret) {
1555 hx509_verify_destroy_ctx(id->verify_ctx);
1556 hx509_certs_free(&id->certs);
1557 hx509_certs_free(&id->anchors);
1558 hx509_certs_free(&id->certpool);
1559 hx509_revoke_free(&id->revokectx);
1560 hx509_context_free(&id->hx509ctx);
1561 free(id);
1562 } else
1563 *ret_id = id;
1565 hx509_lock_free(lock);
1567 return ret;
1570 static krb5_error_code
1571 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1572 struct krb5_dh_moduli **moduli)
1574 const struct krb5_dh_moduli *m;
1576 if (bits == 0) {
1577 m = moduli[1]; /* XXX */
1578 if (m == NULL)
1579 m = moduli[0]; /* XXX */
1580 } else {
1581 int i;
1582 for (i = 0; moduli[i] != NULL; i++) {
1583 if (bits < moduli[i]->bits)
1584 break;
1586 if (moduli[i] == NULL) {
1587 krb5_set_error_string(context,
1588 "Did not find a DH group parameter "
1589 "matching requirement of %lu bits",
1590 bits);
1591 return EINVAL;
1593 m = moduli[i];
1596 dh->p = integer_to_BN(context, "p", &m->p);
1597 if (dh->p == NULL)
1598 return ENOMEM;
1599 dh->g = integer_to_BN(context, "g", &m->g);
1600 if (dh->g == NULL)
1601 return ENOMEM;
1602 dh->q = integer_to_BN(context, "q", &m->q);
1603 if (dh->q == NULL)
1604 return ENOMEM;
1606 return 0;
1609 #endif /* PKINIT */
1611 static int
1612 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1613 const char *name, heim_integer *integer)
1615 int ret;
1616 char *p1;
1617 p1 = strsep(p, " \t");
1618 if (p1 == NULL) {
1619 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1620 file, name, lineno);
1621 return EINVAL;
1623 ret = der_parse_hex_heim_integer(p1, integer);
1624 if (ret) {
1625 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1626 "on line %d",
1627 file, name, lineno);
1628 return ret;
1631 return 0;
1634 krb5_error_code
1635 _krb5_parse_moduli_line(krb5_context context,
1636 const char *file,
1637 int lineno,
1638 char *p,
1639 struct krb5_dh_moduli **m)
1641 struct krb5_dh_moduli *m1;
1642 char *p1;
1643 int ret;
1645 *m = NULL;
1647 m1 = calloc(1, sizeof(*m1));
1648 if (m1 == NULL) {
1649 krb5_set_error_string(context, "malloc - out of memory");
1650 return ENOMEM;
1653 while (isspace((unsigned char)*p))
1654 p++;
1655 if (*p == '#')
1656 return 0;
1657 ret = EINVAL;
1659 p1 = strsep(&p, " \t");
1660 if (p1 == NULL) {
1661 krb5_set_error_string(context, "moduli file %s missing name "
1662 "on line %d", file, lineno);
1663 goto out;
1665 m1->name = strdup(p1);
1666 if (p1 == NULL) {
1667 krb5_set_error_string(context, "malloc - out of memeory");
1668 ret = ENOMEM;
1669 goto out;
1672 p1 = strsep(&p, " \t");
1673 if (p1 == NULL) {
1674 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1675 file, lineno);
1676 goto out;
1679 m1->bits = atoi(p1);
1680 if (m1->bits == 0) {
1681 krb5_set_error_string(context, "moduli file %s have un-parsable "
1682 "bits on line %d", file, lineno);
1683 goto out;
1686 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1687 if (ret)
1688 goto out;
1689 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1690 if (ret)
1691 goto out;
1692 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1693 if (ret)
1694 goto out;
1696 *m = m1;
1698 return 0;
1699 out:
1700 free(m1->name);
1701 der_free_heim_integer(&m1->p);
1702 der_free_heim_integer(&m1->g);
1703 der_free_heim_integer(&m1->q);
1704 free(m1);
1705 return ret;
1708 void
1709 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1711 int i;
1712 for (i = 0; moduli[i] != NULL; i++) {
1713 free(moduli[i]->name);
1714 der_free_heim_integer(&moduli[i]->p);
1715 der_free_heim_integer(&moduli[i]->g);
1716 der_free_heim_integer(&moduli[i]->q);
1717 free(moduli[i]);
1719 free(moduli);
1722 static const char *default_moduli_RFC2412_MODP_group2 =
1723 /* name */
1724 "RFC2412-MODP-group2 "
1725 /* bits */
1726 "1024 "
1727 /* p */
1728 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1729 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1730 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1731 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1732 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1733 "FFFFFFFF" "FFFFFFFF "
1734 /* g */
1735 "02 "
1736 /* q */
1737 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1738 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1739 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1740 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1741 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1742 "FFFFFFFF" "FFFFFFFF";
1744 static const char *default_moduli_rfc3526_MODP_group14 =
1745 /* name */
1746 "rfc3526-MODP-group14 "
1747 /* bits */
1748 "1760 "
1749 /* p */
1750 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1751 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1752 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1753 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1754 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1755 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1756 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1757 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1758 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1759 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1760 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1761 /* g */
1762 "02 "
1763 /* q */
1764 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1765 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1766 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1767 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1768 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1769 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1770 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1771 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1772 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1773 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1774 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1776 krb5_error_code
1777 _krb5_parse_moduli(krb5_context context, const char *file,
1778 struct krb5_dh_moduli ***moduli)
1780 /* name bits P G Q */
1781 krb5_error_code ret;
1782 struct krb5_dh_moduli **m = NULL, **m2;
1783 char buf[4096];
1784 FILE *f;
1785 int lineno = 0, n = 0;
1787 *moduli = NULL;
1789 m = calloc(1, sizeof(m[0]) * 3);
1790 if (m == NULL) {
1791 krb5_set_error_string(context, "malloc: out of memory");
1792 return ENOMEM;
1795 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1796 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1797 if (ret) {
1798 _krb5_free_moduli(m);
1799 return ret;
1801 n++;
1803 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1804 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
1805 if (ret) {
1806 _krb5_free_moduli(m);
1807 return ret;
1809 n++;
1812 if (file == NULL)
1813 file = MODULI_FILE;
1815 f = fopen(file, "r");
1816 if (f == NULL) {
1817 *moduli = m;
1818 return 0;
1821 while(fgets(buf, sizeof(buf), f) != NULL) {
1822 struct krb5_dh_moduli *element;
1824 buf[strcspn(buf, "\n")] = '\0';
1825 lineno++;
1827 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1828 if (m2 == NULL) {
1829 krb5_set_error_string(context, "malloc: out of memory");
1830 _krb5_free_moduli(m);
1831 return ENOMEM;
1833 m = m2;
1835 m[n] = NULL;
1837 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1838 if (ret) {
1839 _krb5_free_moduli(m);
1840 return ret;
1842 if (element == NULL)
1843 continue;
1845 m[n] = element;
1846 m[n + 1] = NULL;
1847 n++;
1849 *moduli = m;
1850 return 0;
1853 krb5_error_code
1854 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1855 heim_integer *p, heim_integer *g, heim_integer *q,
1856 struct krb5_dh_moduli **moduli,
1857 char **name)
1859 int i;
1861 if (name)
1862 *name = NULL;
1864 for (i = 0; moduli[i] != NULL; i++) {
1865 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1866 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1867 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1869 if (bits && bits > moduli[i]->bits) {
1870 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1871 "no accepted, not enough bits generated",
1872 moduli[i]->name);
1873 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1875 if (name)
1876 *name = strdup(moduli[i]->name);
1877 return 0;
1880 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1881 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1884 void KRB5_LIB_FUNCTION
1885 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1887 #ifdef PKINIT
1888 krb5_pk_init_ctx ctx;
1890 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1891 return;
1892 ctx = opt->opt_private->pk_init_ctx;
1893 if (ctx->dh)
1894 DH_free(ctx->dh);
1895 ctx->dh = NULL;
1896 if (ctx->id) {
1897 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1898 hx509_certs_free(&ctx->id->certs);
1899 hx509_certs_free(&ctx->id->anchors);
1900 hx509_certs_free(&ctx->id->certpool);
1901 hx509_context_free(&ctx->id->hx509ctx);
1903 if (ctx->clientDHNonce) {
1904 krb5_free_data(NULL, ctx->clientDHNonce);
1905 ctx->clientDHNonce = NULL;
1907 if (ctx->m)
1908 _krb5_free_moduli(ctx->m);
1909 free(ctx->id);
1910 ctx->id = NULL;
1912 free(opt->opt_private->pk_init_ctx);
1913 opt->opt_private->pk_init_ctx = NULL;
1914 #endif
1917 krb5_error_code KRB5_LIB_FUNCTION
1918 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1919 krb5_get_init_creds_opt *opt,
1920 krb5_principal principal,
1921 const char *user_id,
1922 const char *x509_anchors,
1923 char * const * pool,
1924 char * const * pki_revoke,
1925 int flags,
1926 krb5_prompter_fct prompter,
1927 void *prompter_data,
1928 char *password)
1930 #ifdef PKINIT
1931 krb5_error_code ret;
1932 char *anchors = NULL;
1934 if (opt->opt_private == NULL) {
1935 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1936 return EINVAL;
1939 opt->opt_private->pk_init_ctx =
1940 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1941 if (opt->opt_private->pk_init_ctx == NULL) {
1942 krb5_set_error_string(context, "malloc: out of memory");
1943 return ENOMEM;
1945 opt->opt_private->pk_init_ctx->dh = NULL;
1946 opt->opt_private->pk_init_ctx->id = NULL;
1947 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1948 opt->opt_private->pk_init_ctx->require_binding = 0;
1949 opt->opt_private->pk_init_ctx->require_eku = 1;
1950 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1951 opt->opt_private->pk_init_ctx->peer = NULL;
1953 /* XXX implement krb5_appdefault_strings */
1954 if (pool == NULL)
1955 pool = krb5_config_get_strings(context, NULL,
1956 "appdefaults",
1957 "pkinit_pool",
1958 NULL);
1960 if (pki_revoke == NULL)
1961 pki_revoke = krb5_config_get_strings(context, NULL,
1962 "appdefaults",
1963 "pkinit_revoke",
1964 NULL);
1966 if (x509_anchors == NULL) {
1967 krb5_appdefault_string(context, "kinit",
1968 krb5_principal_get_realm(context, principal),
1969 "pkinit_anchors", NULL, &anchors);
1970 x509_anchors = anchors;
1973 ret = _krb5_pk_load_id(context,
1974 &opt->opt_private->pk_init_ctx->id,
1975 user_id,
1976 x509_anchors,
1977 pool,
1978 pki_revoke,
1979 prompter,
1980 prompter_data,
1981 password);
1982 if (ret) {
1983 free(opt->opt_private->pk_init_ctx);
1984 opt->opt_private->pk_init_ctx = NULL;
1985 return ret;
1988 if ((flags & 2) == 0) {
1989 const char *moduli_file;
1990 unsigned long dh_min_bits;
1992 moduli_file = krb5_config_get_string(context, NULL,
1993 "libdefaults",
1994 "moduli",
1995 NULL);
1997 dh_min_bits =
1998 krb5_config_get_int_default(context, NULL, 0,
1999 "libdefaults",
2000 "pkinit_dh_min_bits",
2001 NULL);
2003 ret = _krb5_parse_moduli(context, moduli_file,
2004 &opt->opt_private->pk_init_ctx->m);
2005 if (ret) {
2006 _krb5_get_init_creds_opt_free_pkinit(opt);
2007 return ret;
2010 opt->opt_private->pk_init_ctx->dh = DH_new();
2011 if (opt->opt_private->pk_init_ctx->dh == NULL) {
2012 krb5_set_error_string(context, "malloc: out of memory");
2013 _krb5_get_init_creds_opt_free_pkinit(opt);
2014 return ENOMEM;
2017 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2018 dh_min_bits,
2019 opt->opt_private->pk_init_ctx->m);
2020 if (ret) {
2021 _krb5_get_init_creds_opt_free_pkinit(opt);
2022 return ret;
2025 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2026 krb5_set_error_string(context, "pkinit: failed to generate DH key");
2027 _krb5_get_init_creds_opt_free_pkinit(opt);
2028 return ENOMEM;
2032 return 0;
2033 #else
2034 krb5_set_error_string(context, "no support for PKINIT compiled in");
2035 return EINVAL;
2036 #endif
2043 static void
2044 _krb5_pk_copy_error(krb5_context context,
2045 hx509_context hx509ctx,
2046 int hxret,
2047 const char *fmt,
2048 ...)
2050 va_list va;
2051 char *s, *f;
2053 va_start(va, fmt);
2054 vasprintf(&f, fmt, va);
2055 va_end(va);
2056 if (f == NULL) {
2057 krb5_clear_error_string(context);
2058 return;
2061 s = hx509_get_error_string(hx509ctx, hxret);
2062 if (s == NULL) {
2063 krb5_clear_error_string(context);
2064 free(f);
2065 return;
2067 krb5_set_error_string(context, "%s: %s", f, s);
2068 free(s);
2069 free(f);