Use anon realm for anonymous PKINIT
[heimdal.git] / lib / krb5 / pkinit.c
blob0a4804a2fa66a1e7a6c8e684845c21785046e401
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", NULL },
187 { "PKINIT EKU", NULL },
188 { "MS EKU", NULL },
189 { "any (or no)", NULL }
191 int ret = HX509_CERT_NOT_FOUND;
192 size_t i, start = 1;
193 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
194 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
197 if (id->flags & PKINIT_BTMM)
198 start = 0;
200 cf[0].oid = &mobileMe;
201 cf[1].oid = &asn1_oid_id_pkekuoid;
202 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
203 cf[3].oid = NULL;
205 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
206 ret = hx509_query_match_eku(q, cf[i].oid);
207 if (ret) {
208 pk_copy_error(context, context->hx509ctx, ret,
209 "Failed setting %s OID", cf[i].type);
210 return ret;
213 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
214 if (ret == 0)
215 break;
216 pk_copy_error(context, context->hx509ctx, ret,
217 "Failed finding certificate with %s OID", cf[i].type);
219 return ret;
223 static krb5_error_code
224 create_signature(krb5_context context,
225 const heim_oid *eContentType,
226 krb5_data *eContent,
227 struct krb5_pk_identity *id,
228 hx509_peer_info peer,
229 krb5_data *sd_data)
231 int ret, flags = 0;
233 if (id->cert == NULL)
234 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
236 ret = hx509_cms_create_signed_1(context->hx509ctx,
237 flags,
238 eContentType,
239 eContent->data,
240 eContent->length,
241 NULL,
242 id->cert,
243 peer,
244 NULL,
245 id->certs,
246 sd_data);
247 if (ret) {
248 pk_copy_error(context, context->hx509ctx, ret,
249 "Create CMS signedData");
250 return ret;
253 return 0;
256 static int
257 cert2epi(hx509_context context, void *ctx, hx509_cert c)
259 ExternalPrincipalIdentifiers *ids = ctx;
260 ExternalPrincipalIdentifier id;
261 hx509_name subject = NULL;
262 void *p;
263 int ret;
265 if (ids->len > 10)
266 return 0;
268 memset(&id, 0, sizeof(id));
270 ret = hx509_cert_get_subject(c, &subject);
271 if (ret)
272 return ret;
274 if (hx509_name_is_null_p(subject) != 0) {
276 id.subjectName = calloc(1, sizeof(*id.subjectName));
277 if (id.subjectName == NULL) {
278 hx509_name_free(&subject);
279 free_ExternalPrincipalIdentifier(&id);
280 return ENOMEM;
283 ret = hx509_name_binary(subject, id.subjectName);
284 if (ret) {
285 hx509_name_free(&subject);
286 free_ExternalPrincipalIdentifier(&id);
287 return ret;
290 hx509_name_free(&subject);
293 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
294 if (id.issuerAndSerialNumber == NULL) {
295 free_ExternalPrincipalIdentifier(&id);
296 return ENOMEM;
300 IssuerAndSerialNumber iasn;
301 hx509_name issuer;
302 size_t size = 0;
304 memset(&iasn, 0, sizeof(iasn));
306 ret = hx509_cert_get_issuer(c, &issuer);
307 if (ret) {
308 free_ExternalPrincipalIdentifier(&id);
309 return ret;
312 ret = hx509_name_to_Name(issuer, &iasn.issuer);
313 hx509_name_free(&issuer);
314 if (ret) {
315 free_ExternalPrincipalIdentifier(&id);
316 return ret;
319 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
320 if (ret) {
321 free_IssuerAndSerialNumber(&iasn);
322 free_ExternalPrincipalIdentifier(&id);
323 return ret;
326 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
327 id.issuerAndSerialNumber->data,
328 id.issuerAndSerialNumber->length,
329 &iasn, &size, ret);
330 free_IssuerAndSerialNumber(&iasn);
331 if (ret)
332 return ret;
333 if (id.issuerAndSerialNumber->length != size)
334 abort();
337 id.subjectKeyIdentifier = NULL;
339 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
340 if (p == NULL) {
341 free_ExternalPrincipalIdentifier(&id);
342 return ENOMEM;
345 ids->val = p;
346 ids->val[ids->len] = id;
347 ids->len++;
349 return 0;
352 static krb5_error_code
353 build_edi(krb5_context context,
354 hx509_context hx509ctx,
355 hx509_certs certs,
356 ExternalPrincipalIdentifiers *ids)
358 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
361 static krb5_error_code
362 build_auth_pack(krb5_context context,
363 unsigned nonce,
364 krb5_pk_init_ctx ctx,
365 const KDC_REQ_BODY *body,
366 AuthPack *a)
368 size_t buf_size, len = 0;
369 krb5_error_code ret;
370 void *buf;
371 krb5_timestamp sec;
372 int32_t usec;
373 Checksum checksum;
375 krb5_clear_error_message(context);
377 memset(&checksum, 0, sizeof(checksum));
379 krb5_us_timeofday(context, &sec, &usec);
380 a->pkAuthenticator.ctime = sec;
381 a->pkAuthenticator.nonce = nonce;
383 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
384 if (ret)
385 return ret;
386 if (buf_size != len)
387 krb5_abortx(context, "internal error in ASN.1 encoder");
389 ret = krb5_create_checksum(context,
390 NULL,
392 CKSUMTYPE_SHA1,
393 buf,
394 len,
395 &checksum);
396 free(buf);
397 if (ret)
398 return ret;
400 ALLOC(a->pkAuthenticator.paChecksum, 1);
401 if (a->pkAuthenticator.paChecksum == NULL) {
402 return krb5_enomem(context);
405 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
406 checksum.checksum.data, checksum.checksum.length);
407 free_Checksum(&checksum);
408 if (ret)
409 return ret;
411 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
412 const char *moduli_file;
413 unsigned long dh_min_bits;
414 krb5_data dhbuf;
415 size_t size = 0;
417 krb5_data_zero(&dhbuf);
421 moduli_file = krb5_config_get_string(context, NULL,
422 "libdefaults",
423 "moduli",
424 NULL);
426 dh_min_bits =
427 krb5_config_get_int_default(context, NULL, 0,
428 "libdefaults",
429 "pkinit_dh_min_bits",
430 NULL);
432 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
433 if (ret)
434 return ret;
436 ctx->u.dh = DH_new();
437 if (ctx->u.dh == NULL)
438 return krb5_enomem(context);
440 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
441 if (ret)
442 return ret;
444 if (DH_generate_key(ctx->u.dh) != 1) {
445 krb5_set_error_message(context, ENOMEM,
446 N_("pkinit: failed to generate DH key", ""));
447 return ENOMEM;
451 if (1 /* support_cached_dh */) {
452 ALLOC(a->clientDHNonce, 1);
453 if (a->clientDHNonce == NULL) {
454 krb5_clear_error_message(context);
455 return ENOMEM;
457 ret = krb5_data_alloc(a->clientDHNonce, 40);
458 if (a->clientDHNonce == NULL) {
459 krb5_clear_error_message(context);
460 return ret;
462 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
463 ret = krb5_copy_data(context, a->clientDHNonce,
464 &ctx->clientDHNonce);
465 if (ret)
466 return ret;
469 ALLOC(a->clientPublicValue, 1);
470 if (a->clientPublicValue == NULL)
471 return ENOMEM;
473 if (ctx->keyex == USE_DH) {
474 DH *dh = ctx->u.dh;
475 DomainParameters dp;
476 heim_integer dh_pub_key;
478 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
479 &a->clientPublicValue->algorithm.algorithm);
480 if (ret)
481 return ret;
483 memset(&dp, 0, sizeof(dp));
485 ret = BN_to_integer(context, dh->p, &dp.p);
486 if (ret) {
487 free_DomainParameters(&dp);
488 return ret;
490 ret = BN_to_integer(context, dh->g, &dp.g);
491 if (ret) {
492 free_DomainParameters(&dp);
493 return ret;
495 dp.q = calloc(1, sizeof(*dp.q));
496 if (dp.q == NULL) {
497 free_DomainParameters(&dp);
498 return ENOMEM;
500 ret = BN_to_integer(context, dh->q, dp.q);
501 if (ret) {
502 free_DomainParameters(&dp);
503 return ret;
505 dp.j = NULL;
506 dp.validationParms = NULL;
508 a->clientPublicValue->algorithm.parameters =
509 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
510 if (a->clientPublicValue->algorithm.parameters == NULL) {
511 free_DomainParameters(&dp);
512 return ret;
515 ASN1_MALLOC_ENCODE(DomainParameters,
516 a->clientPublicValue->algorithm.parameters->data,
517 a->clientPublicValue->algorithm.parameters->length,
518 &dp, &size, ret);
519 free_DomainParameters(&dp);
520 if (ret)
521 return ret;
522 if (size != a->clientPublicValue->algorithm.parameters->length)
523 krb5_abortx(context, "Internal ASN1 encoder error");
525 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
526 if (ret)
527 return ret;
529 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
530 &dh_pub_key, &size, ret);
531 der_free_heim_integer(&dh_pub_key);
532 if (ret)
533 return ret;
534 if (size != dhbuf.length)
535 krb5_abortx(context, "asn1 internal error");
536 } else if (ctx->keyex == USE_ECDH) {
537 #ifdef HAVE_OPENSSL
538 ECParameters ecp;
539 unsigned char *p;
540 int xlen;
542 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
544 ecp.element = choice_ECParameters_namedCurve;
545 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
546 &ecp.u.namedCurve);
547 if (ret)
548 return ret;
550 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
551 if (a->clientPublicValue->algorithm.parameters == NULL) {
552 free_ECParameters(&ecp);
553 return ENOMEM;
555 ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
556 free_ECParameters(&ecp);
557 if (ret)
558 return ret;
559 if ((int)size != xlen)
560 krb5_abortx(context, "asn1 internal error");
562 a->clientPublicValue->algorithm.parameters->data = p;
563 a->clientPublicValue->algorithm.parameters->length = size;
565 /* copy in public key */
567 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
568 &a->clientPublicValue->algorithm.algorithm);
569 if (ret)
570 return ret;
572 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
573 if (ctx->u.eckey == NULL)
574 return ENOMEM;
576 ret = EC_KEY_generate_key(ctx->u.eckey);
577 if (ret != 1)
578 return EINVAL;
580 /* encode onto dhkey */
582 xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
583 if (xlen <= 0)
584 abort();
586 dhbuf.data = malloc(xlen);
587 if (dhbuf.data == NULL)
588 abort();
589 dhbuf.length = xlen;
590 p = dhbuf.data;
592 xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
593 if (xlen <= 0)
594 abort();
596 /* XXX verify that this is right with RFC3279 */
597 #else
598 return EINVAL;
599 #endif
600 } else
601 krb5_abortx(context, "internal error");
602 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
603 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
607 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
608 if (a->supportedCMSTypes == NULL)
609 return ENOMEM;
611 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
612 ctx->id->cert,
613 &a->supportedCMSTypes->val,
614 &a->supportedCMSTypes->len);
615 if (ret)
616 return ret;
619 return ret;
622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
623 _krb5_pk_mk_ContentInfo(krb5_context context,
624 const krb5_data *buf,
625 const heim_oid *oid,
626 struct ContentInfo *content_info)
628 krb5_error_code ret;
630 ret = der_copy_oid(oid, &content_info->contentType);
631 if (ret)
632 return ret;
633 ALLOC(content_info->content, 1);
634 if (content_info->content == NULL)
635 return ENOMEM;
636 content_info->content->data = malloc(buf->length);
637 if (content_info->content->data == NULL)
638 return ENOMEM;
639 memcpy(content_info->content->data, buf->data, buf->length);
640 content_info->content->length = buf->length;
641 return 0;
644 static krb5_error_code
645 pk_mk_padata(krb5_context context,
646 krb5_pk_init_ctx ctx,
647 const KDC_REQ_BODY *req_body,
648 unsigned nonce,
649 METHOD_DATA *md)
651 struct ContentInfo content_info;
652 krb5_error_code ret;
653 const heim_oid *oid = NULL;
654 size_t size = 0;
655 krb5_data buf, sd_buf;
656 int pa_type = -1;
658 krb5_data_zero(&buf);
659 krb5_data_zero(&sd_buf);
660 memset(&content_info, 0, sizeof(content_info));
662 if (ctx->type == PKINIT_WIN2K) {
663 AuthPack_Win2k ap;
664 krb5_timestamp sec;
665 int32_t usec;
667 memset(&ap, 0, sizeof(ap));
669 /* fill in PKAuthenticator */
670 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
671 if (ret) {
672 free_AuthPack_Win2k(&ap);
673 krb5_clear_error_message(context);
674 goto out;
676 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
677 if (ret) {
678 free_AuthPack_Win2k(&ap);
679 krb5_clear_error_message(context);
680 goto out;
683 krb5_us_timeofday(context, &sec, &usec);
684 ap.pkAuthenticator.ctime = sec;
685 ap.pkAuthenticator.cusec = usec;
686 ap.pkAuthenticator.nonce = nonce;
688 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
689 &ap, &size, ret);
690 free_AuthPack_Win2k(&ap);
691 if (ret) {
692 krb5_set_error_message(context, ret,
693 N_("Failed encoding AuthPackWin: %d", ""),
694 (int)ret);
695 goto out;
697 if (buf.length != size)
698 krb5_abortx(context, "internal ASN1 encoder error");
700 oid = &asn1_oid_id_pkcs7_data;
701 } else if (ctx->type == PKINIT_27) {
702 AuthPack ap;
704 memset(&ap, 0, sizeof(ap));
706 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
707 if (ret) {
708 free_AuthPack(&ap);
709 goto out;
712 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
713 free_AuthPack(&ap);
714 if (ret) {
715 krb5_set_error_message(context, ret,
716 N_("Failed encoding AuthPack: %d", ""),
717 (int)ret);
718 goto out;
720 if (buf.length != size)
721 krb5_abortx(context, "internal ASN1 encoder error");
723 oid = &asn1_oid_id_pkauthdata;
724 } else
725 krb5_abortx(context, "internal pkinit error");
727 ret = create_signature(context, oid, &buf, ctx->id,
728 ctx->peer, &sd_buf);
729 krb5_data_free(&buf);
730 if (ret)
731 goto out;
733 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
734 krb5_data_free(&sd_buf);
735 if (ret) {
736 krb5_set_error_message(context, ret,
737 N_("ContentInfo wrapping of signedData failed",""));
738 goto out;
741 if (ctx->type == PKINIT_WIN2K) {
742 PA_PK_AS_REQ_Win2k winreq;
744 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
746 memset(&winreq, 0, sizeof(winreq));
748 winreq.signed_auth_pack = buf;
750 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
751 &winreq, &size, ret);
752 free_PA_PK_AS_REQ_Win2k(&winreq);
754 } else if (ctx->type == PKINIT_27) {
755 PA_PK_AS_REQ req;
757 pa_type = KRB5_PADATA_PK_AS_REQ;
759 memset(&req, 0, sizeof(req));
760 req.signedAuthPack = buf;
762 if (ctx->trustedCertifiers) {
764 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
765 if (req.trustedCertifiers == NULL) {
766 ret = krb5_enomem(context);
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 ((unsigned)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 return krb5_enomem(context);
984 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
985 free_ReplyKeyPack_Win2k(&key_pack);
986 if (ret) {
987 krb5_set_error_message(context, ret,
988 N_("PKINIT failed copying reply key", ""));
989 free(*key);
990 *key = NULL;
993 return ret;
996 static krb5_error_code
997 get_reply_key(krb5_context context,
998 const krb5_data *content,
999 const krb5_data *req_buffer,
1000 krb5_keyblock **key)
1002 ReplyKeyPack key_pack;
1003 krb5_error_code ret;
1004 size_t size;
1006 ret = decode_ReplyKeyPack(content->data,
1007 content->length,
1008 &key_pack,
1009 &size);
1010 if (ret) {
1011 krb5_set_error_message(context, ret,
1012 N_("PKINIT decoding reply key failed", ""));
1013 free_ReplyKeyPack(&key_pack);
1014 return ret;
1018 krb5_crypto crypto;
1021 * XXX Verify kp.replyKey is a allowed enctype in the
1022 * configuration file
1025 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1026 if (ret) {
1027 free_ReplyKeyPack(&key_pack);
1028 return ret;
1031 ret = krb5_verify_checksum(context, crypto, 6,
1032 req_buffer->data, req_buffer->length,
1033 &key_pack.asChecksum);
1034 krb5_crypto_destroy(context, crypto);
1035 if (ret) {
1036 free_ReplyKeyPack(&key_pack);
1037 return ret;
1041 *key = malloc (sizeof (**key));
1042 if (*key == NULL) {
1043 free_ReplyKeyPack(&key_pack);
1044 return krb5_enomem(context);
1047 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1048 free_ReplyKeyPack(&key_pack);
1049 if (ret) {
1050 krb5_set_error_message(context, ret,
1051 N_("PKINIT failed copying reply key", ""));
1052 free(*key);
1053 *key = NULL;
1056 return ret;
1060 static krb5_error_code
1061 pk_verify_host(krb5_context context,
1062 const char *realm,
1063 const krb5_krbhst_info *hi,
1064 struct krb5_pk_init_ctx_data *ctx,
1065 struct krb5_pk_cert *host)
1067 krb5_error_code ret = 0;
1069 if (ctx->require_eku) {
1070 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1071 &asn1_oid_id_pkkdcekuoid, 0);
1072 if (ret) {
1073 krb5_set_error_message(context, ret,
1074 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1075 return ret;
1078 if (ctx->require_krbtgt_otherName) {
1079 hx509_octet_string_list list;
1080 size_t i;
1081 int matched = 0;
1083 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1084 host->cert,
1085 &asn1_oid_id_pkinit_san,
1086 &list);
1087 if (ret) {
1088 krb5_set_error_message(context, ret,
1089 N_("Failed to find the PK-INIT "
1090 "subjectAltName in the KDC "
1091 "certificate", ""));
1093 return ret;
1097 * subjectAltNames are multi-valued, and a single KDC may serve
1098 * multiple realms. The SAN validation here must accept
1099 * the KDC's cert if *any* of the SANs match the expected KDC.
1100 * It is OK for *some* of the SANs to not match, provided at least
1101 * one does.
1103 for (i = 0; matched == 0 && i < list.len; i++) {
1104 KRB5PrincipalName r;
1106 ret = decode_KRB5PrincipalName(list.val[i].data,
1107 list.val[i].length,
1109 NULL);
1110 if (ret) {
1111 krb5_set_error_message(context, ret,
1112 N_("Failed to decode the PK-INIT "
1113 "subjectAltName in the "
1114 "KDC certificate", ""));
1116 break;
1119 if (r.principalName.name_string.len == 2 &&
1120 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1121 && strcmp(r.principalName.name_string.val[1], realm) == 0
1122 && strcmp(r.realm, realm) == 0)
1123 matched = 1;
1125 free_KRB5PrincipalName(&r);
1127 hx509_free_octet_string_list(&list);
1128 if (matched == 0) {
1129 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1130 /* XXX: Lost in translation... */
1131 krb5_set_error_message(context, ret,
1132 N_("KDC have wrong realm name in "
1133 "the certificate", ""));
1136 if (ret)
1137 return ret;
1139 if (hi) {
1140 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1141 ctx->require_hostname_match,
1142 HX509_HN_HOSTNAME,
1143 hi->hostname,
1144 hi->ai->ai_addr, hi->ai->ai_addrlen);
1146 if (ret)
1147 krb5_set_error_message(context, ret,
1148 N_("Address mismatch in "
1149 "the KDC certificate", ""));
1151 return ret;
1154 static krb5_error_code
1155 pk_rd_pa_reply_enckey(krb5_context context,
1156 int type,
1157 const heim_octet_string *indata,
1158 const heim_oid *dataType,
1159 const char *realm,
1160 krb5_pk_init_ctx ctx,
1161 krb5_enctype etype,
1162 const krb5_krbhst_info *hi,
1163 unsigned nonce,
1164 const krb5_data *req_buffer,
1165 PA_DATA *pa,
1166 krb5_keyblock **key)
1168 krb5_error_code ret;
1169 struct krb5_pk_cert *host = NULL;
1170 krb5_data content;
1171 heim_oid contentType = { 0, NULL };
1172 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1174 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1175 krb5_set_error_message(context, EINVAL,
1176 N_("PKINIT: Invalid content type", ""));
1177 return EINVAL;
1180 if (ctx->type == PKINIT_WIN2K)
1181 flags |= HX509_CMS_UE_ALLOW_WEAK;
1183 ret = hx509_cms_unenvelope(context->hx509ctx,
1184 ctx->id->certs,
1185 flags,
1186 indata->data,
1187 indata->length,
1188 NULL,
1190 &contentType,
1191 &content);
1192 if (ret) {
1193 pk_copy_error(context, context->hx509ctx, ret,
1194 "Failed to unenvelope CMS data in PK-INIT reply");
1195 return ret;
1197 der_free_oid(&contentType);
1199 /* win2k uses ContentInfo */
1200 if (type == PKINIT_WIN2K) {
1201 heim_oid type2;
1202 heim_octet_string out;
1204 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1205 if (ret) {
1206 /* windows LH with interesting CMS packets */
1207 size_t ph = 1 + der_length_len(content.length);
1208 unsigned char *ptr = malloc(content.length + ph);
1209 size_t l;
1211 memcpy(ptr + ph, content.data, content.length);
1213 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1214 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1215 if (ret)
1216 return ret;
1217 free(content.data);
1218 content.data = ptr;
1219 content.length += ph;
1221 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1222 if (ret)
1223 goto out;
1225 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1226 ret = EINVAL; /* XXX */
1227 krb5_set_error_message(context, ret,
1228 N_("PKINIT: Invalid content type", ""));
1229 der_free_oid(&type2);
1230 der_free_octet_string(&out);
1231 goto out;
1233 der_free_oid(&type2);
1234 krb5_data_free(&content);
1235 ret = krb5_data_copy(&content, out.data, out.length);
1236 der_free_octet_string(&out);
1237 if (ret) {
1238 krb5_set_error_message(context, ret,
1239 N_("malloc: out of memory", ""));
1240 goto out;
1244 ret = pk_verify_sign(context,
1245 content.data,
1246 content.length,
1247 ctx->id,
1248 &contentType,
1249 &content,
1250 &host);
1251 if (ret)
1252 goto out;
1254 /* make sure that it is the kdc's certificate */
1255 ret = pk_verify_host(context, realm, hi, ctx, host);
1256 if (ret) {
1257 goto out;
1260 #if 0
1261 if (type == PKINIT_WIN2K) {
1262 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1263 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1264 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1265 goto out;
1267 } else {
1268 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1269 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1270 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1271 goto out;
1274 #endif
1276 switch(type) {
1277 case PKINIT_WIN2K:
1278 ret = get_reply_key(context, &content, req_buffer, key);
1279 if (ret != 0 && ctx->require_binding == 0)
1280 ret = get_reply_key_win(context, &content, nonce, key);
1281 break;
1282 case PKINIT_27:
1283 ret = get_reply_key(context, &content, req_buffer, key);
1284 break;
1286 if (ret)
1287 goto out;
1289 /* XXX compare given etype with key->etype */
1291 out:
1292 if (host)
1293 _krb5_pk_cert_free(host);
1294 der_free_oid(&contentType);
1295 krb5_data_free(&content);
1297 return ret;
1300 static krb5_error_code
1301 pk_rd_pa_reply_dh(krb5_context context,
1302 const heim_octet_string *indata,
1303 const heim_oid *dataType,
1304 const char *realm,
1305 krb5_pk_init_ctx ctx,
1306 krb5_enctype etype,
1307 const krb5_krbhst_info *hi,
1308 const DHNonce *c_n,
1309 const DHNonce *k_n,
1310 unsigned nonce,
1311 PA_DATA *pa,
1312 krb5_keyblock **key)
1314 const unsigned char *p;
1315 unsigned char *dh_gen_key = NULL;
1316 struct krb5_pk_cert *host = NULL;
1317 BIGNUM *kdc_dh_pubkey = NULL;
1318 KDCDHKeyInfo kdc_dh_info;
1319 heim_oid contentType = { 0, NULL };
1320 krb5_data content;
1321 krb5_error_code ret;
1322 int dh_gen_keylen = 0;
1323 size_t size;
1325 krb5_data_zero(&content);
1326 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1328 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1329 krb5_set_error_message(context, EINVAL,
1330 N_("PKINIT: Invalid content type", ""));
1331 return EINVAL;
1334 ret = pk_verify_sign(context,
1335 indata->data,
1336 indata->length,
1337 ctx->id,
1338 &contentType,
1339 &content,
1340 &host);
1341 if (ret)
1342 goto out;
1344 /* make sure that it is the kdc's certificate */
1345 ret = pk_verify_host(context, realm, hi, ctx, host);
1346 if (ret)
1347 goto out;
1349 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1350 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1351 krb5_set_error_message(context, ret,
1352 N_("pkinit - dh reply contains wrong oid", ""));
1353 goto out;
1356 ret = decode_KDCDHKeyInfo(content.data,
1357 content.length,
1358 &kdc_dh_info,
1359 &size);
1361 if (ret) {
1362 krb5_set_error_message(context, ret,
1363 N_("pkinit - failed to decode "
1364 "KDC DH Key Info", ""));
1365 goto out;
1368 if (kdc_dh_info.nonce != nonce) {
1369 ret = KRB5KRB_AP_ERR_MODIFIED;
1370 krb5_set_error_message(context, ret,
1371 N_("PKINIT: DH nonce is wrong", ""));
1372 goto out;
1375 if (kdc_dh_info.dhKeyExpiration) {
1376 if (k_n == NULL) {
1377 ret = KRB5KRB_ERR_GENERIC;
1378 krb5_set_error_message(context, ret,
1379 N_("pkinit; got key expiration "
1380 "without server nonce", ""));
1381 goto out;
1383 if (c_n == NULL) {
1384 ret = KRB5KRB_ERR_GENERIC;
1385 krb5_set_error_message(context, ret,
1386 N_("pkinit; got DH reuse but no "
1387 "client nonce", ""));
1388 goto out;
1390 } else {
1391 if (k_n) {
1392 ret = KRB5KRB_ERR_GENERIC;
1393 krb5_set_error_message(context, ret,
1394 N_("pkinit: got server nonce "
1395 "without key expiration", ""));
1396 goto out;
1398 c_n = NULL;
1402 p = kdc_dh_info.subjectPublicKey.data;
1403 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1405 if (ctx->keyex == USE_DH) {
1406 DHPublicKey k;
1407 ret = decode_DHPublicKey(p, size, &k, NULL);
1408 if (ret) {
1409 krb5_set_error_message(context, ret,
1410 N_("pkinit: can't decode "
1411 "without key expiration", ""));
1412 goto out;
1415 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1416 free_DHPublicKey(&k);
1417 if (kdc_dh_pubkey == NULL) {
1418 ret = ENOMEM;
1419 goto out;
1423 size = DH_size(ctx->u.dh);
1425 dh_gen_key = malloc(size);
1426 if (dh_gen_key == NULL) {
1427 ret = krb5_enomem(context);
1428 goto out;
1431 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1432 if (dh_gen_keylen == -1) {
1433 ret = KRB5KRB_ERR_GENERIC;
1434 dh_gen_keylen = 0;
1435 krb5_set_error_message(context, ret,
1436 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1437 goto out;
1439 if (dh_gen_keylen < (int)size) {
1440 size -= dh_gen_keylen;
1441 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1442 memset(dh_gen_key, 0, size);
1445 } else {
1446 #ifdef HAVE_OPENSSL
1447 const EC_GROUP *group;
1448 EC_KEY *public = NULL;
1450 group = EC_KEY_get0_group(ctx->u.eckey);
1452 public = EC_KEY_new();
1453 if (public == NULL) {
1454 ret = ENOMEM;
1455 goto out;
1457 if (EC_KEY_set_group(public, group) != 1) {
1458 EC_KEY_free(public);
1459 ret = ENOMEM;
1460 goto out;
1463 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1464 EC_KEY_free(public);
1465 ret = KRB5KRB_ERR_GENERIC;
1466 krb5_set_error_message(context, ret,
1467 N_("PKINIT: Can't parse ECDH public key", ""));
1468 goto out;
1471 size = (EC_GROUP_get_degree(group) + 7) / 8;
1472 dh_gen_key = malloc(size);
1473 if (dh_gen_key == NULL) {
1474 EC_KEY_free(public);
1475 ret = krb5_enomem(context);
1476 goto out;
1478 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1479 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1480 EC_KEY_free(public);
1481 if (dh_gen_keylen == -1) {
1482 ret = KRB5KRB_ERR_GENERIC;
1483 dh_gen_keylen = 0;
1484 krb5_set_error_message(context, ret,
1485 N_("PKINIT: Can't compute ECDH public key", ""));
1486 goto out;
1488 #else
1489 ret = EINVAL;
1490 #endif
1493 if (dh_gen_keylen <= 0) {
1494 ret = EINVAL;
1495 krb5_set_error_message(context, ret,
1496 N_("PKINIT: resulting DH key <= 0", ""));
1497 dh_gen_keylen = 0;
1498 goto out;
1501 *key = malloc (sizeof (**key));
1502 if (*key == NULL) {
1503 ret = krb5_enomem(context);
1504 goto out;
1507 ret = _krb5_pk_octetstring2key(context,
1508 etype,
1509 dh_gen_key, dh_gen_keylen,
1510 c_n, k_n,
1511 *key);
1512 if (ret) {
1513 krb5_set_error_message(context, ret,
1514 N_("PKINIT: can't create key from DH key", ""));
1515 free(*key);
1516 *key = NULL;
1517 goto out;
1520 out:
1521 if (kdc_dh_pubkey)
1522 BN_free(kdc_dh_pubkey);
1523 if (dh_gen_key) {
1524 memset(dh_gen_key, 0, dh_gen_keylen);
1525 free(dh_gen_key);
1527 if (host)
1528 _krb5_pk_cert_free(host);
1529 if (content.data)
1530 krb5_data_free(&content);
1531 der_free_oid(&contentType);
1532 free_KDCDHKeyInfo(&kdc_dh_info);
1534 return ret;
1537 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1538 _krb5_pk_rd_pa_reply(krb5_context context,
1539 const char *realm,
1540 void *c,
1541 krb5_enctype etype,
1542 const krb5_krbhst_info *hi,
1543 unsigned nonce,
1544 const krb5_data *req_buffer,
1545 PA_DATA *pa,
1546 krb5_keyblock **key)
1548 krb5_pk_init_ctx ctx = c;
1549 krb5_error_code ret;
1550 size_t size;
1552 /* Check for IETF PK-INIT first */
1553 if (ctx->type == PKINIT_27) {
1554 PA_PK_AS_REP rep;
1555 heim_octet_string os, data;
1556 heim_oid oid;
1558 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1559 krb5_set_error_message(context, EINVAL,
1560 N_("PKINIT: wrong padata recv", ""));
1561 return EINVAL;
1564 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1565 pa->padata_value.length,
1566 &rep,
1567 &size);
1568 if (ret) {
1569 krb5_set_error_message(context, ret,
1570 N_("Failed to decode pkinit AS rep", ""));
1571 return ret;
1574 switch (rep.element) {
1575 case choice_PA_PK_AS_REP_dhInfo:
1576 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1577 os = rep.u.dhInfo.dhSignedData;
1578 break;
1579 case choice_PA_PK_AS_REP_encKeyPack:
1580 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1581 os = rep.u.encKeyPack;
1582 break;
1583 default: {
1584 PA_PK_AS_REP_BTMM btmm;
1585 free_PA_PK_AS_REP(&rep);
1586 memset(&rep, 0, sizeof(rep));
1588 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1590 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1591 pa->padata_value.length,
1592 &btmm,
1593 &size);
1594 if (ret) {
1595 krb5_set_error_message(context, EINVAL,
1596 N_("PKINIT: -27 reply "
1597 "invalid content type", ""));
1598 return EINVAL;
1601 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1602 free_PA_PK_AS_REP_BTMM(&btmm);
1603 ret = EINVAL;
1604 krb5_set_error_message(context, ret,
1605 N_("DH mode not supported for BTMM mode", ""));
1606 return ret;
1610 * Transform to IETF style PK-INIT reply so that free works below
1613 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1614 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1615 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1616 btmm.encKeyPack->data = NULL;
1617 btmm.encKeyPack->length = 0;
1618 free_PA_PK_AS_REP_BTMM(&btmm);
1619 os = rep.u.encKeyPack;
1623 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1624 if (ret) {
1625 free_PA_PK_AS_REP(&rep);
1626 krb5_set_error_message(context, ret,
1627 N_("PKINIT: failed to unwrap CI", ""));
1628 return ret;
1631 switch (rep.element) {
1632 case choice_PA_PK_AS_REP_dhInfo:
1633 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1634 ctx->clientDHNonce,
1635 rep.u.dhInfo.serverDHNonce,
1636 nonce, pa, key);
1637 break;
1638 case choice_PA_PK_AS_REP_encKeyPack:
1639 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1640 ctx, etype, hi, nonce, req_buffer, pa, key);
1641 break;
1642 default:
1643 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1645 der_free_octet_string(&data);
1646 der_free_oid(&oid);
1647 free_PA_PK_AS_REP(&rep);
1649 } else if (ctx->type == PKINIT_WIN2K) {
1650 PA_PK_AS_REP_Win2k w2krep;
1652 /* Check for Windows encoding of the AS-REP pa data */
1654 #if 0 /* should this be ? */
1655 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1656 krb5_set_error_message(context, EINVAL,
1657 "PKINIT: wrong padata recv");
1658 return EINVAL;
1660 #endif
1662 memset(&w2krep, 0, sizeof(w2krep));
1664 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1665 pa->padata_value.length,
1666 &w2krep,
1667 &size);
1668 if (ret) {
1669 krb5_set_error_message(context, ret,
1670 N_("PKINIT: Failed decoding windows "
1671 "pkinit reply %d", ""), (int)ret);
1672 return ret;
1675 krb5_clear_error_message(context);
1677 switch (w2krep.element) {
1678 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1679 heim_octet_string data;
1680 heim_oid oid;
1682 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1683 &oid, &data, NULL);
1684 free_PA_PK_AS_REP_Win2k(&w2krep);
1685 if (ret) {
1686 krb5_set_error_message(context, ret,
1687 N_("PKINIT: failed to unwrap CI", ""));
1688 return ret;
1691 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1692 ctx, etype, hi, nonce, req_buffer, pa, key);
1693 der_free_octet_string(&data);
1694 der_free_oid(&oid);
1696 break;
1698 default:
1699 free_PA_PK_AS_REP_Win2k(&w2krep);
1700 ret = EINVAL;
1701 krb5_set_error_message(context, ret,
1702 N_("PKINIT: win2k reply invalid "
1703 "content type", ""));
1704 break;
1707 } else {
1708 ret = EINVAL;
1709 krb5_set_error_message(context, ret,
1710 N_("PKINIT: unknown reply type", ""));
1713 return ret;
1716 struct prompter {
1717 krb5_context context;
1718 krb5_prompter_fct prompter;
1719 void *prompter_data;
1722 static int
1723 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1725 krb5_error_code ret;
1726 krb5_prompt prompt;
1727 krb5_data password_data;
1728 struct prompter *p = data;
1730 password_data.data = prompter->reply.data;
1731 password_data.length = prompter->reply.length;
1733 prompt.prompt = prompter->prompt;
1734 prompt.hidden = hx509_prompt_hidden(prompter->type);
1735 prompt.reply = &password_data;
1737 switch (prompter->type) {
1738 case HX509_PROMPT_TYPE_INFO:
1739 prompt.type = KRB5_PROMPT_TYPE_INFO;
1740 break;
1741 case HX509_PROMPT_TYPE_PASSWORD:
1742 case HX509_PROMPT_TYPE_QUESTION:
1743 default:
1744 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1745 break;
1748 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1749 if (ret) {
1750 memset (prompter->reply.data, 0, prompter->reply.length);
1751 return 1;
1753 return 0;
1756 static krb5_error_code
1757 _krb5_pk_set_user_id(krb5_context context,
1758 krb5_principal principal,
1759 krb5_pk_init_ctx ctx,
1760 struct hx509_certs_data *certs)
1762 hx509_certs c = hx509_certs_ref(certs);
1763 hx509_query *q = NULL;
1764 int ret;
1766 if (ctx->id->certs)
1767 hx509_certs_free(&ctx->id->certs);
1768 if (ctx->id->cert) {
1769 hx509_cert_free(ctx->id->cert);
1770 ctx->id->cert = NULL;
1773 ctx->id->certs = c;
1774 ctx->anonymous = 0;
1776 ret = hx509_query_alloc(context->hx509ctx, &q);
1777 if (ret) {
1778 pk_copy_error(context, context->hx509ctx, ret,
1779 "Allocate query to find signing certificate");
1780 return ret;
1783 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1784 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1786 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1787 ctx->id->flags |= PKINIT_BTMM;
1790 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1791 hx509_query_free(context->hx509ctx, q);
1793 if (ret == 0 && _krb5_have_debug(context, 2)) {
1794 hx509_name name;
1795 char *str, *sn;
1796 heim_integer i;
1798 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1799 if (ret)
1800 goto out;
1802 ret = hx509_name_to_string(name, &str);
1803 hx509_name_free(&name);
1804 if (ret)
1805 goto out;
1807 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1808 if (ret) {
1809 free(str);
1810 goto out;
1813 ret = der_print_hex_heim_integer(&i, &sn);
1814 der_free_heim_integer(&i);
1815 if (ret) {
1816 free(name);
1817 goto out;
1820 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1821 free(str);
1822 free(sn);
1824 out:
1826 return ret;
1829 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1830 _krb5_pk_load_id(krb5_context context,
1831 struct krb5_pk_identity **ret_id,
1832 const char *user_id,
1833 const char *anchor_id,
1834 char * const *chain_list,
1835 char * const *revoke_list,
1836 krb5_prompter_fct prompter,
1837 void *prompter_data,
1838 char *password)
1840 struct krb5_pk_identity *id = NULL;
1841 struct prompter p;
1842 int ret;
1844 *ret_id = NULL;
1846 if (anchor_id == NULL) {
1847 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1848 N_("PKINIT: No anchor given", ""));
1849 return HEIM_PKINIT_NO_VALID_CA;
1852 /* load cert */
1854 id = calloc(1, sizeof(*id));
1855 if (id == NULL)
1856 return krb5_enomem(context);
1858 if (user_id) {
1859 hx509_lock lock;
1861 ret = hx509_lock_init(context->hx509ctx, &lock);
1862 if (ret) {
1863 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1864 goto out;
1867 if (password && password[0])
1868 hx509_lock_add_password(lock, password);
1870 if (prompter) {
1871 p.context = context;
1872 p.prompter = prompter;
1873 p.prompter_data = prompter_data;
1875 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1876 if (ret) {
1877 hx509_lock_free(lock);
1878 goto out;
1882 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1883 hx509_lock_free(lock);
1884 if (ret) {
1885 pk_copy_error(context, context->hx509ctx, ret,
1886 "Failed to init cert certs");
1887 goto out;
1889 } else {
1890 id->certs = NULL;
1893 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1894 if (ret) {
1895 pk_copy_error(context, context->hx509ctx, ret,
1896 "Failed to init anchors");
1897 goto out;
1900 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1901 0, NULL, &id->certpool);
1902 if (ret) {
1903 pk_copy_error(context, context->hx509ctx, ret,
1904 "Failed to init chain");
1905 goto out;
1908 while (chain_list && *chain_list) {
1909 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1910 NULL, *chain_list);
1911 if (ret) {
1912 pk_copy_error(context, context->hx509ctx, ret,
1913 "Failed to laod chain %s",
1914 *chain_list);
1915 goto out;
1917 chain_list++;
1920 if (revoke_list) {
1921 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1922 if (ret) {
1923 pk_copy_error(context, context->hx509ctx, ret,
1924 "Failed init revoke list");
1925 goto out;
1928 while (*revoke_list) {
1929 ret = hx509_revoke_add_crl(context->hx509ctx,
1930 id->revokectx,
1931 *revoke_list);
1932 if (ret) {
1933 pk_copy_error(context, context->hx509ctx, ret,
1934 "Failed load revoke list");
1935 goto out;
1937 revoke_list++;
1939 } else
1940 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1942 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1943 if (ret) {
1944 pk_copy_error(context, context->hx509ctx, ret,
1945 "Failed init verify context");
1946 goto out;
1949 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1950 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1952 out:
1953 if (ret) {
1954 hx509_verify_destroy_ctx(id->verify_ctx);
1955 hx509_certs_free(&id->certs);
1956 hx509_certs_free(&id->anchors);
1957 hx509_certs_free(&id->certpool);
1958 hx509_revoke_free(&id->revokectx);
1959 free(id);
1960 } else
1961 *ret_id = id;
1963 return ret;
1970 static void
1971 pk_copy_error(krb5_context context,
1972 hx509_context hx509ctx,
1973 int hxret,
1974 const char *fmt,
1975 ...)
1977 va_list va;
1978 char *s, *f;
1979 int ret;
1981 va_start(va, fmt);
1982 ret = vasprintf(&f, fmt, va);
1983 va_end(va);
1984 if (ret == -1 || f == NULL) {
1985 krb5_clear_error_message(context);
1986 return;
1989 s = hx509_get_error_string(hx509ctx, hxret);
1990 if (s == NULL) {
1991 krb5_clear_error_message(context);
1992 free(f);
1993 return;
1995 krb5_set_error_message(context, hxret, "%s: %s", f, s);
1996 free(s);
1997 free(f);
2000 static int
2001 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2002 const char *name, heim_integer *integer)
2004 int ret;
2005 char *p1;
2006 p1 = strsep(p, " \t");
2007 if (p1 == NULL) {
2008 krb5_set_error_message(context, EINVAL,
2009 N_("moduli file %s missing %s on line %d", ""),
2010 file, name, lineno);
2011 return EINVAL;
2013 ret = der_parse_hex_heim_integer(p1, integer);
2014 if (ret) {
2015 krb5_set_error_message(context, ret,
2016 N_("moduli file %s failed parsing %s "
2017 "on line %d", ""),
2018 file, name, lineno);
2019 return ret;
2022 return 0;
2025 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2026 _krb5_parse_moduli_line(krb5_context context,
2027 const char *file,
2028 int lineno,
2029 char *p,
2030 struct krb5_dh_moduli **m)
2032 struct krb5_dh_moduli *m1;
2033 char *p1;
2034 int ret;
2036 *m = NULL;
2038 m1 = calloc(1, sizeof(*m1));
2039 if (m1 == NULL)
2040 return krb5_enomem(context);
2042 while (isspace((unsigned char)*p))
2043 p++;
2044 if (*p == '#') {
2045 free(m1);
2046 return 0;
2048 ret = EINVAL;
2050 p1 = strsep(&p, " \t");
2051 if (p1 == NULL) {
2052 krb5_set_error_message(context, ret,
2053 N_("moduli file %s missing name on line %d", ""),
2054 file, lineno);
2055 goto out;
2057 m1->name = strdup(p1);
2058 if (m1->name == NULL) {
2059 ret = krb5_enomem(context);
2060 goto out;
2063 p1 = strsep(&p, " \t");
2064 if (p1 == NULL) {
2065 krb5_set_error_message(context, ret,
2066 N_("moduli file %s missing bits on line %d", ""),
2067 file, lineno);
2068 goto out;
2071 m1->bits = atoi(p1);
2072 if (m1->bits == 0) {
2073 krb5_set_error_message(context, ret,
2074 N_("moduli file %s have un-parsable "
2075 "bits on line %d", ""), file, lineno);
2076 goto out;
2079 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2080 if (ret)
2081 goto out;
2082 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2083 if (ret)
2084 goto out;
2085 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2086 if (ret)
2087 goto out;
2089 *m = m1;
2091 return 0;
2092 out:
2093 free(m1->name);
2094 der_free_heim_integer(&m1->p);
2095 der_free_heim_integer(&m1->g);
2096 der_free_heim_integer(&m1->q);
2097 free(m1);
2098 return ret;
2101 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2102 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2104 int i;
2105 for (i = 0; moduli[i] != NULL; i++) {
2106 free(moduli[i]->name);
2107 der_free_heim_integer(&moduli[i]->p);
2108 der_free_heim_integer(&moduli[i]->g);
2109 der_free_heim_integer(&moduli[i]->q);
2110 free(moduli[i]);
2112 free(moduli);
2115 static const char *default_moduli_RFC2412_MODP_group2 =
2116 /* name */
2117 "RFC2412-MODP-group2 "
2118 /* bits */
2119 "1024 "
2120 /* p */
2121 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2122 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2123 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2124 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2125 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2126 "FFFFFFFF" "FFFFFFFF "
2127 /* g */
2128 "02 "
2129 /* q */
2130 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2131 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2132 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2133 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2134 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2135 "FFFFFFFF" "FFFFFFFF";
2137 static const char *default_moduli_rfc3526_MODP_group14 =
2138 /* name */
2139 "rfc3526-MODP-group14 "
2140 /* bits */
2141 "1760 "
2142 /* p */
2143 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2144 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2145 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2146 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2147 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2148 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2149 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2150 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2151 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2152 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2153 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2154 /* g */
2155 "02 "
2156 /* q */
2157 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2158 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2159 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2160 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2161 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2162 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2163 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2164 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2165 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2166 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2167 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2169 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2170 _krb5_parse_moduli(krb5_context context, const char *file,
2171 struct krb5_dh_moduli ***moduli)
2173 /* name bits P G Q */
2174 krb5_error_code ret;
2175 struct krb5_dh_moduli **m = NULL, **m2;
2176 char buf[4096];
2177 FILE *f;
2178 int lineno = 0, n = 0;
2180 *moduli = NULL;
2182 m = calloc(1, sizeof(m[0]) * 3);
2183 if (m == NULL)
2184 return krb5_enomem(context);
2186 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2187 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2188 if (ret) {
2189 _krb5_free_moduli(m);
2190 return ret;
2192 n++;
2194 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2195 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2196 if (ret) {
2197 _krb5_free_moduli(m);
2198 return ret;
2200 n++;
2203 if (file == NULL)
2204 file = MODULI_FILE;
2206 #ifdef KRB5_USE_PATH_TOKENS
2208 char * exp_file;
2210 if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2211 f = fopen(exp_file, "r");
2212 krb5_xfree(exp_file);
2213 } else {
2214 f = NULL;
2217 #else
2218 f = fopen(file, "r");
2219 #endif
2221 if (f == NULL) {
2222 *moduli = m;
2223 return 0;
2225 rk_cloexec_file(f);
2227 while(fgets(buf, sizeof(buf), f) != NULL) {
2228 struct krb5_dh_moduli *element;
2230 buf[strcspn(buf, "\n")] = '\0';
2231 lineno++;
2233 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2234 if (m2 == NULL) {
2235 _krb5_free_moduli(m);
2236 return krb5_enomem(context);
2238 m = m2;
2240 m[n] = NULL;
2242 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2243 if (ret) {
2244 _krb5_free_moduli(m);
2245 return ret;
2247 if (element == NULL)
2248 continue;
2250 m[n] = element;
2251 m[n + 1] = NULL;
2252 n++;
2254 *moduli = m;
2255 return 0;
2258 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2259 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2260 heim_integer *p, heim_integer *g, heim_integer *q,
2261 struct krb5_dh_moduli **moduli,
2262 char **name)
2264 int i;
2266 if (name)
2267 *name = NULL;
2269 for (i = 0; moduli[i] != NULL; i++) {
2270 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2271 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2272 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2274 if (bits && bits > moduli[i]->bits) {
2275 krb5_set_error_message(context,
2276 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2277 N_("PKINIT: DH group parameter %s "
2278 "no accepted, not enough bits "
2279 "generated", ""),
2280 moduli[i]->name);
2281 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2283 if (name)
2284 *name = strdup(moduli[i]->name);
2285 return 0;
2288 krb5_set_error_message(context,
2289 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2290 N_("PKINIT: DH group parameter no ok", ""));
2291 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2293 #endif /* PKINIT */
2295 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2296 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2298 #ifdef PKINIT
2299 krb5_pk_init_ctx ctx;
2301 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2302 return;
2303 ctx = opt->opt_private->pk_init_ctx;
2304 switch (ctx->keyex) {
2305 case USE_DH:
2306 if (ctx->u.dh)
2307 DH_free(ctx->u.dh);
2308 break;
2309 case USE_RSA:
2310 break;
2311 case USE_ECDH:
2312 #ifdef HAVE_OPENSSL
2313 if (ctx->u.eckey)
2314 EC_KEY_free(ctx->u.eckey);
2315 #endif
2316 break;
2318 if (ctx->id) {
2319 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2320 hx509_certs_free(&ctx->id->certs);
2321 hx509_cert_free(ctx->id->cert);
2322 hx509_certs_free(&ctx->id->anchors);
2323 hx509_certs_free(&ctx->id->certpool);
2325 if (ctx->clientDHNonce) {
2326 krb5_free_data(NULL, ctx->clientDHNonce);
2327 ctx->clientDHNonce = NULL;
2329 if (ctx->m)
2330 _krb5_free_moduli(ctx->m);
2331 free(ctx->id);
2332 ctx->id = NULL;
2334 free(opt->opt_private->pk_init_ctx);
2335 opt->opt_private->pk_init_ctx = NULL;
2336 #endif
2339 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2340 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2341 krb5_get_init_creds_opt *opt,
2342 krb5_principal principal,
2343 const char *user_id,
2344 const char *x509_anchors,
2345 char * const * pool,
2346 char * const * pki_revoke,
2347 int flags,
2348 krb5_prompter_fct prompter,
2349 void *prompter_data,
2350 char *password)
2352 #ifdef PKINIT
2353 krb5_error_code ret;
2354 char *anchors = NULL;
2356 if (opt->opt_private == NULL) {
2357 krb5_set_error_message(context, EINVAL,
2358 N_("PKINIT: on non extendable opt", ""));
2359 return EINVAL;
2362 opt->opt_private->pk_init_ctx =
2363 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2364 if (opt->opt_private->pk_init_ctx == NULL)
2365 return krb5_enomem(context);
2366 opt->opt_private->pk_init_ctx->require_binding = 0;
2367 opt->opt_private->pk_init_ctx->require_eku = 1;
2368 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2369 opt->opt_private->pk_init_ctx->peer = NULL;
2371 /* XXX implement krb5_appdefault_strings */
2372 if (pool == NULL)
2373 pool = krb5_config_get_strings(context, NULL,
2374 "appdefaults",
2375 "pkinit_pool",
2376 NULL);
2378 if (pki_revoke == NULL)
2379 pki_revoke = krb5_config_get_strings(context, NULL,
2380 "appdefaults",
2381 "pkinit_revoke",
2382 NULL);
2384 if (x509_anchors == NULL) {
2385 krb5_appdefault_string(context, "kinit",
2386 krb5_principal_get_realm(context, principal),
2387 "pkinit_anchors", NULL, &anchors);
2388 x509_anchors = anchors;
2391 if (flags & 4)
2392 opt->opt_private->pk_init_ctx->anonymous = 1;
2394 ret = _krb5_pk_load_id(context,
2395 &opt->opt_private->pk_init_ctx->id,
2396 user_id,
2397 x509_anchors,
2398 pool,
2399 pki_revoke,
2400 prompter,
2401 prompter_data,
2402 password);
2403 if (ret) {
2404 free(opt->opt_private->pk_init_ctx);
2405 opt->opt_private->pk_init_ctx = NULL;
2406 return ret;
2409 if (opt->opt_private->pk_init_ctx->id->certs) {
2410 _krb5_pk_set_user_id(context,
2411 principal,
2412 opt->opt_private->pk_init_ctx,
2413 opt->opt_private->pk_init_ctx->id->certs);
2414 } else
2415 opt->opt_private->pk_init_ctx->id->cert = NULL;
2417 if ((flags & 2) == 0) {
2418 hx509_context hx509ctx = context->hx509ctx;
2419 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2421 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2424 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2426 if (cert) {
2427 AlgorithmIdentifier alg;
2429 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2430 if (ret == 0) {
2431 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2432 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2433 free_AlgorithmIdentifier(&alg);
2437 } else {
2438 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2440 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2441 krb5_set_error_message(context, EINVAL,
2442 N_("No anonymous pkinit support in RSA mode", ""));
2443 return EINVAL;
2447 return 0;
2448 #else
2449 krb5_set_error_message(context, EINVAL,
2450 N_("no support for PKINIT compiled in", ""));
2451 return EINVAL;
2452 #endif
2455 krb5_error_code KRB5_LIB_FUNCTION
2456 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2457 krb5_get_init_creds_opt *opt,
2458 struct hx509_certs_data *certs)
2460 #ifdef PKINIT
2461 if (opt->opt_private == NULL) {
2462 krb5_set_error_message(context, EINVAL,
2463 N_("PKINIT: on non extendable opt", ""));
2464 return EINVAL;
2466 if (opt->opt_private->pk_init_ctx == NULL) {
2467 krb5_set_error_message(context, EINVAL,
2468 N_("PKINIT: on pkinit context", ""));
2469 return EINVAL;
2472 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2474 return 0;
2475 #else
2476 krb5_set_error_message(context, EINVAL,
2477 N_("no support for PKINIT compiled in", ""));
2478 return EINVAL;
2479 #endif
2482 #ifdef PKINIT
2484 static int
2485 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2487 hx509_octet_string_list list;
2488 int ret;
2490 *upn = NULL;
2492 ret = hx509_cert_find_subjectAltName_otherName(context,
2493 cert,
2494 &asn1_oid_id_pkinit_ms_san,
2495 &list);
2496 if (ret)
2497 return 0;
2499 if (list.len > 0 && list.val[0].length > 0)
2500 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2501 upn, NULL);
2502 else
2503 ret = 1;
2504 hx509_free_octet_string_list(&list);
2506 return ret;
2509 static int
2510 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2512 char *upn;
2513 int ret;
2515 ret = get_ms_san(context, cert, &upn);
2516 if (ret == 0)
2517 free(upn);
2518 return ret;
2523 #endif
2526 * Private since it need to be redesigned using krb5_get_init_creds()
2529 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2530 krb5_pk_enterprise_cert(krb5_context context,
2531 const char *user_id,
2532 krb5_const_realm realm,
2533 krb5_principal *principal,
2534 struct hx509_certs_data **res)
2536 #ifdef PKINIT
2537 krb5_error_code ret;
2538 hx509_certs certs, result;
2539 hx509_cert cert = NULL;
2540 hx509_query *q;
2541 char *name;
2543 *principal = NULL;
2544 if (res)
2545 *res = NULL;
2547 if (user_id == NULL) {
2548 krb5_set_error_message(context, ENOENT, "no user id");
2549 return ENOENT;
2552 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2553 if (ret) {
2554 pk_copy_error(context, context->hx509ctx, ret,
2555 "Failed to init cert certs");
2556 goto out;
2559 ret = hx509_query_alloc(context->hx509ctx, &q);
2560 if (ret) {
2561 krb5_set_error_message(context, ret, "out of memory");
2562 hx509_certs_free(&certs);
2563 goto out;
2566 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2567 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2568 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2569 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2571 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2572 hx509_query_free(context->hx509ctx, q);
2573 hx509_certs_free(&certs);
2574 if (ret) {
2575 pk_copy_error(context, context->hx509ctx, ret,
2576 "Failed to find PKINIT certificate");
2577 return ret;
2580 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2581 hx509_certs_free(&result);
2582 if (ret) {
2583 pk_copy_error(context, context->hx509ctx, ret,
2584 "Failed to get one cert");
2585 goto out;
2588 ret = get_ms_san(context->hx509ctx, cert, &name);
2589 if (ret) {
2590 pk_copy_error(context, context->hx509ctx, ret,
2591 "Failed to get MS SAN");
2592 goto out;
2595 ret = krb5_make_principal(context, principal, realm, name, NULL);
2596 free(name);
2597 if (ret)
2598 goto out;
2600 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2602 if (res) {
2603 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2604 if (ret)
2605 goto out;
2607 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2608 if (ret) {
2609 hx509_certs_free(res);
2610 goto out;
2614 out:
2615 hx509_cert_free(cert);
2617 return ret;
2618 #else
2619 krb5_set_error_message(context, EINVAL,
2620 N_("no support for PKINIT compiled in", ""));
2621 return EINVAL;
2622 #endif