Add roken/rename.c to fix non-standard rename()
[heimdal.git] / lib / krb5 / pkinit.c
blob92c1200f0645da99debf64d9d682bd6c09b99cda
1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 struct krb5_dh_moduli {
39 char *name;
40 unsigned long bits;
41 heim_integer p;
42 heim_integer g;
43 heim_integer q;
46 #ifdef PKINIT
48 #include <cms_asn1.h>
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
53 #include <asn1_err.h>
55 #include <der.h>
57 struct krb5_pk_cert {
58 hx509_cert cert;
61 struct krb5_pk_init_ctx_data {
62 struct krb5_pk_identity *id;
63 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
64 union {
65 DH *dh;
66 #ifdef HAVE_OPENSSL
67 EC_KEY *eckey;
68 #endif
69 } u;
70 krb5_data *clientDHNonce;
71 struct krb5_dh_moduli **m;
72 hx509_peer_info peer;
73 enum krb5_pk_type type;
74 unsigned int require_binding:1;
75 unsigned int require_eku:1;
76 unsigned int require_krbtgt_otherName:1;
77 unsigned int require_hostname_match:1;
78 unsigned int trustedCertifiers:1;
79 unsigned int anonymous:1;
82 static void
83 pk_copy_error(krb5_context context,
84 hx509_context hx509ctx,
85 int hxret,
86 const char *fmt,
87 ...)
88 __attribute__ ((format (printf, 4, 5)));
94 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
95 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
97 if (cert->cert) {
98 hx509_cert_free(cert->cert);
100 free(cert);
103 static krb5_error_code
104 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
106 integer->length = BN_num_bytes(bn);
107 integer->data = malloc(integer->length);
108 if (integer->data == NULL) {
109 krb5_clear_error_message(context);
110 return ENOMEM;
112 BN_bn2bin(bn, integer->data);
113 integer->negative = BN_is_negative(bn);
114 return 0;
117 static BIGNUM *
118 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
120 BIGNUM *bn;
122 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
123 if (bn == NULL) {
124 krb5_set_error_message(context, ENOMEM,
125 N_("PKINIT: parsing BN failed %s", ""), field);
126 return NULL;
128 BN_set_negative(bn, f->negative);
129 return bn;
132 static krb5_error_code
133 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
134 struct krb5_dh_moduli **moduli)
136 const struct krb5_dh_moduli *m;
138 if (bits == 0) {
139 m = moduli[1]; /* XXX */
140 if (m == NULL)
141 m = moduli[0]; /* XXX */
142 } else {
143 int i;
144 for (i = 0; moduli[i] != NULL; i++) {
145 if (bits < moduli[i]->bits)
146 break;
148 if (moduli[i] == NULL) {
149 krb5_set_error_message(context, EINVAL,
150 N_("Did not find a DH group parameter "
151 "matching requirement of %lu bits", ""),
152 bits);
153 return EINVAL;
155 m = moduli[i];
158 dh->p = integer_to_BN(context, "p", &m->p);
159 if (dh->p == NULL)
160 return ENOMEM;
161 dh->g = integer_to_BN(context, "g", &m->g);
162 if (dh->g == NULL)
163 return ENOMEM;
164 dh->q = integer_to_BN(context, "q", &m->q);
165 if (dh->q == NULL)
166 return ENOMEM;
168 return 0;
171 struct certfind {
172 const char *type;
173 const heim_oid *oid;
177 * Try searchin the key by to use by first looking for for PK-INIT
178 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
181 static krb5_error_code
182 find_cert(krb5_context context, struct krb5_pk_identity *id,
183 hx509_query *q, hx509_cert *cert)
185 struct certfind cf[4] = {
186 { "MobileMe EKU" },
187 { "PKINIT EKU" },
188 { "MS EKU" },
189 { "any (or no)" }
191 int i, ret, start = 1;
192 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
193 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
196 if (id->flags & PKINIT_BTMM)
197 start = 0;
199 cf[0].oid = &mobileMe;
200 cf[1].oid = &asn1_oid_id_pkekuoid;
201 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
202 cf[3].oid = NULL;
204 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
205 ret = hx509_query_match_eku(q, cf[i].oid);
206 if (ret) {
207 pk_copy_error(context, context->hx509ctx, ret,
208 "Failed setting %s OID", cf[i].type);
209 return ret;
212 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
213 if (ret == 0)
214 break;
215 pk_copy_error(context, context->hx509ctx, ret,
216 "Failed finding certificate with %s OID", cf[i].type);
218 return ret;
222 static krb5_error_code
223 create_signature(krb5_context context,
224 const heim_oid *eContentType,
225 krb5_data *eContent,
226 struct krb5_pk_identity *id,
227 hx509_peer_info peer,
228 krb5_data *sd_data)
230 int ret, flags = 0;
232 if (id->cert == NULL)
233 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
235 ret = hx509_cms_create_signed_1(context->hx509ctx,
236 flags,
237 eContentType,
238 eContent->data,
239 eContent->length,
240 NULL,
241 id->cert,
242 peer,
243 NULL,
244 id->certs,
245 sd_data);
246 if (ret) {
247 pk_copy_error(context, context->hx509ctx, ret,
248 "Create CMS signedData");
249 return ret;
252 return 0;
255 static int
256 cert2epi(hx509_context context, void *ctx, hx509_cert c)
258 ExternalPrincipalIdentifiers *ids = ctx;
259 ExternalPrincipalIdentifier id;
260 hx509_name subject = NULL;
261 void *p;
262 int ret;
264 if (ids->len > 10)
265 return 0;
267 memset(&id, 0, sizeof(id));
269 ret = hx509_cert_get_subject(c, &subject);
270 if (ret)
271 return ret;
273 if (hx509_name_is_null_p(subject) != 0) {
275 id.subjectName = calloc(1, sizeof(*id.subjectName));
276 if (id.subjectName == NULL) {
277 hx509_name_free(&subject);
278 free_ExternalPrincipalIdentifier(&id);
279 return ENOMEM;
282 ret = hx509_name_binary(subject, id.subjectName);
283 if (ret) {
284 hx509_name_free(&subject);
285 free_ExternalPrincipalIdentifier(&id);
286 return ret;
289 hx509_name_free(&subject);
292 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
293 if (id.issuerAndSerialNumber == NULL) {
294 free_ExternalPrincipalIdentifier(&id);
295 return ENOMEM;
299 IssuerAndSerialNumber iasn;
300 hx509_name issuer;
301 size_t size;
303 memset(&iasn, 0, sizeof(iasn));
305 ret = hx509_cert_get_issuer(c, &issuer);
306 if (ret) {
307 free_ExternalPrincipalIdentifier(&id);
308 return ret;
311 ret = hx509_name_to_Name(issuer, &iasn.issuer);
312 hx509_name_free(&issuer);
313 if (ret) {
314 free_ExternalPrincipalIdentifier(&id);
315 return ret;
318 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
319 if (ret) {
320 free_IssuerAndSerialNumber(&iasn);
321 free_ExternalPrincipalIdentifier(&id);
322 return ret;
325 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
326 id.issuerAndSerialNumber->data,
327 id.issuerAndSerialNumber->length,
328 &iasn, &size, ret);
329 free_IssuerAndSerialNumber(&iasn);
330 if (ret)
331 return ret;
332 if (id.issuerAndSerialNumber->length != size)
333 abort();
336 id.subjectKeyIdentifier = NULL;
338 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
339 if (p == NULL) {
340 free_ExternalPrincipalIdentifier(&id);
341 return ENOMEM;
344 ids->val = p;
345 ids->val[ids->len] = id;
346 ids->len++;
348 return 0;
351 static krb5_error_code
352 build_edi(krb5_context context,
353 hx509_context hx509ctx,
354 hx509_certs certs,
355 ExternalPrincipalIdentifiers *ids)
357 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
360 static krb5_error_code
361 build_auth_pack(krb5_context context,
362 unsigned nonce,
363 krb5_pk_init_ctx ctx,
364 const KDC_REQ_BODY *body,
365 AuthPack *a)
367 size_t buf_size, len;
368 krb5_error_code ret;
369 void *buf;
370 krb5_timestamp sec;
371 int32_t usec;
372 Checksum checksum;
374 krb5_clear_error_message(context);
376 memset(&checksum, 0, sizeof(checksum));
378 krb5_us_timeofday(context, &sec, &usec);
379 a->pkAuthenticator.ctime = sec;
380 a->pkAuthenticator.nonce = nonce;
382 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
383 if (ret)
384 return ret;
385 if (buf_size != len)
386 krb5_abortx(context, "internal error in ASN.1 encoder");
388 ret = krb5_create_checksum(context,
389 NULL,
391 CKSUMTYPE_SHA1,
392 buf,
393 len,
394 &checksum);
395 free(buf);
396 if (ret)
397 return ret;
399 ALLOC(a->pkAuthenticator.paChecksum, 1);
400 if (a->pkAuthenticator.paChecksum == NULL) {
401 krb5_set_error_message(context, ENOMEM,
402 N_("malloc: out of memory", ""));
403 return ENOMEM;
406 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
407 checksum.checksum.data, checksum.checksum.length);
408 free_Checksum(&checksum);
409 if (ret)
410 return ret;
412 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
413 const char *moduli_file;
414 unsigned long dh_min_bits;
415 krb5_data dhbuf;
416 size_t size;
418 krb5_data_zero(&dhbuf);
422 moduli_file = krb5_config_get_string(context, NULL,
423 "libdefaults",
424 "moduli",
425 NULL);
427 dh_min_bits =
428 krb5_config_get_int_default(context, NULL, 0,
429 "libdefaults",
430 "pkinit_dh_min_bits",
431 NULL);
433 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
434 if (ret)
435 return ret;
437 ctx->u.dh = DH_new();
438 if (ctx->u.dh == NULL) {
439 krb5_set_error_message(context, ENOMEM,
440 N_("malloc: out of memory", ""));
441 return ENOMEM;
444 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
445 if (ret)
446 return ret;
448 if (DH_generate_key(ctx->u.dh) != 1) {
449 krb5_set_error_message(context, ENOMEM,
450 N_("pkinit: failed to generate DH key", ""));
451 return ENOMEM;
455 if (1 /* support_cached_dh */) {
456 ALLOC(a->clientDHNonce, 1);
457 if (a->clientDHNonce == NULL) {
458 krb5_clear_error_message(context);
459 return ENOMEM;
461 ret = krb5_data_alloc(a->clientDHNonce, 40);
462 if (a->clientDHNonce == NULL) {
463 krb5_clear_error_message(context);
464 return ret;
466 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
467 ret = krb5_copy_data(context, a->clientDHNonce,
468 &ctx->clientDHNonce);
469 if (ret)
470 return ret;
473 ALLOC(a->clientPublicValue, 1);
474 if (a->clientPublicValue == NULL)
475 return ENOMEM;
477 if (ctx->keyex == USE_DH) {
478 DH *dh = ctx->u.dh;
479 DomainParameters dp;
480 heim_integer dh_pub_key;
482 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
483 &a->clientPublicValue->algorithm.algorithm);
484 if (ret)
485 return ret;
487 memset(&dp, 0, sizeof(dp));
489 ret = BN_to_integer(context, dh->p, &dp.p);
490 if (ret) {
491 free_DomainParameters(&dp);
492 return ret;
494 ret = BN_to_integer(context, dh->g, &dp.g);
495 if (ret) {
496 free_DomainParameters(&dp);
497 return ret;
499 ret = BN_to_integer(context, dh->q, &dp.q);
500 if (ret) {
501 free_DomainParameters(&dp);
502 return ret;
504 dp.j = NULL;
505 dp.validationParms = NULL;
507 a->clientPublicValue->algorithm.parameters =
508 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
509 if (a->clientPublicValue->algorithm.parameters == NULL) {
510 free_DomainParameters(&dp);
511 return ret;
514 ASN1_MALLOC_ENCODE(DomainParameters,
515 a->clientPublicValue->algorithm.parameters->data,
516 a->clientPublicValue->algorithm.parameters->length,
517 &dp, &size, ret);
518 free_DomainParameters(&dp);
519 if (ret)
520 return ret;
521 if (size != a->clientPublicValue->algorithm.parameters->length)
522 krb5_abortx(context, "Internal ASN1 encoder error");
524 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
525 if (ret)
526 return ret;
528 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
529 &dh_pub_key, &size, ret);
530 der_free_heim_integer(&dh_pub_key);
531 if (ret)
532 return ret;
533 if (size != dhbuf.length)
534 krb5_abortx(context, "asn1 internal error");
535 } else if (ctx->keyex == USE_ECDH) {
536 #ifdef HAVE_OPENSSL
537 ECParameters ecp;
538 unsigned char *p;
539 int len;
541 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
543 ecp.element = choice_ECParameters_namedCurve;
544 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
545 &ecp.u.namedCurve);
546 if (ret)
547 return ret;
549 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
550 if (a->clientPublicValue->algorithm.parameters == NULL) {
551 free_ECParameters(&ecp);
552 return ENOMEM;
554 ASN1_MALLOC_ENCODE(ECParameters, p, len, &ecp, &size, ret);
555 free_ECParameters(&ecp);
556 if (ret)
557 return ret;
558 if (size != len)
559 krb5_abortx(context, "asn1 internal error");
561 a->clientPublicValue->algorithm.parameters->data = p;
562 a->clientPublicValue->algorithm.parameters->length = size;
564 /* copy in public key */
566 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
567 &a->clientPublicValue->algorithm.algorithm);
568 if (ret)
569 return ret;
571 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
572 if (ctx->u.eckey == NULL)
573 return ENOMEM;
575 ret = EC_KEY_generate_key(ctx->u.eckey);
576 if (ret != 1)
577 return EINVAL;
579 /* encode onto dhkey */
581 len = i2o_ECPublicKey(ctx->u.eckey, NULL);
582 if (len <= 0)
583 abort();
585 dhbuf.data = malloc(len);
586 if (dhbuf.data == NULL)
587 abort();
588 dhbuf.length = len;
589 p = dhbuf.data;
591 len = i2o_ECPublicKey(ctx->u.eckey, &p);
592 if (len <= 0)
593 abort();
595 /* XXX verify that this is right with RFC3279 */
596 #else
597 return EINVAL;
598 #endif
599 } else
600 krb5_abortx(context, "internal error");
601 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
602 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
606 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
607 if (a->supportedCMSTypes == NULL)
608 return ENOMEM;
610 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, NULL,
611 &a->supportedCMSTypes->val,
612 &a->supportedCMSTypes->len);
613 if (ret)
614 return ret;
617 return ret;
620 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
621 _krb5_pk_mk_ContentInfo(krb5_context context,
622 const krb5_data *buf,
623 const heim_oid *oid,
624 struct ContentInfo *content_info)
626 krb5_error_code ret;
628 ret = der_copy_oid(oid, &content_info->contentType);
629 if (ret)
630 return ret;
631 ALLOC(content_info->content, 1);
632 if (content_info->content == NULL)
633 return ENOMEM;
634 content_info->content->data = malloc(buf->length);
635 if (content_info->content->data == NULL)
636 return ENOMEM;
637 memcpy(content_info->content->data, buf->data, buf->length);
638 content_info->content->length = buf->length;
639 return 0;
642 static krb5_error_code
643 pk_mk_padata(krb5_context context,
644 krb5_pk_init_ctx ctx,
645 const KDC_REQ_BODY *req_body,
646 unsigned nonce,
647 METHOD_DATA *md)
649 struct ContentInfo content_info;
650 krb5_error_code ret;
651 const heim_oid *oid;
652 size_t size;
653 krb5_data buf, sd_buf;
654 int pa_type;
656 krb5_data_zero(&buf);
657 krb5_data_zero(&sd_buf);
658 memset(&content_info, 0, sizeof(content_info));
660 if (ctx->type == PKINIT_WIN2K) {
661 AuthPack_Win2k ap;
662 krb5_timestamp sec;
663 int32_t usec;
665 memset(&ap, 0, sizeof(ap));
667 /* fill in PKAuthenticator */
668 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
669 if (ret) {
670 free_AuthPack_Win2k(&ap);
671 krb5_clear_error_message(context);
672 goto out;
674 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
675 if (ret) {
676 free_AuthPack_Win2k(&ap);
677 krb5_clear_error_message(context);
678 goto out;
681 krb5_us_timeofday(context, &sec, &usec);
682 ap.pkAuthenticator.ctime = sec;
683 ap.pkAuthenticator.cusec = usec;
684 ap.pkAuthenticator.nonce = nonce;
686 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
687 &ap, &size, ret);
688 free_AuthPack_Win2k(&ap);
689 if (ret) {
690 krb5_set_error_message(context, ret,
691 N_("Failed encoding AuthPackWin: %d", ""),
692 (int)ret);
693 goto out;
695 if (buf.length != size)
696 krb5_abortx(context, "internal ASN1 encoder error");
698 oid = &asn1_oid_id_pkcs7_data;
699 } else if (ctx->type == PKINIT_27) {
700 AuthPack ap;
702 memset(&ap, 0, sizeof(ap));
704 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
705 if (ret) {
706 free_AuthPack(&ap);
707 goto out;
710 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
711 free_AuthPack(&ap);
712 if (ret) {
713 krb5_set_error_message(context, ret,
714 N_("Failed encoding AuthPack: %d", ""),
715 (int)ret);
716 goto out;
718 if (buf.length != size)
719 krb5_abortx(context, "internal ASN1 encoder error");
721 oid = &asn1_oid_id_pkauthdata;
722 } else
723 krb5_abortx(context, "internal pkinit error");
725 ret = create_signature(context, oid, &buf, ctx->id,
726 ctx->peer, &sd_buf);
727 krb5_data_free(&buf);
728 if (ret)
729 goto out;
731 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
732 krb5_data_free(&sd_buf);
733 if (ret) {
734 krb5_set_error_message(context, ret,
735 N_("ContentInfo wrapping of signedData failed",""));
736 goto out;
739 if (ctx->type == PKINIT_WIN2K) {
740 PA_PK_AS_REQ_Win2k winreq;
742 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
744 memset(&winreq, 0, sizeof(winreq));
746 winreq.signed_auth_pack = buf;
748 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
749 &winreq, &size, ret);
750 free_PA_PK_AS_REQ_Win2k(&winreq);
752 } else if (ctx->type == PKINIT_27) {
753 PA_PK_AS_REQ req;
755 pa_type = KRB5_PADATA_PK_AS_REQ;
757 memset(&req, 0, sizeof(req));
758 req.signedAuthPack = buf;
760 if (ctx->trustedCertifiers) {
762 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
763 if (req.trustedCertifiers == NULL) {
764 ret = ENOMEM;
765 krb5_set_error_message(context, ret,
766 N_("malloc: out of memory", ""));
767 free_PA_PK_AS_REQ(&req);
768 goto out;
770 ret = build_edi(context, context->hx509ctx,
771 ctx->id->anchors, req.trustedCertifiers);
772 if (ret) {
773 krb5_set_error_message(context, ret,
774 N_("pk-init: failed to build "
775 "trustedCertifiers", ""));
776 free_PA_PK_AS_REQ(&req);
777 goto out;
780 req.kdcPkId = NULL;
782 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
783 &req, &size, ret);
785 free_PA_PK_AS_REQ(&req);
787 } else
788 krb5_abortx(context, "internal pkinit error");
789 if (ret) {
790 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
791 goto out;
793 if (buf.length != size)
794 krb5_abortx(context, "Internal ASN1 encoder error");
796 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
797 if (ret)
798 free(buf.data);
800 if (ret == 0)
801 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
803 out:
804 free_ContentInfo(&content_info);
806 return ret;
810 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
811 _krb5_pk_mk_padata(krb5_context context,
812 void *c,
813 int ic_flags,
814 int win2k,
815 const KDC_REQ_BODY *req_body,
816 unsigned nonce,
817 METHOD_DATA *md)
819 krb5_pk_init_ctx ctx = c;
820 int win2k_compat;
822 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
823 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
824 N_("PKINIT: No user certificate given", ""));
825 return HEIM_PKINIT_NO_PRIVATE_KEY;
828 win2k_compat = krb5_config_get_bool_default(context, NULL,
829 win2k,
830 "realms",
831 req_body->realm,
832 "pkinit_win2k",
833 NULL);
835 if (win2k_compat) {
836 ctx->require_binding =
837 krb5_config_get_bool_default(context, NULL,
838 TRUE,
839 "realms",
840 req_body->realm,
841 "pkinit_win2k_require_binding",
842 NULL);
843 ctx->type = PKINIT_WIN2K;
844 } else
845 ctx->type = PKINIT_27;
847 ctx->require_eku =
848 krb5_config_get_bool_default(context, NULL,
849 TRUE,
850 "realms",
851 req_body->realm,
852 "pkinit_require_eku",
853 NULL);
854 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
855 ctx->require_eku = 0;
856 if (ctx->id->flags & PKINIT_BTMM)
857 ctx->require_eku = 0;
859 ctx->require_krbtgt_otherName =
860 krb5_config_get_bool_default(context, NULL,
861 TRUE,
862 "realms",
863 req_body->realm,
864 "pkinit_require_krbtgt_otherName",
865 NULL);
867 ctx->require_hostname_match =
868 krb5_config_get_bool_default(context, NULL,
869 FALSE,
870 "realms",
871 req_body->realm,
872 "pkinit_require_hostname_match",
873 NULL);
875 ctx->trustedCertifiers =
876 krb5_config_get_bool_default(context, NULL,
877 TRUE,
878 "realms",
879 req_body->realm,
880 "pkinit_trustedCertifiers",
881 NULL);
883 return pk_mk_padata(context, ctx, req_body, nonce, md);
886 static krb5_error_code
887 pk_verify_sign(krb5_context context,
888 const void *data,
889 size_t length,
890 struct krb5_pk_identity *id,
891 heim_oid *contentType,
892 krb5_data *content,
893 struct krb5_pk_cert **signer)
895 hx509_certs signer_certs;
896 int ret, flags = 0;
898 /* BTMM is broken in Leo and SnowLeo */
899 if (id->flags & PKINIT_BTMM) {
900 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
901 flags |= HX509_CMS_VS_NO_KU_CHECK;
902 flags |= HX509_CMS_VS_NO_VALIDATE;
905 *signer = NULL;
907 ret = hx509_cms_verify_signed(context->hx509ctx,
908 id->verify_ctx,
909 flags,
910 data,
911 length,
912 NULL,
913 id->certpool,
914 contentType,
915 content,
916 &signer_certs);
917 if (ret) {
918 pk_copy_error(context, context->hx509ctx, ret,
919 "CMS verify signed failed");
920 return ret;
923 *signer = calloc(1, sizeof(**signer));
924 if (*signer == NULL) {
925 krb5_clear_error_message(context);
926 ret = ENOMEM;
927 goto out;
930 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
931 if (ret) {
932 pk_copy_error(context, context->hx509ctx, ret,
933 "Failed to get on of the signer certs");
934 goto out;
937 out:
938 hx509_certs_free(&signer_certs);
939 if (ret) {
940 if (*signer) {
941 hx509_cert_free((*signer)->cert);
942 free(*signer);
943 *signer = NULL;
947 return ret;
950 static krb5_error_code
951 get_reply_key_win(krb5_context context,
952 const krb5_data *content,
953 unsigned nonce,
954 krb5_keyblock **key)
956 ReplyKeyPack_Win2k key_pack;
957 krb5_error_code ret;
958 size_t size;
960 ret = decode_ReplyKeyPack_Win2k(content->data,
961 content->length,
962 &key_pack,
963 &size);
964 if (ret) {
965 krb5_set_error_message(context, ret,
966 N_("PKINIT decoding reply key failed", ""));
967 free_ReplyKeyPack_Win2k(&key_pack);
968 return ret;
971 if (key_pack.nonce != nonce) {
972 krb5_set_error_message(context, ret,
973 N_("PKINIT enckey nonce is wrong", ""));
974 free_ReplyKeyPack_Win2k(&key_pack);
975 return KRB5KRB_AP_ERR_MODIFIED;
978 *key = malloc (sizeof (**key));
979 if (*key == NULL) {
980 free_ReplyKeyPack_Win2k(&key_pack);
981 krb5_set_error_message(context, ENOMEM,
982 N_("malloc: out of memory", ""));
983 return ENOMEM;
986 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
987 free_ReplyKeyPack_Win2k(&key_pack);
988 if (ret) {
989 krb5_set_error_message(context, ret,
990 N_("PKINIT failed copying reply key", ""));
991 free(*key);
992 *key = NULL;
995 return ret;
998 static krb5_error_code
999 get_reply_key(krb5_context context,
1000 const krb5_data *content,
1001 const krb5_data *req_buffer,
1002 krb5_keyblock **key)
1004 ReplyKeyPack key_pack;
1005 krb5_error_code ret;
1006 size_t size;
1008 ret = decode_ReplyKeyPack(content->data,
1009 content->length,
1010 &key_pack,
1011 &size);
1012 if (ret) {
1013 krb5_set_error_message(context, ret,
1014 N_("PKINIT decoding reply key failed", ""));
1015 free_ReplyKeyPack(&key_pack);
1016 return ret;
1020 krb5_crypto crypto;
1023 * XXX Verify kp.replyKey is a allowed enctype in the
1024 * configuration file
1027 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1028 if (ret) {
1029 free_ReplyKeyPack(&key_pack);
1030 return ret;
1033 ret = krb5_verify_checksum(context, crypto, 6,
1034 req_buffer->data, req_buffer->length,
1035 &key_pack.asChecksum);
1036 krb5_crypto_destroy(context, crypto);
1037 if (ret) {
1038 free_ReplyKeyPack(&key_pack);
1039 return ret;
1043 *key = malloc (sizeof (**key));
1044 if (*key == NULL) {
1045 free_ReplyKeyPack(&key_pack);
1046 krb5_set_error_message(context, ENOMEM,
1047 N_("malloc: out of memory", ""));
1048 return ENOMEM;
1051 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1052 free_ReplyKeyPack(&key_pack);
1053 if (ret) {
1054 krb5_set_error_message(context, ret,
1055 N_("PKINIT failed copying reply key", ""));
1056 free(*key);
1057 *key = NULL;
1060 return ret;
1064 static krb5_error_code
1065 pk_verify_host(krb5_context context,
1066 const char *realm,
1067 const krb5_krbhst_info *hi,
1068 struct krb5_pk_init_ctx_data *ctx,
1069 struct krb5_pk_cert *host)
1071 krb5_error_code ret = 0;
1073 if (ctx->require_eku) {
1074 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1075 &asn1_oid_id_pkkdcekuoid, 0);
1076 if (ret) {
1077 krb5_set_error_message(context, ret,
1078 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1079 return ret;
1082 if (ctx->require_krbtgt_otherName) {
1083 hx509_octet_string_list list;
1084 int i;
1086 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1087 host->cert,
1088 &asn1_oid_id_pkinit_san,
1089 &list);
1090 if (ret) {
1091 krb5_set_error_message(context, ret,
1092 N_("Failed to find the PK-INIT "
1093 "subjectAltName in the KDC "
1094 "certificate", ""));
1096 return ret;
1099 for (i = 0; i < list.len; i++) {
1100 KRB5PrincipalName r;
1102 ret = decode_KRB5PrincipalName(list.val[i].data,
1103 list.val[i].length,
1105 NULL);
1106 if (ret) {
1107 krb5_set_error_message(context, ret,
1108 N_("Failed to decode the PK-INIT "
1109 "subjectAltName in the "
1110 "KDC certificate", ""));
1112 break;
1115 if (r.principalName.name_string.len != 2 ||
1116 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1117 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1118 strcmp(r.realm, realm) != 0)
1120 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1121 krb5_set_error_message(context, ret,
1122 N_("KDC have wrong realm name in "
1123 "the certificate", ""));
1126 free_KRB5PrincipalName(&r);
1127 if (ret)
1128 break;
1130 hx509_free_octet_string_list(&list);
1132 if (ret)
1133 return ret;
1135 if (hi) {
1136 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1137 ctx->require_hostname_match,
1138 HX509_HN_HOSTNAME,
1139 hi->hostname,
1140 hi->ai->ai_addr, hi->ai->ai_addrlen);
1142 if (ret)
1143 krb5_set_error_message(context, ret,
1144 N_("Address mismatch in "
1145 "the KDC certificate", ""));
1147 return ret;
1150 static krb5_error_code
1151 pk_rd_pa_reply_enckey(krb5_context context,
1152 int type,
1153 const heim_octet_string *indata,
1154 const heim_oid *dataType,
1155 const char *realm,
1156 krb5_pk_init_ctx ctx,
1157 krb5_enctype etype,
1158 const krb5_krbhst_info *hi,
1159 unsigned nonce,
1160 const krb5_data *req_buffer,
1161 PA_DATA *pa,
1162 krb5_keyblock **key)
1164 krb5_error_code ret;
1165 struct krb5_pk_cert *host = NULL;
1166 krb5_data content;
1167 heim_oid contentType = { 0, NULL };
1168 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1170 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1171 krb5_set_error_message(context, EINVAL,
1172 N_("PKINIT: Invalid content type", ""));
1173 return EINVAL;
1176 if (ctx->type == PKINIT_WIN2K)
1177 flags |= HX509_CMS_UE_ALLOW_WEAK;
1179 ret = hx509_cms_unenvelope(context->hx509ctx,
1180 ctx->id->certs,
1181 flags,
1182 indata->data,
1183 indata->length,
1184 NULL,
1186 &contentType,
1187 &content);
1188 if (ret) {
1189 pk_copy_error(context, context->hx509ctx, ret,
1190 "Failed to unenvelope CMS data in PK-INIT reply");
1191 return ret;
1193 der_free_oid(&contentType);
1195 /* win2k uses ContentInfo */
1196 if (type == PKINIT_WIN2K) {
1197 heim_oid type2;
1198 heim_octet_string out;
1200 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1201 if (ret) {
1202 /* windows LH with interesting CMS packets */
1203 size_t ph = 1 + der_length_len(content.length);
1204 unsigned char *ptr = malloc(content.length + ph);
1205 size_t l;
1207 memcpy(ptr + ph, content.data, content.length);
1209 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1210 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1211 if (ret)
1212 return ret;
1213 free(content.data);
1214 content.data = ptr;
1215 content.length += ph;
1217 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1218 if (ret)
1219 goto out;
1221 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1222 ret = EINVAL; /* XXX */
1223 krb5_set_error_message(context, ret,
1224 N_("PKINIT: Invalid content type", ""));
1225 der_free_oid(&type2);
1226 der_free_octet_string(&out);
1227 goto out;
1229 der_free_oid(&type2);
1230 krb5_data_free(&content);
1231 ret = krb5_data_copy(&content, out.data, out.length);
1232 der_free_octet_string(&out);
1233 if (ret) {
1234 krb5_set_error_message(context, ret,
1235 N_("malloc: out of memory", ""));
1236 goto out;
1240 ret = pk_verify_sign(context,
1241 content.data,
1242 content.length,
1243 ctx->id,
1244 &contentType,
1245 &content,
1246 &host);
1247 if (ret)
1248 goto out;
1250 /* make sure that it is the kdc's certificate */
1251 ret = pk_verify_host(context, realm, hi, ctx, host);
1252 if (ret) {
1253 goto out;
1256 #if 0
1257 if (type == PKINIT_WIN2K) {
1258 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1259 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1260 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1261 goto out;
1263 } else {
1264 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1265 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1266 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1267 goto out;
1270 #endif
1272 switch(type) {
1273 case PKINIT_WIN2K:
1274 ret = get_reply_key(context, &content, req_buffer, key);
1275 if (ret != 0 && ctx->require_binding == 0)
1276 ret = get_reply_key_win(context, &content, nonce, key);
1277 break;
1278 case PKINIT_27:
1279 ret = get_reply_key(context, &content, req_buffer, key);
1280 break;
1282 if (ret)
1283 goto out;
1285 /* XXX compare given etype with key->etype */
1287 out:
1288 if (host)
1289 _krb5_pk_cert_free(host);
1290 der_free_oid(&contentType);
1291 krb5_data_free(&content);
1293 return ret;
1296 static krb5_error_code
1297 pk_rd_pa_reply_dh(krb5_context context,
1298 const heim_octet_string *indata,
1299 const heim_oid *dataType,
1300 const char *realm,
1301 krb5_pk_init_ctx ctx,
1302 krb5_enctype etype,
1303 const krb5_krbhst_info *hi,
1304 const DHNonce *c_n,
1305 const DHNonce *k_n,
1306 unsigned nonce,
1307 PA_DATA *pa,
1308 krb5_keyblock **key)
1310 const unsigned char *p;
1311 unsigned char *dh_gen_key = NULL;
1312 struct krb5_pk_cert *host = NULL;
1313 BIGNUM *kdc_dh_pubkey = NULL;
1314 KDCDHKeyInfo kdc_dh_info;
1315 heim_oid contentType = { 0, NULL };
1316 krb5_data content;
1317 krb5_error_code ret;
1318 int dh_gen_keylen = 0;
1319 size_t size;
1321 krb5_data_zero(&content);
1322 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1324 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1325 krb5_set_error_message(context, EINVAL,
1326 N_("PKINIT: Invalid content type", ""));
1327 return EINVAL;
1330 ret = pk_verify_sign(context,
1331 indata->data,
1332 indata->length,
1333 ctx->id,
1334 &contentType,
1335 &content,
1336 &host);
1337 if (ret)
1338 goto out;
1340 /* make sure that it is the kdc's certificate */
1341 ret = pk_verify_host(context, realm, hi, ctx, host);
1342 if (ret)
1343 goto out;
1345 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1346 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1347 krb5_set_error_message(context, ret,
1348 N_("pkinit - dh reply contains wrong oid", ""));
1349 goto out;
1352 ret = decode_KDCDHKeyInfo(content.data,
1353 content.length,
1354 &kdc_dh_info,
1355 &size);
1357 if (ret) {
1358 krb5_set_error_message(context, ret,
1359 N_("pkinit - failed to decode "
1360 "KDC DH Key Info", ""));
1361 goto out;
1364 if (kdc_dh_info.nonce != nonce) {
1365 ret = KRB5KRB_AP_ERR_MODIFIED;
1366 krb5_set_error_message(context, ret,
1367 N_("PKINIT: DH nonce is wrong", ""));
1368 goto out;
1371 if (kdc_dh_info.dhKeyExpiration) {
1372 if (k_n == NULL) {
1373 ret = KRB5KRB_ERR_GENERIC;
1374 krb5_set_error_message(context, ret,
1375 N_("pkinit; got key expiration "
1376 "without server nonce", ""));
1377 goto out;
1379 if (c_n == NULL) {
1380 ret = KRB5KRB_ERR_GENERIC;
1381 krb5_set_error_message(context, ret,
1382 N_("pkinit; got DH reuse but no "
1383 "client nonce", ""));
1384 goto out;
1386 } else {
1387 if (k_n) {
1388 ret = KRB5KRB_ERR_GENERIC;
1389 krb5_set_error_message(context, ret,
1390 N_("pkinit: got server nonce "
1391 "without key expiration", ""));
1392 goto out;
1394 c_n = NULL;
1398 p = kdc_dh_info.subjectPublicKey.data;
1399 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1401 if (ctx->keyex == USE_DH) {
1402 DHPublicKey k;
1403 ret = decode_DHPublicKey(p, size, &k, NULL);
1404 if (ret) {
1405 krb5_set_error_message(context, ret,
1406 N_("pkinit: can't decode "
1407 "without key expiration", ""));
1408 goto out;
1411 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1412 free_DHPublicKey(&k);
1413 if (kdc_dh_pubkey == NULL) {
1414 ret = ENOMEM;
1415 goto out;
1419 size = DH_size(ctx->u.dh);
1421 dh_gen_key = malloc(size);
1422 if (dh_gen_key == NULL) {
1423 ret = ENOMEM;
1424 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1425 goto out;
1428 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1429 if (dh_gen_keylen == -1) {
1430 ret = KRB5KRB_ERR_GENERIC;
1431 dh_gen_keylen = 0;
1432 krb5_set_error_message(context, ret,
1433 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1434 goto out;
1436 if (dh_gen_keylen < size) {
1437 size -= dh_gen_keylen;
1438 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1439 memset(dh_gen_key, 0, size);
1442 } else {
1443 #ifdef HAVE_OPENSSL
1444 const EC_GROUP *group;
1445 EC_KEY *public = NULL;
1447 group = EC_KEY_get0_group(ctx->u.eckey);
1449 public = EC_KEY_new();
1450 if (public == NULL) {
1451 ret = ENOMEM;
1452 goto out;
1454 if (EC_KEY_set_group(public, group) != 1) {
1455 EC_KEY_free(public);
1456 ret = ENOMEM;
1457 goto out;
1460 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1461 EC_KEY_free(public);
1462 ret = KRB5KRB_ERR_GENERIC;
1463 krb5_set_error_message(context, ret,
1464 N_("PKINIT: Can't parse ECDH public key", ""));
1465 goto out;
1468 size = (EC_GROUP_get_degree(group) + 7) / 8;
1469 dh_gen_key = malloc(size);
1470 if (dh_gen_key == NULL) {
1471 EC_KEY_free(public);
1472 ret = ENOMEM;
1473 krb5_set_error_message(context, ret,
1474 N_("malloc: out of memory", ""));
1475 goto out;
1477 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1478 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1479 EC_KEY_free(public);
1480 if (dh_gen_keylen == -1) {
1481 ret = KRB5KRB_ERR_GENERIC;
1482 dh_gen_keylen = 0;
1483 krb5_set_error_message(context, ret,
1484 N_("PKINIT: Can't compute ECDH public key", ""));
1485 goto out;
1487 #else
1488 ret = EINVAL;
1489 #endif
1492 if (dh_gen_keylen <= 0) {
1493 ret = EINVAL;
1494 krb5_set_error_message(context, ret,
1495 N_("PKINIT: resulting DH key <= 0", ""));
1496 dh_gen_keylen = 0;
1497 goto out;
1500 *key = malloc (sizeof (**key));
1501 if (*key == NULL) {
1502 ret = ENOMEM;
1503 krb5_set_error_message(context, ret,
1504 N_("malloc: out of memory", ""));
1505 goto out;
1508 ret = _krb5_pk_octetstring2key(context,
1509 etype,
1510 dh_gen_key, dh_gen_keylen,
1511 c_n, k_n,
1512 *key);
1513 if (ret) {
1514 krb5_set_error_message(context, ret,
1515 N_("PKINIT: can't create key from DH key", ""));
1516 free(*key);
1517 *key = NULL;
1518 goto out;
1521 out:
1522 if (kdc_dh_pubkey)
1523 BN_free(kdc_dh_pubkey);
1524 if (dh_gen_key) {
1525 memset(dh_gen_key, 0, dh_gen_keylen);
1526 free(dh_gen_key);
1528 if (host)
1529 _krb5_pk_cert_free(host);
1530 if (content.data)
1531 krb5_data_free(&content);
1532 der_free_oid(&contentType);
1533 free_KDCDHKeyInfo(&kdc_dh_info);
1535 return ret;
1538 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1539 _krb5_pk_rd_pa_reply(krb5_context context,
1540 const char *realm,
1541 void *c,
1542 krb5_enctype etype,
1543 const krb5_krbhst_info *hi,
1544 unsigned nonce,
1545 const krb5_data *req_buffer,
1546 PA_DATA *pa,
1547 krb5_keyblock **key)
1549 krb5_pk_init_ctx ctx = c;
1550 krb5_error_code ret;
1551 size_t size;
1553 /* Check for IETF PK-INIT first */
1554 if (ctx->type == PKINIT_27) {
1555 PA_PK_AS_REP rep;
1556 heim_octet_string os, data;
1557 heim_oid oid;
1559 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1560 krb5_set_error_message(context, EINVAL,
1561 N_("PKINIT: wrong padata recv", ""));
1562 return EINVAL;
1565 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1566 pa->padata_value.length,
1567 &rep,
1568 &size);
1569 if (ret) {
1570 krb5_set_error_message(context, ret,
1571 N_("Failed to decode pkinit AS rep", ""));
1572 return ret;
1575 switch (rep.element) {
1576 case choice_PA_PK_AS_REP_dhInfo:
1577 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1578 os = rep.u.dhInfo.dhSignedData;
1579 break;
1580 case choice_PA_PK_AS_REP_encKeyPack:
1581 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1582 os = rep.u.encKeyPack;
1583 break;
1584 default: {
1585 PA_PK_AS_REP_BTMM btmm;
1586 free_PA_PK_AS_REP(&rep);
1587 memset(&rep, 0, sizeof(rep));
1589 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1591 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1592 pa->padata_value.length,
1593 &btmm,
1594 &size);
1595 if (ret) {
1596 krb5_set_error_message(context, EINVAL,
1597 N_("PKINIT: -27 reply "
1598 "invalid content type", ""));
1599 return EINVAL;
1602 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1603 free_PA_PK_AS_REP_BTMM(&btmm);
1604 ret = EINVAL;
1605 krb5_set_error_message(context, ret,
1606 N_("DH mode not supported for BTMM mode", ""));
1607 return ret;
1611 * Transform to IETF style PK-INIT reply so that free works below
1614 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1615 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1616 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1617 btmm.encKeyPack->data = NULL;
1618 btmm.encKeyPack->length = 0;
1619 free_PA_PK_AS_REP_BTMM(&btmm);
1620 os = rep.u.encKeyPack;
1624 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1625 if (ret) {
1626 free_PA_PK_AS_REP(&rep);
1627 krb5_set_error_message(context, ret,
1628 N_("PKINIT: failed to unwrap CI", ""));
1629 return ret;
1632 switch (rep.element) {
1633 case choice_PA_PK_AS_REP_dhInfo:
1634 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1635 ctx->clientDHNonce,
1636 rep.u.dhInfo.serverDHNonce,
1637 nonce, pa, key);
1638 break;
1639 case choice_PA_PK_AS_REP_encKeyPack:
1640 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1641 ctx, etype, hi, nonce, req_buffer, pa, key);
1642 break;
1643 default:
1644 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1646 der_free_octet_string(&data);
1647 der_free_oid(&oid);
1648 free_PA_PK_AS_REP(&rep);
1650 } else if (ctx->type == PKINIT_WIN2K) {
1651 PA_PK_AS_REP_Win2k w2krep;
1653 /* Check for Windows encoding of the AS-REP pa data */
1655 #if 0 /* should this be ? */
1656 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1657 krb5_set_error_message(context, EINVAL,
1658 "PKINIT: wrong padata recv");
1659 return EINVAL;
1661 #endif
1663 memset(&w2krep, 0, sizeof(w2krep));
1665 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1666 pa->padata_value.length,
1667 &w2krep,
1668 &size);
1669 if (ret) {
1670 krb5_set_error_message(context, ret,
1671 N_("PKINIT: Failed decoding windows "
1672 "pkinit reply %d", ""), (int)ret);
1673 return ret;
1676 krb5_clear_error_message(context);
1678 switch (w2krep.element) {
1679 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1680 heim_octet_string data;
1681 heim_oid oid;
1683 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1684 &oid, &data, NULL);
1685 free_PA_PK_AS_REP_Win2k(&w2krep);
1686 if (ret) {
1687 krb5_set_error_message(context, ret,
1688 N_("PKINIT: failed to unwrap CI", ""));
1689 return ret;
1692 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1693 ctx, etype, hi, nonce, req_buffer, pa, key);
1694 der_free_octet_string(&data);
1695 der_free_oid(&oid);
1697 break;
1699 default:
1700 free_PA_PK_AS_REP_Win2k(&w2krep);
1701 ret = EINVAL;
1702 krb5_set_error_message(context, ret,
1703 N_("PKINIT: win2k reply invalid "
1704 "content type", ""));
1705 break;
1708 } else {
1709 ret = EINVAL;
1710 krb5_set_error_message(context, ret,
1711 N_("PKINIT: unknown reply type", ""));
1714 return ret;
1717 struct prompter {
1718 krb5_context context;
1719 krb5_prompter_fct prompter;
1720 void *prompter_data;
1723 static int
1724 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1726 krb5_error_code ret;
1727 krb5_prompt prompt;
1728 krb5_data password_data;
1729 struct prompter *p = data;
1731 password_data.data = prompter->reply.data;
1732 password_data.length = prompter->reply.length;
1734 prompt.prompt = prompter->prompt;
1735 prompt.hidden = hx509_prompt_hidden(prompter->type);
1736 prompt.reply = &password_data;
1738 switch (prompter->type) {
1739 case HX509_PROMPT_TYPE_INFO:
1740 prompt.type = KRB5_PROMPT_TYPE_INFO;
1741 break;
1742 case HX509_PROMPT_TYPE_PASSWORD:
1743 case HX509_PROMPT_TYPE_QUESTION:
1744 default:
1745 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1746 break;
1749 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1750 if (ret) {
1751 memset (prompter->reply.data, 0, prompter->reply.length);
1752 return 1;
1754 return 0;
1757 static krb5_error_code
1758 _krb5_pk_set_user_id(krb5_context context,
1759 krb5_principal principal,
1760 krb5_pk_init_ctx ctx,
1761 struct hx509_certs_data *certs)
1763 hx509_certs c = hx509_certs_ref(certs);
1764 hx509_query *q = NULL;
1765 int ret;
1767 if (ctx->id->certs)
1768 hx509_certs_free(&ctx->id->certs);
1769 if (ctx->id->cert) {
1770 hx509_cert_free(ctx->id->cert);
1771 ctx->id->cert = NULL;
1774 ctx->id->certs = c;
1775 ctx->anonymous = 0;
1777 ret = hx509_query_alloc(context->hx509ctx, &q);
1778 if (ret) {
1779 pk_copy_error(context, context->hx509ctx, ret,
1780 "Allocate query to find signing certificate");
1781 return ret;
1784 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1785 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1787 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1788 ctx->id->flags |= PKINIT_BTMM;
1791 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1792 hx509_query_free(context->hx509ctx, q);
1794 if (ret == 0 && _krb5_have_debug(context, 2)) {
1795 hx509_name name;
1796 char *str, *sn;
1797 heim_integer i;
1799 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1800 if (ret)
1801 goto out;
1803 ret = hx509_name_to_string(name, &str);
1804 hx509_name_free(&name);
1805 if (ret)
1806 goto out;
1808 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1809 if (ret) {
1810 free(str);
1811 goto out;
1814 ret = der_print_hex_heim_integer(&i, &sn);
1815 der_free_heim_integer(&i);
1816 if (ret) {
1817 free(name);
1818 goto out;
1821 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1822 free(str);
1823 free(sn);
1825 out:
1827 return ret;
1830 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1831 _krb5_pk_load_id(krb5_context context,
1832 struct krb5_pk_identity **ret_id,
1833 const char *user_id,
1834 const char *anchor_id,
1835 char * const *chain_list,
1836 char * const *revoke_list,
1837 krb5_prompter_fct prompter,
1838 void *prompter_data,
1839 char *password)
1841 struct krb5_pk_identity *id = NULL;
1842 struct prompter p;
1843 int ret;
1845 *ret_id = NULL;
1847 if (anchor_id == NULL) {
1848 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1849 N_("PKINIT: No anchor given", ""));
1850 return HEIM_PKINIT_NO_VALID_CA;
1853 /* load cert */
1855 id = calloc(1, sizeof(*id));
1856 if (id == NULL) {
1857 krb5_set_error_message(context, ENOMEM,
1858 N_("malloc: out of memory", ""));
1859 return ENOMEM;
1862 if (user_id) {
1863 hx509_lock lock;
1865 ret = hx509_lock_init(context->hx509ctx, &lock);
1866 if (ret) {
1867 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1868 goto out;
1871 if (password && password[0])
1872 hx509_lock_add_password(lock, password);
1874 if (prompter) {
1875 p.context = context;
1876 p.prompter = prompter;
1877 p.prompter_data = prompter_data;
1879 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1880 if (ret) {
1881 hx509_lock_free(lock);
1882 goto out;
1886 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1887 hx509_lock_free(lock);
1888 if (ret) {
1889 pk_copy_error(context, context->hx509ctx, ret,
1890 "Failed to init cert certs");
1891 goto out;
1893 } else {
1894 id->certs = NULL;
1897 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1898 if (ret) {
1899 pk_copy_error(context, context->hx509ctx, ret,
1900 "Failed to init anchors");
1901 goto out;
1904 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1905 0, NULL, &id->certpool);
1906 if (ret) {
1907 pk_copy_error(context, context->hx509ctx, ret,
1908 "Failed to init chain");
1909 goto out;
1912 while (chain_list && *chain_list) {
1913 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1914 NULL, *chain_list);
1915 if (ret) {
1916 pk_copy_error(context, context->hx509ctx, ret,
1917 "Failed to laod chain %s",
1918 *chain_list);
1919 goto out;
1921 chain_list++;
1924 if (revoke_list) {
1925 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1926 if (ret) {
1927 pk_copy_error(context, context->hx509ctx, ret,
1928 "Failed init revoke list");
1929 goto out;
1932 while (*revoke_list) {
1933 ret = hx509_revoke_add_crl(context->hx509ctx,
1934 id->revokectx,
1935 *revoke_list);
1936 if (ret) {
1937 pk_copy_error(context, context->hx509ctx, ret,
1938 "Failed load revoke list");
1939 goto out;
1941 revoke_list++;
1943 } else
1944 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1946 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1947 if (ret) {
1948 pk_copy_error(context, context->hx509ctx, ret,
1949 "Failed init verify context");
1950 goto out;
1953 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1954 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1956 out:
1957 if (ret) {
1958 hx509_verify_destroy_ctx(id->verify_ctx);
1959 hx509_certs_free(&id->certs);
1960 hx509_certs_free(&id->anchors);
1961 hx509_certs_free(&id->certpool);
1962 hx509_revoke_free(&id->revokectx);
1963 free(id);
1964 } else
1965 *ret_id = id;
1967 return ret;
1974 static void
1975 pk_copy_error(krb5_context context,
1976 hx509_context hx509ctx,
1977 int hxret,
1978 const char *fmt,
1979 ...)
1981 va_list va;
1982 char *s, *f;
1983 int ret;
1985 va_start(va, fmt);
1986 ret = vasprintf(&f, fmt, va);
1987 va_end(va);
1988 if (ret == -1 || f == NULL) {
1989 krb5_clear_error_message(context);
1990 return;
1993 s = hx509_get_error_string(hx509ctx, hxret);
1994 if (s == NULL) {
1995 krb5_clear_error_message(context);
1996 free(f);
1997 return;
1999 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2000 free(s);
2001 free(f);
2004 static int
2005 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2006 const char *name, heim_integer *integer)
2008 int ret;
2009 char *p1;
2010 p1 = strsep(p, " \t");
2011 if (p1 == NULL) {
2012 krb5_set_error_message(context, EINVAL,
2013 N_("moduli file %s missing %s on line %d", ""),
2014 file, name, lineno);
2015 return EINVAL;
2017 ret = der_parse_hex_heim_integer(p1, integer);
2018 if (ret) {
2019 krb5_set_error_message(context, ret,
2020 N_("moduli file %s failed parsing %s "
2021 "on line %d", ""),
2022 file, name, lineno);
2023 return ret;
2026 return 0;
2029 krb5_error_code
2030 _krb5_parse_moduli_line(krb5_context context,
2031 const char *file,
2032 int lineno,
2033 char *p,
2034 struct krb5_dh_moduli **m)
2036 struct krb5_dh_moduli *m1;
2037 char *p1;
2038 int ret;
2040 *m = NULL;
2042 m1 = calloc(1, sizeof(*m1));
2043 if (m1 == NULL) {
2044 krb5_set_error_message(context, ENOMEM,
2045 N_("malloc: out of memory", ""));
2046 return ENOMEM;
2049 while (isspace((unsigned char)*p))
2050 p++;
2051 if (*p == '#') {
2052 free(m1);
2053 return 0;
2055 ret = EINVAL;
2057 p1 = strsep(&p, " \t");
2058 if (p1 == NULL) {
2059 krb5_set_error_message(context, ret,
2060 N_("moduli file %s missing name on line %d", ""),
2061 file, lineno);
2062 goto out;
2064 m1->name = strdup(p1);
2065 if (m1->name == NULL) {
2066 ret = ENOMEM;
2067 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2068 goto out;
2071 p1 = strsep(&p, " \t");
2072 if (p1 == NULL) {
2073 krb5_set_error_message(context, ret,
2074 N_("moduli file %s missing bits on line %d", ""),
2075 file, lineno);
2076 goto out;
2079 m1->bits = atoi(p1);
2080 if (m1->bits == 0) {
2081 krb5_set_error_message(context, ret,
2082 N_("moduli file %s have un-parsable "
2083 "bits on line %d", ""), file, lineno);
2084 goto out;
2087 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2088 if (ret)
2089 goto out;
2090 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2091 if (ret)
2092 goto out;
2093 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2094 if (ret)
2095 goto out;
2097 *m = m1;
2099 return 0;
2100 out:
2101 free(m1->name);
2102 der_free_heim_integer(&m1->p);
2103 der_free_heim_integer(&m1->g);
2104 der_free_heim_integer(&m1->q);
2105 free(m1);
2106 return ret;
2109 void
2110 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2112 int i;
2113 for (i = 0; moduli[i] != NULL; i++) {
2114 free(moduli[i]->name);
2115 der_free_heim_integer(&moduli[i]->p);
2116 der_free_heim_integer(&moduli[i]->g);
2117 der_free_heim_integer(&moduli[i]->q);
2118 free(moduli[i]);
2120 free(moduli);
2123 static const char *default_moduli_RFC2412_MODP_group2 =
2124 /* name */
2125 "RFC2412-MODP-group2 "
2126 /* bits */
2127 "1024 "
2128 /* p */
2129 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2130 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2131 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2132 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2133 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2134 "FFFFFFFF" "FFFFFFFF "
2135 /* g */
2136 "02 "
2137 /* q */
2138 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2139 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2140 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2141 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2142 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2143 "FFFFFFFF" "FFFFFFFF";
2145 static const char *default_moduli_rfc3526_MODP_group14 =
2146 /* name */
2147 "rfc3526-MODP-group14 "
2148 /* bits */
2149 "1760 "
2150 /* p */
2151 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2152 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2153 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2154 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2155 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2156 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2157 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2158 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2159 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2160 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2161 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2162 /* g */
2163 "02 "
2164 /* q */
2165 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2166 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2167 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2168 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2169 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2170 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2171 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2172 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2173 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2174 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2175 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2177 krb5_error_code
2178 _krb5_parse_moduli(krb5_context context, const char *file,
2179 struct krb5_dh_moduli ***moduli)
2181 /* name bits P G Q */
2182 krb5_error_code ret;
2183 struct krb5_dh_moduli **m = NULL, **m2;
2184 char buf[4096];
2185 FILE *f;
2186 int lineno = 0, n = 0;
2188 *moduli = NULL;
2190 m = calloc(1, sizeof(m[0]) * 3);
2191 if (m == NULL) {
2192 krb5_set_error_message(context, ENOMEM,
2193 N_("malloc: out of memory", ""));
2194 return ENOMEM;
2197 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2198 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2199 if (ret) {
2200 _krb5_free_moduli(m);
2201 return ret;
2203 n++;
2205 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2206 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2207 if (ret) {
2208 _krb5_free_moduli(m);
2209 return ret;
2211 n++;
2214 if (file == NULL)
2215 file = MODULI_FILE;
2217 f = fopen(file, "r");
2218 if (f == NULL) {
2219 *moduli = m;
2220 return 0;
2222 rk_cloexec_file(f);
2224 while(fgets(buf, sizeof(buf), f) != NULL) {
2225 struct krb5_dh_moduli *element;
2227 buf[strcspn(buf, "\n")] = '\0';
2228 lineno++;
2230 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2231 if (m2 == NULL) {
2232 _krb5_free_moduli(m);
2233 krb5_set_error_message(context, ENOMEM,
2234 N_("malloc: out of memory", ""));
2235 return ENOMEM;
2237 m = m2;
2239 m[n] = NULL;
2241 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2242 if (ret) {
2243 _krb5_free_moduli(m);
2244 return ret;
2246 if (element == NULL)
2247 continue;
2249 m[n] = element;
2250 m[n + 1] = NULL;
2251 n++;
2253 *moduli = m;
2254 return 0;
2257 krb5_error_code
2258 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2259 heim_integer *p, heim_integer *g, heim_integer *q,
2260 struct krb5_dh_moduli **moduli,
2261 char **name)
2263 int i;
2265 if (name)
2266 *name = NULL;
2268 for (i = 0; moduli[i] != NULL; i++) {
2269 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2270 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2271 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2273 if (bits && bits > moduli[i]->bits) {
2274 krb5_set_error_message(context,
2275 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2276 N_("PKINIT: DH group parameter %s "
2277 "no accepted, not enough bits "
2278 "generated", ""),
2279 moduli[i]->name);
2280 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2282 if (name)
2283 *name = strdup(moduli[i]->name);
2284 return 0;
2287 krb5_set_error_message(context,
2288 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2289 N_("PKINIT: DH group parameter no ok", ""));
2290 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2292 #endif /* PKINIT */
2294 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2295 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2297 #ifdef PKINIT
2298 krb5_pk_init_ctx ctx;
2300 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2301 return;
2302 ctx = opt->opt_private->pk_init_ctx;
2303 switch (ctx->keyex) {
2304 case USE_DH:
2305 if (ctx->u.dh)
2306 DH_free(ctx->u.dh);
2307 break;
2308 case USE_RSA:
2309 break;
2310 case USE_ECDH:
2311 #ifdef HAVE_OPENSSL
2312 if (ctx->u.eckey)
2313 EC_KEY_free(ctx->u.eckey);
2314 #endif
2315 break;
2317 if (ctx->id) {
2318 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2319 hx509_certs_free(&ctx->id->certs);
2320 hx509_cert_free(ctx->id->cert);
2321 hx509_certs_free(&ctx->id->anchors);
2322 hx509_certs_free(&ctx->id->certpool);
2324 if (ctx->clientDHNonce) {
2325 krb5_free_data(NULL, ctx->clientDHNonce);
2326 ctx->clientDHNonce = NULL;
2328 if (ctx->m)
2329 _krb5_free_moduli(ctx->m);
2330 free(ctx->id);
2331 ctx->id = NULL;
2333 free(opt->opt_private->pk_init_ctx);
2334 opt->opt_private->pk_init_ctx = NULL;
2335 #endif
2338 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2339 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2340 krb5_get_init_creds_opt *opt,
2341 krb5_principal principal,
2342 const char *user_id,
2343 const char *x509_anchors,
2344 char * const * pool,
2345 char * const * pki_revoke,
2346 int flags,
2347 krb5_prompter_fct prompter,
2348 void *prompter_data,
2349 char *password)
2351 #ifdef PKINIT
2352 krb5_error_code ret;
2353 char *anchors = NULL;
2355 if (opt->opt_private == NULL) {
2356 krb5_set_error_message(context, EINVAL,
2357 N_("PKINIT: on non extendable opt", ""));
2358 return EINVAL;
2361 opt->opt_private->pk_init_ctx =
2362 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2363 if (opt->opt_private->pk_init_ctx == NULL) {
2364 krb5_set_error_message(context, ENOMEM,
2365 N_("malloc: out of memory", ""));
2366 return ENOMEM;
2368 opt->opt_private->pk_init_ctx->require_binding = 0;
2369 opt->opt_private->pk_init_ctx->require_eku = 1;
2370 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2371 opt->opt_private->pk_init_ctx->peer = NULL;
2373 /* XXX implement krb5_appdefault_strings */
2374 if (pool == NULL)
2375 pool = krb5_config_get_strings(context, NULL,
2376 "appdefaults",
2377 "pkinit_pool",
2378 NULL);
2380 if (pki_revoke == NULL)
2381 pki_revoke = krb5_config_get_strings(context, NULL,
2382 "appdefaults",
2383 "pkinit_revoke",
2384 NULL);
2386 if (x509_anchors == NULL) {
2387 krb5_appdefault_string(context, "kinit",
2388 krb5_principal_get_realm(context, principal),
2389 "pkinit_anchors", NULL, &anchors);
2390 x509_anchors = anchors;
2393 if (flags & 4)
2394 opt->opt_private->pk_init_ctx->anonymous = 1;
2396 ret = _krb5_pk_load_id(context,
2397 &opt->opt_private->pk_init_ctx->id,
2398 user_id,
2399 x509_anchors,
2400 pool,
2401 pki_revoke,
2402 prompter,
2403 prompter_data,
2404 password);
2405 if (ret) {
2406 free(opt->opt_private->pk_init_ctx);
2407 opt->opt_private->pk_init_ctx = NULL;
2408 return ret;
2411 if (opt->opt_private->pk_init_ctx->id->certs) {
2412 _krb5_pk_set_user_id(context,
2413 principal,
2414 opt->opt_private->pk_init_ctx,
2415 opt->opt_private->pk_init_ctx->id->certs);
2416 } else
2417 opt->opt_private->pk_init_ctx->id->cert = NULL;
2419 if ((flags & 2) == 0) {
2420 hx509_context hx509ctx = context->hx509ctx;
2421 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2423 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2426 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2428 if (cert) {
2429 AlgorithmIdentifier alg;
2431 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2432 if (ret == 0) {
2433 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2434 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2435 free_AlgorithmIdentifier(&alg);
2439 } else {
2440 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2442 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2443 krb5_set_error_message(context, EINVAL,
2444 N_("No anonymous pkinit support in RSA mode", ""));
2445 return EINVAL;
2449 return 0;
2450 #else
2451 krb5_set_error_message(context, EINVAL,
2452 N_("no support for PKINIT compiled in", ""));
2453 return EINVAL;
2454 #endif
2457 krb5_error_code KRB5_LIB_FUNCTION
2458 _krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2459 krb5_get_init_creds_opt *opt,
2460 struct hx509_certs_data *certs)
2462 #ifdef PKINIT
2463 if (opt->opt_private == NULL) {
2464 krb5_set_error_message(context, EINVAL,
2465 N_("PKINIT: on non extendable opt", ""));
2466 return EINVAL;
2468 if (opt->opt_private->pk_init_ctx == NULL) {
2469 krb5_set_error_message(context, EINVAL,
2470 N_("PKINIT: on pkinit context", ""));
2471 return EINVAL;
2474 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2476 return 0;
2477 #else
2478 krb5_set_error_message(context, EINVAL,
2479 N_("no support for PKINIT compiled in", ""));
2480 return EINVAL;
2481 #endif
2484 #ifdef PKINIT
2486 static int
2487 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2489 hx509_octet_string_list list;
2490 int ret;
2492 *upn = NULL;
2494 ret = hx509_cert_find_subjectAltName_otherName(context,
2495 cert,
2496 &asn1_oid_id_pkinit_ms_san,
2497 &list);
2498 if (ret)
2499 return 0;
2501 if (list.len > 0 && list.val[0].length > 0)
2502 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2503 upn, NULL);
2504 else
2505 ret = 1;
2506 hx509_free_octet_string_list(&list);
2508 return ret;
2511 static int
2512 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2514 char *upn;
2515 int ret;
2517 ret = get_ms_san(context, cert, &upn);
2518 if (ret == 0)
2519 free(upn);
2520 return ret;
2525 #endif
2528 * Private since it need to be redesigned using krb5_get_init_creds()
2531 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2532 _krb5_pk_enterprise_cert(krb5_context context,
2533 const char *user_id,
2534 krb5_const_realm realm,
2535 krb5_principal *principal,
2536 struct hx509_certs_data **res)
2538 #ifdef PKINIT
2539 krb5_error_code ret;
2540 hx509_certs certs, result;
2541 hx509_cert cert;
2542 hx509_query *q;
2543 char *name;
2545 *principal = NULL;
2546 if (res)
2547 *res = NULL;
2549 if (user_id == NULL) {
2550 krb5_set_error_message(context, ENOENT, "no user id");
2551 return ENOENT;
2554 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2555 if (ret) {
2556 pk_copy_error(context, context->hx509ctx, ret,
2557 "Failed to init cert certs");
2558 goto out;
2561 ret = hx509_query_alloc(context->hx509ctx, &q);
2562 if (ret) {
2563 krb5_set_error_message(context, ret, "out of memory");
2564 hx509_certs_free(&certs);
2565 goto out;
2568 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2569 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2570 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2571 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2573 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2574 hx509_query_free(context->hx509ctx, q);
2575 hx509_certs_free(&certs);
2576 if (ret) {
2577 pk_copy_error(context, context->hx509ctx, ret,
2578 "Failed to find PKINIT certificate");
2579 return ret;
2582 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2583 hx509_certs_free(&result);
2584 if (ret) {
2585 pk_copy_error(context, context->hx509ctx, ret,
2586 "Failed to get one cert");
2587 goto out;
2590 ret = get_ms_san(context->hx509ctx, cert, &name);
2591 if (ret) {
2592 pk_copy_error(context, context->hx509ctx, ret,
2593 "Failed to get MS SAN");
2594 goto out;
2597 ret = krb5_make_principal(context, principal, realm, name, NULL);
2598 free(name);
2599 if (ret)
2600 goto out;
2602 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2604 if (res) {
2605 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2606 if (ret) {
2607 hx509_cert_free(cert);
2608 goto out;
2611 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2612 if (ret) {
2613 hx509_certs_free(res);
2614 goto out;
2618 out:
2619 hx509_cert_free(cert);
2621 return ret;
2622 #else
2623 krb5_set_error_message(context, EINVAL,
2624 N_("no support for PKINIT compiled in", ""));
2625 return EINVAL;
2626 #endif