No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / lib / krb5 / pkinit.c
blob21f6b91c24b6a858f59d2c75478b6106519dbda0
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("$Heimdal: pkinit.c 22433 2008-01-13 14:11:46Z lha $"
37 "$NetBSD$");
39 struct krb5_dh_moduli {
40 char *name;
41 unsigned long bits;
42 heim_integer p;
43 heim_integer g;
44 heim_integer q;
47 #ifdef PKINIT
49 #include <heim_asn1.h>
50 #include <rfc2459_asn1.h>
51 #include <cms_asn1.h>
52 #include <pkcs8_asn1.h>
53 #include <pkcs9_asn1.h>
54 #include <pkcs12_asn1.h>
55 #include <pkinit_asn1.h>
56 #include <asn1_err.h>
58 #include <der.h>
60 #include <hx509.h>
62 enum {
63 COMPAT_WIN2K = 1,
64 COMPAT_IETF = 2
67 struct krb5_pk_identity {
68 hx509_context hx509ctx;
69 hx509_verify_ctx verify_ctx;
70 hx509_certs certs;
71 hx509_certs anchors;
72 hx509_certs certpool;
73 hx509_revoke_ctx revokectx;
76 struct krb5_pk_cert {
77 hx509_cert cert;
80 struct krb5_pk_init_ctx_data {
81 struct krb5_pk_identity *id;
82 DH *dh;
83 krb5_data *clientDHNonce;
84 struct krb5_dh_moduli **m;
85 hx509_peer_info peer;
86 int type;
87 unsigned int require_binding:1;
88 unsigned int require_eku:1;
89 unsigned int require_krbtgt_otherName:1;
90 unsigned int require_hostname_match:1;
91 unsigned int trustedCertifiers:1;
94 static void
95 _krb5_pk_copy_error(krb5_context context,
96 hx509_context hx509ctx,
97 int hxret,
98 const char *fmt,
99 ...)
100 __attribute__ ((format (printf, 4, 5)));
106 void KRB5_LIB_FUNCTION
107 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
109 if (cert->cert) {
110 hx509_cert_free(cert->cert);
112 free(cert);
115 static krb5_error_code
116 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
118 integer->length = BN_num_bytes(bn);
119 integer->data = malloc(integer->length);
120 if (integer->data == NULL) {
121 krb5_clear_error_string(context);
122 return ENOMEM;
124 BN_bn2bin(bn, integer->data);
125 integer->negative = BN_is_negative(bn);
126 return 0;
129 static BIGNUM *
130 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
132 BIGNUM *bn;
134 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
135 if (bn == NULL) {
136 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
137 return NULL;
139 BN_set_negative(bn, f->negative);
140 return bn;
144 static krb5_error_code
145 _krb5_pk_create_sign(krb5_context context,
146 const heim_oid *eContentType,
147 krb5_data *eContent,
148 struct krb5_pk_identity *id,
149 hx509_peer_info peer,
150 krb5_data *sd_data)
152 hx509_cert cert;
153 hx509_query *q;
154 int ret;
156 ret = hx509_query_alloc(id->hx509ctx, &q);
157 if (ret) {
158 _krb5_pk_copy_error(context, id->hx509ctx, ret,
159 "Allocate query to find signing certificate");
160 return ret;
163 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
164 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
166 ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
167 hx509_query_free(id->hx509ctx, q);
168 if (ret) {
169 _krb5_pk_copy_error(context, id->hx509ctx, ret,
170 "Find certificate to signed CMS data");
171 return ret;
174 ret = hx509_cms_create_signed_1(id->hx509ctx,
176 eContentType,
177 eContent->data,
178 eContent->length,
179 NULL,
180 cert,
181 peer,
182 NULL,
183 id->certs,
184 sd_data);
185 if (ret)
186 _krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
187 hx509_cert_free(cert);
189 return ret;
192 static int
193 cert2epi(hx509_context context, void *ctx, hx509_cert c)
195 ExternalPrincipalIdentifiers *ids = ctx;
196 ExternalPrincipalIdentifier id;
197 hx509_name subject = NULL;
198 void *p;
199 int ret;
201 memset(&id, 0, sizeof(id));
203 ret = hx509_cert_get_subject(c, &subject);
204 if (ret)
205 return ret;
207 if (hx509_name_is_null_p(subject) != 0) {
209 id.subjectName = calloc(1, sizeof(*id.subjectName));
210 if (id.subjectName == NULL) {
211 hx509_name_free(&subject);
212 free_ExternalPrincipalIdentifier(&id);
213 return ENOMEM;
216 ret = hx509_name_binary(subject, id.subjectName);
217 if (ret) {
218 hx509_name_free(&subject);
219 free_ExternalPrincipalIdentifier(&id);
220 return ret;
223 hx509_name_free(&subject);
226 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
227 if (id.issuerAndSerialNumber == NULL) {
228 free_ExternalPrincipalIdentifier(&id);
229 return ENOMEM;
233 IssuerAndSerialNumber iasn;
234 hx509_name issuer;
235 size_t size;
237 memset(&iasn, 0, sizeof(iasn));
239 ret = hx509_cert_get_issuer(c, &issuer);
240 if (ret) {
241 free_ExternalPrincipalIdentifier(&id);
242 return ret;
245 ret = hx509_name_to_Name(issuer, &iasn.issuer);
246 hx509_name_free(&issuer);
247 if (ret) {
248 free_ExternalPrincipalIdentifier(&id);
249 return ret;
252 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
253 if (ret) {
254 free_IssuerAndSerialNumber(&iasn);
255 free_ExternalPrincipalIdentifier(&id);
256 return ret;
259 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
260 id.issuerAndSerialNumber->data,
261 id.issuerAndSerialNumber->length,
262 &iasn, &size, ret);
263 free_IssuerAndSerialNumber(&iasn);
264 if (ret)
265 return ret;
266 if (id.issuerAndSerialNumber->length != size)
267 abort();
270 id.subjectKeyIdentifier = NULL;
272 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
273 if (p == NULL) {
274 free_ExternalPrincipalIdentifier(&id);
275 return ENOMEM;
278 ids->val = p;
279 ids->val[ids->len] = id;
280 ids->len++;
282 return 0;
285 static krb5_error_code
286 build_edi(krb5_context context,
287 hx509_context hx509ctx,
288 hx509_certs certs,
289 ExternalPrincipalIdentifiers *ids)
291 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
294 static krb5_error_code
295 build_auth_pack(krb5_context context,
296 unsigned nonce,
297 krb5_pk_init_ctx ctx,
298 DH *dh,
299 const KDC_REQ_BODY *body,
300 AuthPack *a)
302 size_t buf_size, len;
303 krb5_error_code ret;
304 void *buf;
305 krb5_timestamp sec;
306 int32_t usec;
307 Checksum checksum;
309 krb5_clear_error_string(context);
311 memset(&checksum, 0, sizeof(checksum));
313 krb5_us_timeofday(context, &sec, &usec);
314 a->pkAuthenticator.ctime = sec;
315 a->pkAuthenticator.nonce = nonce;
317 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
318 if (ret)
319 return ret;
320 if (buf_size != len)
321 krb5_abortx(context, "internal error in ASN.1 encoder");
323 ret = krb5_create_checksum(context,
324 NULL,
326 CKSUMTYPE_SHA1,
327 buf,
328 len,
329 &checksum);
330 free(buf);
331 if (ret)
332 return ret;
334 ALLOC(a->pkAuthenticator.paChecksum, 1);
335 if (a->pkAuthenticator.paChecksum == NULL) {
336 krb5_set_error_string(context, "malloc: out of memory");
337 return ENOMEM;
340 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
341 checksum.checksum.data, checksum.checksum.length);
342 free_Checksum(&checksum);
343 if (ret)
344 return ret;
346 if (dh) {
347 DomainParameters dp;
348 heim_integer dh_pub_key;
349 krb5_data dhbuf;
350 size_t size;
352 if (1 /* support_cached_dh */) {
353 ALLOC(a->clientDHNonce, 1);
354 if (a->clientDHNonce == NULL) {
355 krb5_clear_error_string(context);
356 return ENOMEM;
358 ret = krb5_data_alloc(a->clientDHNonce, 40);
359 if (a->clientDHNonce == NULL) {
360 krb5_clear_error_string(context);
361 return ENOMEM;
363 memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
364 ret = krb5_copy_data(context, a->clientDHNonce,
365 &ctx->clientDHNonce);
366 if (ret)
367 return ret;
370 ALLOC(a->clientPublicValue, 1);
371 if (a->clientPublicValue == NULL)
372 return ENOMEM;
373 ret = der_copy_oid(oid_id_dhpublicnumber(),
374 &a->clientPublicValue->algorithm.algorithm);
375 if (ret)
376 return ret;
378 memset(&dp, 0, sizeof(dp));
380 ret = BN_to_integer(context, dh->p, &dp.p);
381 if (ret) {
382 free_DomainParameters(&dp);
383 return ret;
385 ret = BN_to_integer(context, dh->g, &dp.g);
386 if (ret) {
387 free_DomainParameters(&dp);
388 return ret;
390 ret = BN_to_integer(context, dh->q, &dp.q);
391 if (ret) {
392 free_DomainParameters(&dp);
393 return ret;
395 dp.j = NULL;
396 dp.validationParms = NULL;
398 a->clientPublicValue->algorithm.parameters =
399 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
400 if (a->clientPublicValue->algorithm.parameters == NULL) {
401 free_DomainParameters(&dp);
402 return ret;
405 ASN1_MALLOC_ENCODE(DomainParameters,
406 a->clientPublicValue->algorithm.parameters->data,
407 a->clientPublicValue->algorithm.parameters->length,
408 &dp, &size, ret);
409 free_DomainParameters(&dp);
410 if (ret)
411 return ret;
412 if (size != a->clientPublicValue->algorithm.parameters->length)
413 krb5_abortx(context, "Internal ASN1 encoder error");
415 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
416 if (ret)
417 return ret;
419 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
420 &dh_pub_key, &size, ret);
421 der_free_heim_integer(&dh_pub_key);
422 if (ret)
423 return ret;
424 if (size != dhbuf.length)
425 krb5_abortx(context, "asn1 internal error");
427 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
428 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
432 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
433 if (a->supportedCMSTypes == NULL)
434 return ENOMEM;
436 ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
437 &a->supportedCMSTypes->val,
438 &a->supportedCMSTypes->len);
439 if (ret)
440 return ret;
443 return ret;
446 krb5_error_code KRB5_LIB_FUNCTION
447 _krb5_pk_mk_ContentInfo(krb5_context context,
448 const krb5_data *buf,
449 const heim_oid *oid,
450 struct ContentInfo *content_info)
452 krb5_error_code ret;
454 ret = der_copy_oid(oid, &content_info->contentType);
455 if (ret)
456 return ret;
457 ALLOC(content_info->content, 1);
458 if (content_info->content == NULL)
459 return ENOMEM;
460 content_info->content->data = malloc(buf->length);
461 if (content_info->content->data == NULL)
462 return ENOMEM;
463 memcpy(content_info->content->data, buf->data, buf->length);
464 content_info->content->length = buf->length;
465 return 0;
468 static krb5_error_code
469 pk_mk_padata(krb5_context context,
470 krb5_pk_init_ctx ctx,
471 const KDC_REQ_BODY *req_body,
472 unsigned nonce,
473 METHOD_DATA *md)
475 struct ContentInfo content_info;
476 krb5_error_code ret;
477 const heim_oid *oid;
478 size_t size;
479 krb5_data buf, sd_buf;
480 int pa_type;
482 krb5_data_zero(&buf);
483 krb5_data_zero(&sd_buf);
484 memset(&content_info, 0, sizeof(content_info));
486 if (ctx->type == COMPAT_WIN2K) {
487 AuthPack_Win2k ap;
488 krb5_timestamp sec;
489 int32_t usec;
491 memset(&ap, 0, sizeof(ap));
493 /* fill in PKAuthenticator */
494 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
495 if (ret) {
496 free_AuthPack_Win2k(&ap);
497 krb5_clear_error_string(context);
498 goto out;
500 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
501 if (ret) {
502 free_AuthPack_Win2k(&ap);
503 krb5_clear_error_string(context);
504 goto out;
507 krb5_us_timeofday(context, &sec, &usec);
508 ap.pkAuthenticator.ctime = sec;
509 ap.pkAuthenticator.cusec = usec;
510 ap.pkAuthenticator.nonce = nonce;
512 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
513 &ap, &size, ret);
514 free_AuthPack_Win2k(&ap);
515 if (ret) {
516 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
517 goto out;
519 if (buf.length != size)
520 krb5_abortx(context, "internal ASN1 encoder error");
522 oid = oid_id_pkcs7_data();
523 } else if (ctx->type == COMPAT_IETF) {
524 AuthPack ap;
526 memset(&ap, 0, sizeof(ap));
528 ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
529 if (ret) {
530 free_AuthPack(&ap);
531 goto out;
534 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
535 free_AuthPack(&ap);
536 if (ret) {
537 krb5_set_error_string(context, "AuthPack: %d", ret);
538 goto out;
540 if (buf.length != size)
541 krb5_abortx(context, "internal ASN1 encoder error");
543 oid = oid_id_pkauthdata();
544 } else
545 krb5_abortx(context, "internal pkinit error");
547 ret = _krb5_pk_create_sign(context,
548 oid,
549 &buf,
550 ctx->id,
551 ctx->peer,
552 &sd_buf);
553 krb5_data_free(&buf);
554 if (ret)
555 goto out;
557 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
558 krb5_data_free(&sd_buf);
559 if (ret) {
560 krb5_set_error_string(context,
561 "ContentInfo wrapping of signedData failed");
562 goto out;
565 if (ctx->type == COMPAT_WIN2K) {
566 PA_PK_AS_REQ_Win2k winreq;
568 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
570 memset(&winreq, 0, sizeof(winreq));
572 winreq.signed_auth_pack = buf;
574 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
575 &winreq, &size, ret);
576 free_PA_PK_AS_REQ_Win2k(&winreq);
578 } else if (ctx->type == COMPAT_IETF) {
579 PA_PK_AS_REQ req;
581 pa_type = KRB5_PADATA_PK_AS_REQ;
583 memset(&req, 0, sizeof(req));
584 req.signedAuthPack = buf;
586 if (ctx->trustedCertifiers) {
588 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
589 if (req.trustedCertifiers == NULL) {
590 krb5_set_error_string(context, "malloc: out of memory");
591 free_PA_PK_AS_REQ(&req);
592 goto out;
594 ret = build_edi(context, ctx->id->hx509ctx,
595 ctx->id->anchors, req.trustedCertifiers);
596 if (ret) {
597 krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
598 free_PA_PK_AS_REQ(&req);
599 goto out;
602 req.kdcPkId = NULL;
604 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
605 &req, &size, ret);
607 free_PA_PK_AS_REQ(&req);
609 } else
610 krb5_abortx(context, "internal pkinit error");
611 if (ret) {
612 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
613 goto out;
615 if (buf.length != size)
616 krb5_abortx(context, "Internal ASN1 encoder error");
618 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
619 if (ret)
620 free(buf.data);
622 if (ret == 0 && ctx->type == COMPAT_WIN2K)
623 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
625 out:
626 free_ContentInfo(&content_info);
628 return ret;
632 krb5_error_code KRB5_LIB_FUNCTION
633 _krb5_pk_mk_padata(krb5_context context,
634 void *c,
635 const KDC_REQ_BODY *req_body,
636 unsigned nonce,
637 METHOD_DATA *md)
639 krb5_pk_init_ctx ctx = c;
640 int win2k_compat;
642 win2k_compat = krb5_config_get_bool_default(context, NULL,
643 FALSE,
644 "realms",
645 req_body->realm,
646 "pkinit_win2k",
647 NULL);
649 if (win2k_compat) {
650 ctx->require_binding =
651 krb5_config_get_bool_default(context, NULL,
652 FALSE,
653 "realms",
654 req_body->realm,
655 "pkinit_win2k_require_binding",
656 NULL);
657 ctx->type = COMPAT_WIN2K;
658 } else
659 ctx->type = COMPAT_IETF;
661 ctx->require_eku =
662 krb5_config_get_bool_default(context, NULL,
663 TRUE,
664 "realms",
665 req_body->realm,
666 "pkinit_require_eku",
667 NULL);
668 ctx->require_krbtgt_otherName =
669 krb5_config_get_bool_default(context, NULL,
670 TRUE,
671 "realms",
672 req_body->realm,
673 "pkinit_require_krbtgt_otherName",
674 NULL);
676 ctx->require_hostname_match =
677 krb5_config_get_bool_default(context, NULL,
678 FALSE,
679 "realms",
680 req_body->realm,
681 "pkinit_require_hostname_match",
682 NULL);
684 ctx->trustedCertifiers =
685 krb5_config_get_bool_default(context, NULL,
686 TRUE,
687 "realms",
688 req_body->realm,
689 "pkinit_trustedCertifiers",
690 NULL);
692 return pk_mk_padata(context, ctx, req_body, nonce, md);
695 krb5_error_code KRB5_LIB_FUNCTION
696 _krb5_pk_verify_sign(krb5_context context,
697 const void *data,
698 size_t length,
699 struct krb5_pk_identity *id,
700 heim_oid *contentType,
701 krb5_data *content,
702 struct krb5_pk_cert **signer)
704 hx509_certs signer_certs;
705 int ret;
707 *signer = NULL;
709 ret = hx509_cms_verify_signed(id->hx509ctx,
710 id->verify_ctx,
711 data,
712 length,
713 NULL,
714 id->certpool,
715 contentType,
716 content,
717 &signer_certs);
718 if (ret) {
719 _krb5_pk_copy_error(context, id->hx509ctx, ret,
720 "CMS verify signed failed");
721 return ret;
724 *signer = calloc(1, sizeof(**signer));
725 if (*signer == NULL) {
726 krb5_clear_error_string(context);
727 ret = ENOMEM;
728 goto out;
731 ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
732 if (ret) {
733 _krb5_pk_copy_error(context, id->hx509ctx, ret,
734 "Failed to get on of the signer certs");
735 goto out;
738 out:
739 hx509_certs_free(&signer_certs);
740 if (ret) {
741 if (*signer) {
742 hx509_cert_free((*signer)->cert);
743 free(*signer);
744 *signer = NULL;
748 return ret;
751 static krb5_error_code
752 get_reply_key_win(krb5_context context,
753 const krb5_data *content,
754 unsigned nonce,
755 krb5_keyblock **key)
757 ReplyKeyPack_Win2k key_pack;
758 krb5_error_code ret;
759 size_t size;
761 ret = decode_ReplyKeyPack_Win2k(content->data,
762 content->length,
763 &key_pack,
764 &size);
765 if (ret) {
766 krb5_set_error_string(context, "PKINIT decoding reply key failed");
767 free_ReplyKeyPack_Win2k(&key_pack);
768 return ret;
771 if (key_pack.nonce != nonce) {
772 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
773 free_ReplyKeyPack_Win2k(&key_pack);
774 return KRB5KRB_AP_ERR_MODIFIED;
777 *key = malloc (sizeof (**key));
778 if (*key == NULL) {
779 krb5_set_error_string(context, "PKINIT failed allocating reply key");
780 free_ReplyKeyPack_Win2k(&key_pack);
781 krb5_set_error_string(context, "malloc: out of memory");
782 return ENOMEM;
785 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
786 free_ReplyKeyPack_Win2k(&key_pack);
787 if (ret) {
788 krb5_set_error_string(context, "PKINIT failed copying reply key");
789 free(*key);
790 *key = NULL;
793 return ret;
796 static krb5_error_code
797 get_reply_key(krb5_context context,
798 const krb5_data *content,
799 const krb5_data *req_buffer,
800 krb5_keyblock **key)
802 ReplyKeyPack key_pack;
803 krb5_error_code ret;
804 size_t size;
806 ret = decode_ReplyKeyPack(content->data,
807 content->length,
808 &key_pack,
809 &size);
810 if (ret) {
811 krb5_set_error_string(context, "PKINIT decoding reply key failed");
812 free_ReplyKeyPack(&key_pack);
813 return ret;
817 krb5_crypto crypto;
820 * XXX Verify kp.replyKey is a allowed enctype in the
821 * configuration file
824 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
825 if (ret) {
826 free_ReplyKeyPack(&key_pack);
827 return ret;
830 ret = krb5_verify_checksum(context, crypto, 6,
831 req_buffer->data, req_buffer->length,
832 &key_pack.asChecksum);
833 krb5_crypto_destroy(context, crypto);
834 if (ret) {
835 free_ReplyKeyPack(&key_pack);
836 return ret;
840 *key = malloc (sizeof (**key));
841 if (*key == NULL) {
842 krb5_set_error_string(context, "PKINIT failed allocating reply key");
843 free_ReplyKeyPack(&key_pack);
844 krb5_set_error_string(context, "malloc: out of memory");
845 return ENOMEM;
848 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
849 free_ReplyKeyPack(&key_pack);
850 if (ret) {
851 krb5_set_error_string(context, "PKINIT failed copying reply key");
852 free(*key);
853 *key = NULL;
856 return ret;
860 static krb5_error_code
861 pk_verify_host(krb5_context context,
862 const char *realm,
863 const krb5_krbhst_info *hi,
864 struct krb5_pk_init_ctx_data *ctx,
865 struct krb5_pk_cert *host)
867 krb5_error_code ret = 0;
869 if (ctx->require_eku) {
870 ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
871 oid_id_pkkdcekuoid(), 0);
872 if (ret) {
873 krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
874 return ret;
877 if (ctx->require_krbtgt_otherName) {
878 hx509_octet_string_list list;
879 int i;
881 ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
882 host->cert,
883 oid_id_pkinit_san(),
884 &list);
885 if (ret) {
886 krb5_set_error_string(context, "Failed to find the PK-INIT "
887 "subjectAltName in the KDC certificate");
889 return ret;
892 for (i = 0; i < list.len; i++) {
893 KRB5PrincipalName r;
895 ret = decode_KRB5PrincipalName(list.val[i].data,
896 list.val[i].length,
898 NULL);
899 if (ret) {
900 krb5_set_error_string(context, "Failed to decode the PK-INIT "
901 "subjectAltName in the KDC certificate");
903 break;
906 if (r.principalName.name_string.len != 2 ||
907 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
908 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
909 strcmp(r.realm, realm) != 0)
911 krb5_set_error_string(context, "KDC have wrong realm name in "
912 "the certificate");
913 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
916 free_KRB5PrincipalName(&r);
917 if (ret)
918 break;
920 hx509_free_octet_string_list(&list);
922 if (ret)
923 return ret;
925 if (hi) {
926 ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
927 ctx->require_hostname_match,
928 HX509_HN_HOSTNAME,
929 hi->hostname,
930 hi->ai->ai_addr, hi->ai->ai_addrlen);
932 if (ret)
933 krb5_set_error_string(context, "Address mismatch in "
934 "the KDC certificate");
936 return ret;
939 static krb5_error_code
940 pk_rd_pa_reply_enckey(krb5_context context,
941 int type,
942 const heim_octet_string *indata,
943 const heim_oid *dataType,
944 const char *realm,
945 krb5_pk_init_ctx ctx,
946 krb5_enctype etype,
947 const krb5_krbhst_info *hi,
948 unsigned nonce,
949 const krb5_data *req_buffer,
950 PA_DATA *pa,
951 krb5_keyblock **key)
953 krb5_error_code ret;
954 struct krb5_pk_cert *host = NULL;
955 krb5_data content;
956 heim_oid contentType = { 0, NULL };
958 if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
959 krb5_set_error_string(context, "PKINIT: Invalid content type");
960 return EINVAL;
963 ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
964 ctx->id->certs,
965 HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
966 indata->data,
967 indata->length,
968 NULL,
969 &contentType,
970 &content);
971 if (ret) {
972 _krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
973 "Failed to unenvelope CMS data in PK-INIT reply");
974 return ret;
976 der_free_oid(&contentType);
978 #if 0 /* windows LH with interesting CMS packets, leaks memory */
980 size_t ph = 1 + der_length_len (length);
981 unsigned char *ptr = malloc(length + ph);
982 size_t l;
984 memcpy(ptr + ph, p, length);
986 ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
987 ASN1_C_UNIV, CONS, UT_Sequence, &l);
988 if (ret)
989 return ret;
990 ptr += ph - l;
991 length += l;
992 p = ptr;
994 #endif
996 /* win2k uses ContentInfo */
997 if (type == COMPAT_WIN2K) {
998 heim_oid type;
999 heim_octet_string out;
1001 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1002 if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1003 ret = EINVAL; /* XXX */
1004 krb5_set_error_string(context, "PKINIT: Invalid content type");
1005 der_free_oid(&type);
1006 der_free_octet_string(&out);
1007 goto out;
1009 der_free_oid(&type);
1010 krb5_data_free(&content);
1011 ret = krb5_data_copy(&content, out.data, out.length);
1012 der_free_octet_string(&out);
1013 if (ret) {
1014 krb5_set_error_string(context, "PKINIT: out of memory");
1015 goto out;
1019 ret = _krb5_pk_verify_sign(context,
1020 content.data,
1021 content.length,
1022 ctx->id,
1023 &contentType,
1024 &content,
1025 &host);
1026 if (ret)
1027 goto out;
1029 /* make sure that it is the kdc's certificate */
1030 ret = pk_verify_host(context, realm, hi, ctx, host);
1031 if (ret) {
1032 goto out;
1035 #if 0
1036 if (type == COMPAT_WIN2K) {
1037 if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1038 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1039 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1040 goto out;
1042 } else {
1043 if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1044 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1045 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1046 goto out;
1049 #endif
1051 switch(type) {
1052 case COMPAT_WIN2K:
1053 ret = get_reply_key(context, &content, req_buffer, key);
1054 if (ret != 0 && ctx->require_binding == 0)
1055 ret = get_reply_key_win(context, &content, nonce, key);
1056 break;
1057 case COMPAT_IETF:
1058 ret = get_reply_key(context, &content, req_buffer, key);
1059 break;
1061 if (ret)
1062 goto out;
1064 /* XXX compare given etype with key->etype */
1066 out:
1067 if (host)
1068 _krb5_pk_cert_free(host);
1069 der_free_oid(&contentType);
1070 krb5_data_free(&content);
1072 return ret;
1075 static krb5_error_code
1076 pk_rd_pa_reply_dh(krb5_context context,
1077 const heim_octet_string *indata,
1078 const heim_oid *dataType,
1079 const char *realm,
1080 krb5_pk_init_ctx ctx,
1081 krb5_enctype etype,
1082 const krb5_krbhst_info *hi,
1083 const DHNonce *c_n,
1084 const DHNonce *k_n,
1085 unsigned nonce,
1086 PA_DATA *pa,
1087 krb5_keyblock **key)
1089 unsigned char *p, *dh_gen_key = NULL;
1090 struct krb5_pk_cert *host = NULL;
1091 BIGNUM *kdc_dh_pubkey = NULL;
1092 KDCDHKeyInfo kdc_dh_info;
1093 heim_oid contentType = { 0, NULL };
1094 krb5_data content;
1095 krb5_error_code ret;
1096 int dh_gen_keylen;
1097 size_t size;
1099 krb5_data_zero(&content);
1100 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1102 if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1103 krb5_set_error_string(context, "PKINIT: Invalid content type");
1104 return EINVAL;
1107 ret = _krb5_pk_verify_sign(context,
1108 indata->data,
1109 indata->length,
1110 ctx->id,
1111 &contentType,
1112 &content,
1113 &host);
1114 if (ret)
1115 goto out;
1117 /* make sure that it is the kdc's certificate */
1118 ret = pk_verify_host(context, realm, hi, ctx, host);
1119 if (ret)
1120 goto out;
1122 if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1123 krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1124 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1125 goto out;
1128 ret = decode_KDCDHKeyInfo(content.data,
1129 content.length,
1130 &kdc_dh_info,
1131 &size);
1133 if (ret) {
1134 krb5_set_error_string(context, "pkinit - "
1135 "failed to decode KDC DH Key Info");
1136 goto out;
1139 if (kdc_dh_info.nonce != nonce) {
1140 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1141 ret = KRB5KRB_AP_ERR_MODIFIED;
1142 goto out;
1145 if (kdc_dh_info.dhKeyExpiration) {
1146 if (k_n == NULL) {
1147 krb5_set_error_string(context, "pkinit; got key expiration "
1148 "without server nonce");
1149 ret = KRB5KRB_ERR_GENERIC;
1150 goto out;
1152 if (c_n == NULL) {
1153 krb5_set_error_string(context, "pkinit; got DH reuse but no "
1154 "client nonce");
1155 ret = KRB5KRB_ERR_GENERIC;
1156 goto out;
1158 } else {
1159 if (k_n) {
1160 krb5_set_error_string(context, "pkinit: got server nonce "
1161 "without key expiration");
1162 ret = KRB5KRB_ERR_GENERIC;
1163 goto out;
1165 c_n = NULL;
1169 p = kdc_dh_info.subjectPublicKey.data;
1170 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1173 DHPublicKey k;
1174 ret = decode_DHPublicKey(p, size, &k, NULL);
1175 if (ret) {
1176 krb5_set_error_string(context, "pkinit: can't decode "
1177 "without key expiration");
1178 goto out;
1181 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1182 free_DHPublicKey(&k);
1183 if (kdc_dh_pubkey == NULL) {
1184 ret = KRB5KRB_ERR_GENERIC;
1185 goto out;
1189 dh_gen_keylen = DH_size(ctx->dh);
1190 size = BN_num_bytes(ctx->dh->p);
1191 if (size < dh_gen_keylen)
1192 size = dh_gen_keylen;
1194 dh_gen_key = malloc(size);
1195 if (dh_gen_key == NULL) {
1196 krb5_set_error_string(context, "malloc: out of memory");
1197 ret = ENOMEM;
1198 goto out;
1200 memset(dh_gen_key, 0, size - dh_gen_keylen);
1202 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1203 kdc_dh_pubkey, ctx->dh);
1204 if (dh_gen_keylen == -1) {
1205 krb5_set_error_string(context,
1206 "PKINIT: Can't compute Diffie-Hellman key");
1207 ret = KRB5KRB_ERR_GENERIC;
1208 goto out;
1211 *key = malloc (sizeof (**key));
1212 if (*key == NULL) {
1213 krb5_set_error_string(context, "malloc: out of memory");
1214 ret = ENOMEM;
1215 goto out;
1218 ret = _krb5_pk_octetstring2key(context,
1219 etype,
1220 dh_gen_key, dh_gen_keylen,
1221 c_n, k_n,
1222 *key);
1223 if (ret) {
1224 krb5_set_error_string(context,
1225 "PKINIT: can't create key from DH key");
1226 free(*key);
1227 *key = NULL;
1228 goto out;
1231 out:
1232 if (kdc_dh_pubkey)
1233 BN_free(kdc_dh_pubkey);
1234 if (dh_gen_key) {
1235 memset(dh_gen_key, 0, DH_size(ctx->dh));
1236 free(dh_gen_key);
1238 if (host)
1239 _krb5_pk_cert_free(host);
1240 if (content.data)
1241 krb5_data_free(&content);
1242 der_free_oid(&contentType);
1243 free_KDCDHKeyInfo(&kdc_dh_info);
1245 return ret;
1248 krb5_error_code KRB5_LIB_FUNCTION
1249 _krb5_pk_rd_pa_reply(krb5_context context,
1250 const char *realm,
1251 void *c,
1252 krb5_enctype etype,
1253 const krb5_krbhst_info *hi,
1254 unsigned nonce,
1255 const krb5_data *req_buffer,
1256 PA_DATA *pa,
1257 krb5_keyblock **key)
1259 krb5_pk_init_ctx ctx = c;
1260 krb5_error_code ret;
1261 size_t size;
1263 /* Check for IETF PK-INIT first */
1264 if (ctx->type == COMPAT_IETF) {
1265 PA_PK_AS_REP rep;
1266 heim_octet_string os, data;
1267 heim_oid oid;
1269 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1270 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1271 return EINVAL;
1274 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1275 pa->padata_value.length,
1276 &rep,
1277 &size);
1278 if (ret) {
1279 krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1280 return ret;
1283 switch (rep.element) {
1284 case choice_PA_PK_AS_REP_dhInfo:
1285 os = rep.u.dhInfo.dhSignedData;
1286 break;
1287 case choice_PA_PK_AS_REP_encKeyPack:
1288 os = rep.u.encKeyPack;
1289 break;
1290 default:
1291 free_PA_PK_AS_REP(&rep);
1292 krb5_set_error_string(context, "PKINIT: -27 reply "
1293 "invalid content type");
1294 return EINVAL;
1297 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1298 if (ret) {
1299 free_PA_PK_AS_REP(&rep);
1300 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1301 return ret;
1304 switch (rep.element) {
1305 case choice_PA_PK_AS_REP_dhInfo:
1306 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1307 ctx->clientDHNonce,
1308 rep.u.dhInfo.serverDHNonce,
1309 nonce, pa, key);
1310 break;
1311 case choice_PA_PK_AS_REP_encKeyPack:
1312 ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,
1313 ctx, etype, hi, nonce, req_buffer, pa, key);
1314 break;
1315 default:
1316 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1318 der_free_octet_string(&data);
1319 der_free_oid(&oid);
1320 free_PA_PK_AS_REP(&rep);
1322 } else if (ctx->type == COMPAT_WIN2K) {
1323 PA_PK_AS_REP_Win2k w2krep;
1325 /* Check for Windows encoding of the AS-REP pa data */
1327 #if 0 /* should this be ? */
1328 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1329 krb5_set_error_string(context, "PKINIT: wrong padata recv");
1330 return EINVAL;
1332 #endif
1334 memset(&w2krep, 0, sizeof(w2krep));
1336 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1337 pa->padata_value.length,
1338 &w2krep,
1339 &size);
1340 if (ret) {
1341 krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1342 "pkinit reply %d", ret);
1343 return ret;
1346 krb5_clear_error_string(context);
1348 switch (w2krep.element) {
1349 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1350 heim_octet_string data;
1351 heim_oid oid;
1353 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1354 &oid, &data, NULL);
1355 free_PA_PK_AS_REP_Win2k(&w2krep);
1356 if (ret) {
1357 krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1358 return ret;
1361 ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1362 ctx, etype, hi, nonce, req_buffer, pa, key);
1363 der_free_octet_string(&data);
1364 der_free_oid(&oid);
1366 break;
1368 default:
1369 free_PA_PK_AS_REP_Win2k(&w2krep);
1370 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1371 "content type");
1372 ret = EINVAL;
1373 break;
1376 } else {
1377 krb5_set_error_string(context, "PKINIT: unknown reply type");
1378 ret = EINVAL;
1381 return ret;
1384 struct prompter {
1385 krb5_context context;
1386 krb5_prompter_fct prompter;
1387 void *prompter_data;
1390 static int
1391 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1393 krb5_error_code ret;
1394 krb5_prompt prompt;
1395 krb5_data password_data;
1396 struct prompter *p = data;
1398 password_data.data = prompter->reply.data;
1399 password_data.length = prompter->reply.length;
1401 prompt.prompt = prompter->prompt;
1402 prompt.hidden = hx509_prompt_hidden(prompter->type);
1403 prompt.reply = &password_data;
1405 switch (prompter->type) {
1406 case HX509_PROMPT_TYPE_INFO:
1407 prompt.type = KRB5_PROMPT_TYPE_INFO;
1408 break;
1409 case HX509_PROMPT_TYPE_PASSWORD:
1410 case HX509_PROMPT_TYPE_QUESTION:
1411 default:
1412 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1413 break;
1416 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1417 if (ret) {
1418 memset (prompter->reply.data, 0, prompter->reply.length);
1419 return 1;
1421 return 0;
1425 void KRB5_LIB_FUNCTION
1426 _krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1427 int boolean)
1429 hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1433 krb5_error_code KRB5_LIB_FUNCTION
1434 _krb5_pk_load_id(krb5_context context,
1435 struct krb5_pk_identity **ret_id,
1436 const char *user_id,
1437 const char *anchor_id,
1438 char * const *chain_list,
1439 char * const *revoke_list,
1440 krb5_prompter_fct prompter,
1441 void *prompter_data,
1442 char *password)
1444 struct krb5_pk_identity *id = NULL;
1445 hx509_lock lock = NULL;
1446 struct prompter p;
1447 int ret;
1449 *ret_id = NULL;
1451 if (anchor_id == NULL) {
1452 krb5_set_error_string(context, "PKINIT: No anchor given");
1453 return HEIM_PKINIT_NO_VALID_CA;
1456 if (user_id == NULL) {
1457 krb5_set_error_string(context,
1458 "PKINIT: No user certificate given");
1459 return HEIM_PKINIT_NO_PRIVATE_KEY;
1462 /* load cert */
1464 id = calloc(1, sizeof(*id));
1465 if (id == NULL) {
1466 krb5_set_error_string(context, "malloc: out of memory");
1467 return ENOMEM;
1470 ret = hx509_context_init(&id->hx509ctx);
1471 if (ret)
1472 goto out;
1474 ret = hx509_lock_init(id->hx509ctx, &lock);
1475 if (password && password[0])
1476 hx509_lock_add_password(lock, password);
1478 if (prompter) {
1479 p.context = context;
1480 p.prompter = prompter;
1481 p.prompter_data = prompter_data;
1483 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1484 if (ret)
1485 goto out;
1488 ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1489 if (ret) {
1490 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1491 "Failed to init cert certs");
1492 goto out;
1495 ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1496 if (ret) {
1497 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1498 "Failed to init anchors");
1499 goto out;
1502 ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1503 0, NULL, &id->certpool);
1504 if (ret) {
1505 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1506 "Failed to init chain");
1507 goto out;
1510 while (chain_list && *chain_list) {
1511 ret = hx509_certs_append(id->hx509ctx, id->certpool,
1512 NULL, *chain_list);
1513 if (ret) {
1514 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1515 "Failed to laod chain %s",
1516 *chain_list);
1517 goto out;
1519 chain_list++;
1522 if (revoke_list) {
1523 ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1524 if (ret) {
1525 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1526 "Failed init revoke list");
1527 goto out;
1530 while (*revoke_list) {
1531 ret = hx509_revoke_add_crl(id->hx509ctx,
1532 id->revokectx,
1533 *revoke_list);
1534 if (ret) {
1535 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1536 "Failed load revoke list");
1537 goto out;
1539 revoke_list++;
1541 } else
1542 hx509_context_set_missing_revoke(id->hx509ctx, 1);
1544 ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1545 if (ret) {
1546 _krb5_pk_copy_error(context, id->hx509ctx, ret,
1547 "Failed init verify context");
1548 goto out;
1551 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1552 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1554 out:
1555 if (ret) {
1556 hx509_verify_destroy_ctx(id->verify_ctx);
1557 hx509_certs_free(&id->certs);
1558 hx509_certs_free(&id->anchors);
1559 hx509_certs_free(&id->certpool);
1560 hx509_revoke_free(&id->revokectx);
1561 hx509_context_free(&id->hx509ctx);
1562 free(id);
1563 } else
1564 *ret_id = id;
1566 hx509_lock_free(lock);
1568 return ret;
1571 static krb5_error_code
1572 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
1573 struct krb5_dh_moduli **moduli)
1575 const struct krb5_dh_moduli *m;
1577 if (bits == 0) {
1578 m = moduli[1]; /* XXX */
1579 if (m == NULL)
1580 m = moduli[0]; /* XXX */
1581 } else {
1582 int i;
1583 for (i = 0; moduli[i] != NULL; i++) {
1584 if (bits < moduli[i]->bits)
1585 break;
1587 if (moduli[i] == NULL) {
1588 krb5_set_error_string(context,
1589 "Did not find a DH group parameter "
1590 "matching requirement of %lu bits",
1591 bits);
1592 return EINVAL;
1594 m = moduli[i];
1597 dh->p = integer_to_BN(context, "p", &m->p);
1598 if (dh->p == NULL)
1599 return ENOMEM;
1600 dh->g = integer_to_BN(context, "g", &m->g);
1601 if (dh->g == NULL)
1602 return ENOMEM;
1603 dh->q = integer_to_BN(context, "q", &m->q);
1604 if (dh->q == NULL)
1605 return ENOMEM;
1607 return 0;
1610 #endif /* PKINIT */
1612 static int
1613 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1614 const char *name, heim_integer *integer)
1616 int ret;
1617 char *p1;
1618 p1 = strsep(p, " \t");
1619 if (p1 == NULL) {
1620 krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1621 file, name, lineno);
1622 return EINVAL;
1624 ret = der_parse_hex_heim_integer(p1, integer);
1625 if (ret) {
1626 krb5_set_error_string(context, "moduli file %s failed parsing %s "
1627 "on line %d",
1628 file, name, lineno);
1629 return ret;
1632 return 0;
1635 krb5_error_code
1636 _krb5_parse_moduli_line(krb5_context context,
1637 const char *file,
1638 int lineno,
1639 char *p,
1640 struct krb5_dh_moduli **m)
1642 struct krb5_dh_moduli *m1;
1643 char *p1;
1644 int ret;
1646 *m = NULL;
1648 m1 = calloc(1, sizeof(*m1));
1649 if (m1 == NULL) {
1650 krb5_set_error_string(context, "malloc - out of memory");
1651 return ENOMEM;
1654 while (isspace((unsigned char)*p))
1655 p++;
1656 if (*p == '#')
1657 return 0;
1658 ret = EINVAL;
1660 p1 = strsep(&p, " \t");
1661 if (p1 == NULL) {
1662 krb5_set_error_string(context, "moduli file %s missing name "
1663 "on line %d", file, lineno);
1664 goto out;
1666 m1->name = strdup(p1);
1667 if (p1 == NULL) {
1668 krb5_set_error_string(context, "malloc - out of memeory");
1669 ret = ENOMEM;
1670 goto out;
1673 p1 = strsep(&p, " \t");
1674 if (p1 == NULL) {
1675 krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1676 file, lineno);
1677 goto out;
1680 m1->bits = atoi(p1);
1681 if (m1->bits == 0) {
1682 krb5_set_error_string(context, "moduli file %s have un-parsable "
1683 "bits on line %d", file, lineno);
1684 goto out;
1687 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1688 if (ret)
1689 goto out;
1690 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1691 if (ret)
1692 goto out;
1693 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1694 if (ret)
1695 goto out;
1697 *m = m1;
1699 return 0;
1700 out:
1701 free(m1->name);
1702 der_free_heim_integer(&m1->p);
1703 der_free_heim_integer(&m1->g);
1704 der_free_heim_integer(&m1->q);
1705 free(m1);
1706 return ret;
1709 void
1710 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
1712 int i;
1713 for (i = 0; moduli[i] != NULL; i++) {
1714 free(moduli[i]->name);
1715 der_free_heim_integer(&moduli[i]->p);
1716 der_free_heim_integer(&moduli[i]->g);
1717 der_free_heim_integer(&moduli[i]->q);
1718 free(moduli[i]);
1720 free(moduli);
1723 static const char *default_moduli_RFC2412_MODP_group2 =
1724 /* name */
1725 "RFC2412-MODP-group2 "
1726 /* bits */
1727 "1024 "
1728 /* p */
1729 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1730 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1731 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1732 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1733 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1734 "FFFFFFFF" "FFFFFFFF "
1735 /* g */
1736 "02 "
1737 /* q */
1738 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1739 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1740 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1741 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1742 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1743 "FFFFFFFF" "FFFFFFFF";
1745 static const char *default_moduli_rfc3526_MODP_group14 =
1746 /* name */
1747 "rfc3526-MODP-group14 "
1748 /* bits */
1749 "1760 "
1750 /* p */
1751 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1752 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1753 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1754 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1755 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1756 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1757 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1758 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1759 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1760 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1761 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1762 /* g */
1763 "02 "
1764 /* q */
1765 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1766 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1767 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1768 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1769 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1770 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1771 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1772 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1773 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1774 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1775 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1777 krb5_error_code
1778 _krb5_parse_moduli(krb5_context context, const char *file,
1779 struct krb5_dh_moduli ***moduli)
1781 /* name bits P G Q */
1782 krb5_error_code ret;
1783 struct krb5_dh_moduli **m = NULL, **m2;
1784 char buf[4096];
1785 FILE *f;
1786 int lineno = 0, n = 0;
1788 *moduli = NULL;
1790 m = calloc(1, sizeof(m[0]) * 3);
1791 if (m == NULL) {
1792 krb5_set_error_string(context, "malloc: out of memory");
1793 return ENOMEM;
1796 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1797 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
1798 if (ret) {
1799 _krb5_free_moduli(m);
1800 return ret;
1802 n++;
1804 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1805 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
1806 if (ret) {
1807 _krb5_free_moduli(m);
1808 return ret;
1810 n++;
1813 if (file == NULL)
1814 file = MODULI_FILE;
1816 f = fopen(file, "r");
1817 if (f == NULL) {
1818 *moduli = m;
1819 return 0;
1822 while(fgets(buf, sizeof(buf), f) != NULL) {
1823 struct krb5_dh_moduli *element;
1825 buf[strcspn(buf, "\n")] = '\0';
1826 lineno++;
1828 m2 = realloc(m, (n + 2) * sizeof(m[0]));
1829 if (m2 == NULL) {
1830 krb5_set_error_string(context, "malloc: out of memory");
1831 _krb5_free_moduli(m);
1832 return ENOMEM;
1834 m = m2;
1836 m[n] = NULL;
1838 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
1839 if (ret) {
1840 _krb5_free_moduli(m);
1841 return ret;
1843 if (element == NULL)
1844 continue;
1846 m[n] = element;
1847 m[n + 1] = NULL;
1848 n++;
1850 *moduli = m;
1851 return 0;
1854 krb5_error_code
1855 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
1856 heim_integer *p, heim_integer *g, heim_integer *q,
1857 struct krb5_dh_moduli **moduli,
1858 char **name)
1860 int i;
1862 if (name)
1863 *name = NULL;
1865 for (i = 0; moduli[i] != NULL; i++) {
1866 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1867 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1868 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1870 if (bits && bits > moduli[i]->bits) {
1871 krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1872 "no accepted, not enough bits generated",
1873 moduli[i]->name);
1874 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1876 if (name)
1877 *name = strdup(moduli[i]->name);
1878 return 0;
1881 krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1882 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1885 void KRB5_LIB_FUNCTION
1886 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1888 #ifdef PKINIT
1889 krb5_pk_init_ctx ctx;
1891 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1892 return;
1893 ctx = opt->opt_private->pk_init_ctx;
1894 if (ctx->dh)
1895 DH_free(ctx->dh);
1896 ctx->dh = NULL;
1897 if (ctx->id) {
1898 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1899 hx509_certs_free(&ctx->id->certs);
1900 hx509_certs_free(&ctx->id->anchors);
1901 hx509_certs_free(&ctx->id->certpool);
1902 hx509_context_free(&ctx->id->hx509ctx);
1904 if (ctx->clientDHNonce) {
1905 krb5_free_data(NULL, ctx->clientDHNonce);
1906 ctx->clientDHNonce = NULL;
1908 if (ctx->m)
1909 _krb5_free_moduli(ctx->m);
1910 free(ctx->id);
1911 ctx->id = NULL;
1913 free(opt->opt_private->pk_init_ctx);
1914 opt->opt_private->pk_init_ctx = NULL;
1915 #endif
1918 krb5_error_code KRB5_LIB_FUNCTION
1919 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
1920 krb5_get_init_creds_opt *opt,
1921 krb5_principal principal,
1922 const char *user_id,
1923 const char *x509_anchors,
1924 char * const * pool,
1925 char * const * pki_revoke,
1926 int flags,
1927 krb5_prompter_fct prompter,
1928 void *prompter_data,
1929 char *password)
1931 #ifdef PKINIT
1932 krb5_error_code ret;
1933 char *anchors = NULL;
1935 if (opt->opt_private == NULL) {
1936 krb5_set_error_string(context, "PKINIT: on non extendable opt");
1937 return EINVAL;
1940 opt->opt_private->pk_init_ctx =
1941 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1942 if (opt->opt_private->pk_init_ctx == NULL) {
1943 krb5_set_error_string(context, "malloc: out of memory");
1944 return ENOMEM;
1946 opt->opt_private->pk_init_ctx->dh = NULL;
1947 opt->opt_private->pk_init_ctx->id = NULL;
1948 opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1949 opt->opt_private->pk_init_ctx->require_binding = 0;
1950 opt->opt_private->pk_init_ctx->require_eku = 1;
1951 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1952 opt->opt_private->pk_init_ctx->peer = NULL;
1954 /* XXX implement krb5_appdefault_strings */
1955 if (pool == NULL)
1956 pool = krb5_config_get_strings(context, NULL,
1957 "appdefaults",
1958 "pkinit_pool",
1959 NULL);
1961 if (pki_revoke == NULL)
1962 pki_revoke = krb5_config_get_strings(context, NULL,
1963 "appdefaults",
1964 "pkinit_revoke",
1965 NULL);
1967 if (x509_anchors == NULL) {
1968 krb5_appdefault_string(context, "kinit",
1969 krb5_principal_get_realm(context, principal),
1970 "pkinit_anchors", NULL, &anchors);
1971 x509_anchors = anchors;
1974 ret = _krb5_pk_load_id(context,
1975 &opt->opt_private->pk_init_ctx->id,
1976 user_id,
1977 x509_anchors,
1978 pool,
1979 pki_revoke,
1980 prompter,
1981 prompter_data,
1982 password);
1983 if (ret) {
1984 free(opt->opt_private->pk_init_ctx);
1985 opt->opt_private->pk_init_ctx = NULL;
1986 return ret;
1989 if ((flags & 2) == 0) {
1990 const char *moduli_file;
1991 unsigned long dh_min_bits;
1993 moduli_file = krb5_config_get_string(context, NULL,
1994 "libdefaults",
1995 "moduli",
1996 NULL);
1998 dh_min_bits =
1999 krb5_config_get_int_default(context, NULL, 0,
2000 "libdefaults",
2001 "pkinit_dh_min_bits",
2002 NULL);
2004 ret = _krb5_parse_moduli(context, moduli_file,
2005 &opt->opt_private->pk_init_ctx->m);
2006 if (ret) {
2007 _krb5_get_init_creds_opt_free_pkinit(opt);
2008 return ret;
2011 opt->opt_private->pk_init_ctx->dh = DH_new();
2012 if (opt->opt_private->pk_init_ctx->dh == NULL) {
2013 krb5_set_error_string(context, "malloc: out of memory");
2014 _krb5_get_init_creds_opt_free_pkinit(opt);
2015 return ENOMEM;
2018 ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2019 dh_min_bits,
2020 opt->opt_private->pk_init_ctx->m);
2021 if (ret) {
2022 _krb5_get_init_creds_opt_free_pkinit(opt);
2023 return ret;
2026 if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2027 krb5_set_error_string(context, "pkinit: failed to generate DH key");
2028 _krb5_get_init_creds_opt_free_pkinit(opt);
2029 return ENOMEM;
2033 return 0;
2034 #else
2035 krb5_set_error_string(context, "no support for PKINIT compiled in");
2036 return EINVAL;
2037 #endif
2044 static void
2045 _krb5_pk_copy_error(krb5_context context,
2046 hx509_context hx509ctx,
2047 int hxret,
2048 const char *fmt,
2049 ...)
2051 va_list va;
2052 char *s, *f;
2054 va_start(va, fmt);
2055 vasprintf(&f, fmt, va);
2056 va_end(va);
2057 if (f == NULL) {
2058 krb5_clear_error_string(context);
2059 return;
2062 s = hx509_get_error_string(hx509ctx, hxret);
2063 if (s == NULL) {
2064 krb5_clear_error_string(context);
2065 free(f);
2066 return;
2068 krb5_set_error_string(context, "%s: %s", f, s);
2069 free(s);
2070 free(f);