Quiet a warning in test_plugin.c
[heimdal.git] / lib / krb5 / pkinit.c
blob90170849988646af9b4b397821392604716f9ca4
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 ret = BN_to_integer(context, dh->q, &dp.q);
496 if (ret) {
497 free_DomainParameters(&dp);
498 return ret;
500 dp.j = NULL;
501 dp.validationParms = NULL;
503 a->clientPublicValue->algorithm.parameters =
504 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
505 if (a->clientPublicValue->algorithm.parameters == NULL) {
506 free_DomainParameters(&dp);
507 return ret;
510 ASN1_MALLOC_ENCODE(DomainParameters,
511 a->clientPublicValue->algorithm.parameters->data,
512 a->clientPublicValue->algorithm.parameters->length,
513 &dp, &size, ret);
514 free_DomainParameters(&dp);
515 if (ret)
516 return ret;
517 if (size != a->clientPublicValue->algorithm.parameters->length)
518 krb5_abortx(context, "Internal ASN1 encoder error");
520 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
521 if (ret)
522 return ret;
524 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
525 &dh_pub_key, &size, ret);
526 der_free_heim_integer(&dh_pub_key);
527 if (ret)
528 return ret;
529 if (size != dhbuf.length)
530 krb5_abortx(context, "asn1 internal error");
531 } else if (ctx->keyex == USE_ECDH) {
532 #ifdef HAVE_OPENSSL
533 ECParameters ecp;
534 unsigned char *p;
535 int xlen;
537 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
539 ecp.element = choice_ECParameters_namedCurve;
540 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
541 &ecp.u.namedCurve);
542 if (ret)
543 return ret;
545 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
546 if (a->clientPublicValue->algorithm.parameters == NULL) {
547 free_ECParameters(&ecp);
548 return ENOMEM;
550 ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
551 free_ECParameters(&ecp);
552 if (ret)
553 return ret;
554 if ((int)size != xlen)
555 krb5_abortx(context, "asn1 internal error");
557 a->clientPublicValue->algorithm.parameters->data = p;
558 a->clientPublicValue->algorithm.parameters->length = size;
560 /* copy in public key */
562 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
563 &a->clientPublicValue->algorithm.algorithm);
564 if (ret)
565 return ret;
567 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
568 if (ctx->u.eckey == NULL)
569 return ENOMEM;
571 ret = EC_KEY_generate_key(ctx->u.eckey);
572 if (ret != 1)
573 return EINVAL;
575 /* encode onto dhkey */
577 xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
578 if (xlen <= 0)
579 abort();
581 dhbuf.data = malloc(xlen);
582 if (dhbuf.data == NULL)
583 abort();
584 dhbuf.length = xlen;
585 p = dhbuf.data;
587 xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
588 if (xlen <= 0)
589 abort();
591 /* XXX verify that this is right with RFC3279 */
592 #else
593 return EINVAL;
594 #endif
595 } else
596 krb5_abortx(context, "internal error");
597 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
598 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
602 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
603 if (a->supportedCMSTypes == NULL)
604 return ENOMEM;
606 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
607 ctx->id->cert,
608 &a->supportedCMSTypes->val,
609 &a->supportedCMSTypes->len);
610 if (ret)
611 return ret;
614 return ret;
617 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
618 _krb5_pk_mk_ContentInfo(krb5_context context,
619 const krb5_data *buf,
620 const heim_oid *oid,
621 struct ContentInfo *content_info)
623 krb5_error_code ret;
625 ret = der_copy_oid(oid, &content_info->contentType);
626 if (ret)
627 return ret;
628 ALLOC(content_info->content, 1);
629 if (content_info->content == NULL)
630 return ENOMEM;
631 content_info->content->data = malloc(buf->length);
632 if (content_info->content->data == NULL)
633 return ENOMEM;
634 memcpy(content_info->content->data, buf->data, buf->length);
635 content_info->content->length = buf->length;
636 return 0;
639 static krb5_error_code
640 pk_mk_padata(krb5_context context,
641 krb5_pk_init_ctx ctx,
642 const KDC_REQ_BODY *req_body,
643 unsigned nonce,
644 METHOD_DATA *md)
646 struct ContentInfo content_info;
647 krb5_error_code ret;
648 const heim_oid *oid = NULL;
649 size_t size = 0;
650 krb5_data buf, sd_buf;
651 int pa_type = -1;
653 krb5_data_zero(&buf);
654 krb5_data_zero(&sd_buf);
655 memset(&content_info, 0, sizeof(content_info));
657 if (ctx->type == PKINIT_WIN2K) {
658 AuthPack_Win2k ap;
659 krb5_timestamp sec;
660 int32_t usec;
662 memset(&ap, 0, sizeof(ap));
664 /* fill in PKAuthenticator */
665 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
666 if (ret) {
667 free_AuthPack_Win2k(&ap);
668 krb5_clear_error_message(context);
669 goto out;
671 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
672 if (ret) {
673 free_AuthPack_Win2k(&ap);
674 krb5_clear_error_message(context);
675 goto out;
678 krb5_us_timeofday(context, &sec, &usec);
679 ap.pkAuthenticator.ctime = sec;
680 ap.pkAuthenticator.cusec = usec;
681 ap.pkAuthenticator.nonce = nonce;
683 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
684 &ap, &size, ret);
685 free_AuthPack_Win2k(&ap);
686 if (ret) {
687 krb5_set_error_message(context, ret,
688 N_("Failed encoding AuthPackWin: %d", ""),
689 (int)ret);
690 goto out;
692 if (buf.length != size)
693 krb5_abortx(context, "internal ASN1 encoder error");
695 oid = &asn1_oid_id_pkcs7_data;
696 } else if (ctx->type == PKINIT_27) {
697 AuthPack ap;
699 memset(&ap, 0, sizeof(ap));
701 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
702 if (ret) {
703 free_AuthPack(&ap);
704 goto out;
707 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
708 free_AuthPack(&ap);
709 if (ret) {
710 krb5_set_error_message(context, ret,
711 N_("Failed encoding AuthPack: %d", ""),
712 (int)ret);
713 goto out;
715 if (buf.length != size)
716 krb5_abortx(context, "internal ASN1 encoder error");
718 oid = &asn1_oid_id_pkauthdata;
719 } else
720 krb5_abortx(context, "internal pkinit error");
722 ret = create_signature(context, oid, &buf, ctx->id,
723 ctx->peer, &sd_buf);
724 krb5_data_free(&buf);
725 if (ret)
726 goto out;
728 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
729 krb5_data_free(&sd_buf);
730 if (ret) {
731 krb5_set_error_message(context, ret,
732 N_("ContentInfo wrapping of signedData failed",""));
733 goto out;
736 if (ctx->type == PKINIT_WIN2K) {
737 PA_PK_AS_REQ_Win2k winreq;
739 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
741 memset(&winreq, 0, sizeof(winreq));
743 winreq.signed_auth_pack = buf;
745 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
746 &winreq, &size, ret);
747 free_PA_PK_AS_REQ_Win2k(&winreq);
749 } else if (ctx->type == PKINIT_27) {
750 PA_PK_AS_REQ req;
752 pa_type = KRB5_PADATA_PK_AS_REQ;
754 memset(&req, 0, sizeof(req));
755 req.signedAuthPack = buf;
757 if (ctx->trustedCertifiers) {
759 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
760 if (req.trustedCertifiers == NULL) {
761 ret = krb5_enomem(context);
762 free_PA_PK_AS_REQ(&req);
763 goto out;
765 ret = build_edi(context, context->hx509ctx,
766 ctx->id->anchors, req.trustedCertifiers);
767 if (ret) {
768 krb5_set_error_message(context, ret,
769 N_("pk-init: failed to build "
770 "trustedCertifiers", ""));
771 free_PA_PK_AS_REQ(&req);
772 goto out;
775 req.kdcPkId = NULL;
777 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
778 &req, &size, ret);
780 free_PA_PK_AS_REQ(&req);
782 } else
783 krb5_abortx(context, "internal pkinit error");
784 if (ret) {
785 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
786 goto out;
788 if (buf.length != size)
789 krb5_abortx(context, "Internal ASN1 encoder error");
791 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
792 if (ret)
793 free(buf.data);
795 if (ret == 0)
796 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
798 out:
799 free_ContentInfo(&content_info);
801 return ret;
805 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
806 _krb5_pk_mk_padata(krb5_context context,
807 void *c,
808 int ic_flags,
809 int win2k,
810 const KDC_REQ_BODY *req_body,
811 unsigned nonce,
812 METHOD_DATA *md)
814 krb5_pk_init_ctx ctx = c;
815 int win2k_compat;
817 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
818 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
819 N_("PKINIT: No user certificate given", ""));
820 return HEIM_PKINIT_NO_PRIVATE_KEY;
823 win2k_compat = krb5_config_get_bool_default(context, NULL,
824 win2k,
825 "realms",
826 req_body->realm,
827 "pkinit_win2k",
828 NULL);
830 if (win2k_compat) {
831 ctx->require_binding =
832 krb5_config_get_bool_default(context, NULL,
833 TRUE,
834 "realms",
835 req_body->realm,
836 "pkinit_win2k_require_binding",
837 NULL);
838 ctx->type = PKINIT_WIN2K;
839 } else
840 ctx->type = PKINIT_27;
842 ctx->require_eku =
843 krb5_config_get_bool_default(context, NULL,
844 TRUE,
845 "realms",
846 req_body->realm,
847 "pkinit_require_eku",
848 NULL);
849 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
850 ctx->require_eku = 0;
851 if (ctx->id->flags & PKINIT_BTMM)
852 ctx->require_eku = 0;
854 ctx->require_krbtgt_otherName =
855 krb5_config_get_bool_default(context, NULL,
856 TRUE,
857 "realms",
858 req_body->realm,
859 "pkinit_require_krbtgt_otherName",
860 NULL);
862 ctx->require_hostname_match =
863 krb5_config_get_bool_default(context, NULL,
864 FALSE,
865 "realms",
866 req_body->realm,
867 "pkinit_require_hostname_match",
868 NULL);
870 ctx->trustedCertifiers =
871 krb5_config_get_bool_default(context, NULL,
872 TRUE,
873 "realms",
874 req_body->realm,
875 "pkinit_trustedCertifiers",
876 NULL);
878 return pk_mk_padata(context, ctx, req_body, nonce, md);
881 static krb5_error_code
882 pk_verify_sign(krb5_context context,
883 const void *data,
884 size_t length,
885 struct krb5_pk_identity *id,
886 heim_oid *contentType,
887 krb5_data *content,
888 struct krb5_pk_cert **signer)
890 hx509_certs signer_certs;
891 int ret, flags = 0;
893 /* BTMM is broken in Leo and SnowLeo */
894 if (id->flags & PKINIT_BTMM) {
895 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
896 flags |= HX509_CMS_VS_NO_KU_CHECK;
897 flags |= HX509_CMS_VS_NO_VALIDATE;
900 *signer = NULL;
902 ret = hx509_cms_verify_signed(context->hx509ctx,
903 id->verify_ctx,
904 flags,
905 data,
906 length,
907 NULL,
908 id->certpool,
909 contentType,
910 content,
911 &signer_certs);
912 if (ret) {
913 pk_copy_error(context, context->hx509ctx, ret,
914 "CMS verify signed failed");
915 return ret;
918 *signer = calloc(1, sizeof(**signer));
919 if (*signer == NULL) {
920 krb5_clear_error_message(context);
921 ret = ENOMEM;
922 goto out;
925 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
926 if (ret) {
927 pk_copy_error(context, context->hx509ctx, ret,
928 "Failed to get on of the signer certs");
929 goto out;
932 out:
933 hx509_certs_free(&signer_certs);
934 if (ret) {
935 if (*signer) {
936 hx509_cert_free((*signer)->cert);
937 free(*signer);
938 *signer = NULL;
942 return ret;
945 static krb5_error_code
946 get_reply_key_win(krb5_context context,
947 const krb5_data *content,
948 unsigned nonce,
949 krb5_keyblock **key)
951 ReplyKeyPack_Win2k key_pack;
952 krb5_error_code ret;
953 size_t size;
955 ret = decode_ReplyKeyPack_Win2k(content->data,
956 content->length,
957 &key_pack,
958 &size);
959 if (ret) {
960 krb5_set_error_message(context, ret,
961 N_("PKINIT decoding reply key failed", ""));
962 free_ReplyKeyPack_Win2k(&key_pack);
963 return ret;
966 if ((unsigned)key_pack.nonce != nonce) {
967 krb5_set_error_message(context, ret,
968 N_("PKINIT enckey nonce is wrong", ""));
969 free_ReplyKeyPack_Win2k(&key_pack);
970 return KRB5KRB_AP_ERR_MODIFIED;
973 *key = malloc (sizeof (**key));
974 if (*key == NULL) {
975 free_ReplyKeyPack_Win2k(&key_pack);
976 return krb5_enomem(context);
979 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
980 free_ReplyKeyPack_Win2k(&key_pack);
981 if (ret) {
982 krb5_set_error_message(context, ret,
983 N_("PKINIT failed copying reply key", ""));
984 free(*key);
985 *key = NULL;
988 return ret;
991 static krb5_error_code
992 get_reply_key(krb5_context context,
993 const krb5_data *content,
994 const krb5_data *req_buffer,
995 krb5_keyblock **key)
997 ReplyKeyPack key_pack;
998 krb5_error_code ret;
999 size_t size;
1001 ret = decode_ReplyKeyPack(content->data,
1002 content->length,
1003 &key_pack,
1004 &size);
1005 if (ret) {
1006 krb5_set_error_message(context, ret,
1007 N_("PKINIT decoding reply key failed", ""));
1008 free_ReplyKeyPack(&key_pack);
1009 return ret;
1013 krb5_crypto crypto;
1016 * XXX Verify kp.replyKey is a allowed enctype in the
1017 * configuration file
1020 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1021 if (ret) {
1022 free_ReplyKeyPack(&key_pack);
1023 return ret;
1026 ret = krb5_verify_checksum(context, crypto, 6,
1027 req_buffer->data, req_buffer->length,
1028 &key_pack.asChecksum);
1029 krb5_crypto_destroy(context, crypto);
1030 if (ret) {
1031 free_ReplyKeyPack(&key_pack);
1032 return ret;
1036 *key = malloc (sizeof (**key));
1037 if (*key == NULL) {
1038 free_ReplyKeyPack(&key_pack);
1039 return krb5_enomem(context);
1042 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1043 free_ReplyKeyPack(&key_pack);
1044 if (ret) {
1045 krb5_set_error_message(context, ret,
1046 N_("PKINIT failed copying reply key", ""));
1047 free(*key);
1048 *key = NULL;
1051 return ret;
1055 static krb5_error_code
1056 pk_verify_host(krb5_context context,
1057 const char *realm,
1058 const krb5_krbhst_info *hi,
1059 struct krb5_pk_init_ctx_data *ctx,
1060 struct krb5_pk_cert *host)
1062 krb5_error_code ret = 0;
1064 if (ctx->require_eku) {
1065 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1066 &asn1_oid_id_pkkdcekuoid, 0);
1067 if (ret) {
1068 krb5_set_error_message(context, ret,
1069 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1070 return ret;
1073 if (ctx->require_krbtgt_otherName) {
1074 hx509_octet_string_list list;
1075 size_t i;
1076 int matched = 0;
1078 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1079 host->cert,
1080 &asn1_oid_id_pkinit_san,
1081 &list);
1082 if (ret) {
1083 krb5_set_error_message(context, ret,
1084 N_("Failed to find the PK-INIT "
1085 "subjectAltName in the KDC "
1086 "certificate", ""));
1088 return ret;
1092 * subjectAltNames are multi-valued, and a single KDC may serve
1093 * multiple realms. The SAN validation here must accept
1094 * the KDC's cert if *any* of the SANs match the expected KDC.
1095 * It is OK for *some* of the SANs to not match, provided at least
1096 * one does.
1098 for (i = 0; matched == 0 && i < list.len; i++) {
1099 KRB5PrincipalName r;
1101 ret = decode_KRB5PrincipalName(list.val[i].data,
1102 list.val[i].length,
1104 NULL);
1105 if (ret) {
1106 krb5_set_error_message(context, ret,
1107 N_("Failed to decode the PK-INIT "
1108 "subjectAltName in the "
1109 "KDC certificate", ""));
1111 break;
1114 if (r.principalName.name_string.len == 2 &&
1115 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1116 && strcmp(r.principalName.name_string.val[1], realm) == 0
1117 && strcmp(r.realm, realm) == 0)
1118 matched = 1;
1120 free_KRB5PrincipalName(&r);
1122 hx509_free_octet_string_list(&list);
1123 if (matched == 0) {
1124 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1125 /* XXX: Lost in translation... */
1126 krb5_set_error_message(context, ret,
1127 N_("KDC have wrong realm name in "
1128 "the certificate", ""));
1131 if (ret)
1132 return ret;
1134 if (hi) {
1135 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1136 ctx->require_hostname_match,
1137 HX509_HN_HOSTNAME,
1138 hi->hostname,
1139 hi->ai->ai_addr, hi->ai->ai_addrlen);
1141 if (ret)
1142 krb5_set_error_message(context, ret,
1143 N_("Address mismatch in "
1144 "the KDC certificate", ""));
1146 return ret;
1149 static krb5_error_code
1150 pk_rd_pa_reply_enckey(krb5_context context,
1151 int type,
1152 const heim_octet_string *indata,
1153 const heim_oid *dataType,
1154 const char *realm,
1155 krb5_pk_init_ctx ctx,
1156 krb5_enctype etype,
1157 const krb5_krbhst_info *hi,
1158 unsigned nonce,
1159 const krb5_data *req_buffer,
1160 PA_DATA *pa,
1161 krb5_keyblock **key)
1163 krb5_error_code ret;
1164 struct krb5_pk_cert *host = NULL;
1165 krb5_data content;
1166 heim_oid contentType = { 0, NULL };
1167 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1169 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1170 krb5_set_error_message(context, EINVAL,
1171 N_("PKINIT: Invalid content type", ""));
1172 return EINVAL;
1175 if (ctx->type == PKINIT_WIN2K)
1176 flags |= HX509_CMS_UE_ALLOW_WEAK;
1178 ret = hx509_cms_unenvelope(context->hx509ctx,
1179 ctx->id->certs,
1180 flags,
1181 indata->data,
1182 indata->length,
1183 NULL,
1185 &contentType,
1186 &content);
1187 if (ret) {
1188 pk_copy_error(context, context->hx509ctx, ret,
1189 "Failed to unenvelope CMS data in PK-INIT reply");
1190 return ret;
1192 der_free_oid(&contentType);
1194 /* win2k uses ContentInfo */
1195 if (type == PKINIT_WIN2K) {
1196 heim_oid type2;
1197 heim_octet_string out;
1199 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1200 if (ret) {
1201 /* windows LH with interesting CMS packets */
1202 size_t ph = 1 + der_length_len(content.length);
1203 unsigned char *ptr = malloc(content.length + ph);
1204 size_t l;
1206 memcpy(ptr + ph, content.data, content.length);
1208 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1209 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1210 if (ret)
1211 return ret;
1212 free(content.data);
1213 content.data = ptr;
1214 content.length += ph;
1216 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1217 if (ret)
1218 goto out;
1220 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1221 ret = EINVAL; /* XXX */
1222 krb5_set_error_message(context, ret,
1223 N_("PKINIT: Invalid content type", ""));
1224 der_free_oid(&type2);
1225 der_free_octet_string(&out);
1226 goto out;
1228 der_free_oid(&type2);
1229 krb5_data_free(&content);
1230 ret = krb5_data_copy(&content, out.data, out.length);
1231 der_free_octet_string(&out);
1232 if (ret) {
1233 krb5_set_error_message(context, ret,
1234 N_("malloc: out of memory", ""));
1235 goto out;
1239 ret = pk_verify_sign(context,
1240 content.data,
1241 content.length,
1242 ctx->id,
1243 &contentType,
1244 &content,
1245 &host);
1246 if (ret)
1247 goto out;
1249 /* make sure that it is the kdc's certificate */
1250 ret = pk_verify_host(context, realm, hi, ctx, host);
1251 if (ret) {
1252 goto out;
1255 #if 0
1256 if (type == PKINIT_WIN2K) {
1257 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1258 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1259 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1260 goto out;
1262 } else {
1263 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1264 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1265 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1266 goto out;
1269 #endif
1271 switch(type) {
1272 case PKINIT_WIN2K:
1273 ret = get_reply_key(context, &content, req_buffer, key);
1274 if (ret != 0 && ctx->require_binding == 0)
1275 ret = get_reply_key_win(context, &content, nonce, key);
1276 break;
1277 case PKINIT_27:
1278 ret = get_reply_key(context, &content, req_buffer, key);
1279 break;
1281 if (ret)
1282 goto out;
1284 /* XXX compare given etype with key->etype */
1286 out:
1287 if (host)
1288 _krb5_pk_cert_free(host);
1289 der_free_oid(&contentType);
1290 krb5_data_free(&content);
1292 return ret;
1295 static krb5_error_code
1296 pk_rd_pa_reply_dh(krb5_context context,
1297 const heim_octet_string *indata,
1298 const heim_oid *dataType,
1299 const char *realm,
1300 krb5_pk_init_ctx ctx,
1301 krb5_enctype etype,
1302 const krb5_krbhst_info *hi,
1303 const DHNonce *c_n,
1304 const DHNonce *k_n,
1305 unsigned nonce,
1306 PA_DATA *pa,
1307 krb5_keyblock **key)
1309 const unsigned char *p;
1310 unsigned char *dh_gen_key = NULL;
1311 struct krb5_pk_cert *host = NULL;
1312 BIGNUM *kdc_dh_pubkey = NULL;
1313 KDCDHKeyInfo kdc_dh_info;
1314 heim_oid contentType = { 0, NULL };
1315 krb5_data content;
1316 krb5_error_code ret;
1317 int dh_gen_keylen = 0;
1318 size_t size;
1320 krb5_data_zero(&content);
1321 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1323 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1324 krb5_set_error_message(context, EINVAL,
1325 N_("PKINIT: Invalid content type", ""));
1326 return EINVAL;
1329 ret = pk_verify_sign(context,
1330 indata->data,
1331 indata->length,
1332 ctx->id,
1333 &contentType,
1334 &content,
1335 &host);
1336 if (ret)
1337 goto out;
1339 /* make sure that it is the kdc's certificate */
1340 ret = pk_verify_host(context, realm, hi, ctx, host);
1341 if (ret)
1342 goto out;
1344 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1345 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1346 krb5_set_error_message(context, ret,
1347 N_("pkinit - dh reply contains wrong oid", ""));
1348 goto out;
1351 ret = decode_KDCDHKeyInfo(content.data,
1352 content.length,
1353 &kdc_dh_info,
1354 &size);
1356 if (ret) {
1357 krb5_set_error_message(context, ret,
1358 N_("pkinit - failed to decode "
1359 "KDC DH Key Info", ""));
1360 goto out;
1363 if (kdc_dh_info.nonce != nonce) {
1364 ret = KRB5KRB_AP_ERR_MODIFIED;
1365 krb5_set_error_message(context, ret,
1366 N_("PKINIT: DH nonce is wrong", ""));
1367 goto out;
1370 if (kdc_dh_info.dhKeyExpiration) {
1371 if (k_n == NULL) {
1372 ret = KRB5KRB_ERR_GENERIC;
1373 krb5_set_error_message(context, ret,
1374 N_("pkinit; got key expiration "
1375 "without server nonce", ""));
1376 goto out;
1378 if (c_n == NULL) {
1379 ret = KRB5KRB_ERR_GENERIC;
1380 krb5_set_error_message(context, ret,
1381 N_("pkinit; got DH reuse but no "
1382 "client nonce", ""));
1383 goto out;
1385 } else {
1386 if (k_n) {
1387 ret = KRB5KRB_ERR_GENERIC;
1388 krb5_set_error_message(context, ret,
1389 N_("pkinit: got server nonce "
1390 "without key expiration", ""));
1391 goto out;
1393 c_n = NULL;
1397 p = kdc_dh_info.subjectPublicKey.data;
1398 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1400 if (ctx->keyex == USE_DH) {
1401 DHPublicKey k;
1402 ret = decode_DHPublicKey(p, size, &k, NULL);
1403 if (ret) {
1404 krb5_set_error_message(context, ret,
1405 N_("pkinit: can't decode "
1406 "without key expiration", ""));
1407 goto out;
1410 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1411 free_DHPublicKey(&k);
1412 if (kdc_dh_pubkey == NULL) {
1413 ret = ENOMEM;
1414 goto out;
1418 size = DH_size(ctx->u.dh);
1420 dh_gen_key = malloc(size);
1421 if (dh_gen_key == NULL) {
1422 ret = krb5_enomem(context);
1423 goto out;
1426 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1427 if (dh_gen_keylen == -1) {
1428 ret = KRB5KRB_ERR_GENERIC;
1429 dh_gen_keylen = 0;
1430 krb5_set_error_message(context, ret,
1431 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1432 goto out;
1434 if (dh_gen_keylen < (int)size) {
1435 size -= dh_gen_keylen;
1436 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1437 memset(dh_gen_key, 0, size);
1440 } else {
1441 #ifdef HAVE_OPENSSL
1442 const EC_GROUP *group;
1443 EC_KEY *public = NULL;
1445 group = EC_KEY_get0_group(ctx->u.eckey);
1447 public = EC_KEY_new();
1448 if (public == NULL) {
1449 ret = ENOMEM;
1450 goto out;
1452 if (EC_KEY_set_group(public, group) != 1) {
1453 EC_KEY_free(public);
1454 ret = ENOMEM;
1455 goto out;
1458 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1459 EC_KEY_free(public);
1460 ret = KRB5KRB_ERR_GENERIC;
1461 krb5_set_error_message(context, ret,
1462 N_("PKINIT: Can't parse ECDH public key", ""));
1463 goto out;
1466 size = (EC_GROUP_get_degree(group) + 7) / 8;
1467 dh_gen_key = malloc(size);
1468 if (dh_gen_key == NULL) {
1469 EC_KEY_free(public);
1470 ret = krb5_enomem(context);
1471 goto out;
1473 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1474 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1475 EC_KEY_free(public);
1476 if (dh_gen_keylen == -1) {
1477 ret = KRB5KRB_ERR_GENERIC;
1478 dh_gen_keylen = 0;
1479 krb5_set_error_message(context, ret,
1480 N_("PKINIT: Can't compute ECDH public key", ""));
1481 goto out;
1483 #else
1484 ret = EINVAL;
1485 #endif
1488 if (dh_gen_keylen <= 0) {
1489 ret = EINVAL;
1490 krb5_set_error_message(context, ret,
1491 N_("PKINIT: resulting DH key <= 0", ""));
1492 dh_gen_keylen = 0;
1493 goto out;
1496 *key = malloc (sizeof (**key));
1497 if (*key == NULL) {
1498 ret = krb5_enomem(context);
1499 goto out;
1502 ret = _krb5_pk_octetstring2key(context,
1503 etype,
1504 dh_gen_key, dh_gen_keylen,
1505 c_n, k_n,
1506 *key);
1507 if (ret) {
1508 krb5_set_error_message(context, ret,
1509 N_("PKINIT: can't create key from DH key", ""));
1510 free(*key);
1511 *key = NULL;
1512 goto out;
1515 out:
1516 if (kdc_dh_pubkey)
1517 BN_free(kdc_dh_pubkey);
1518 if (dh_gen_key) {
1519 memset(dh_gen_key, 0, dh_gen_keylen);
1520 free(dh_gen_key);
1522 if (host)
1523 _krb5_pk_cert_free(host);
1524 if (content.data)
1525 krb5_data_free(&content);
1526 der_free_oid(&contentType);
1527 free_KDCDHKeyInfo(&kdc_dh_info);
1529 return ret;
1532 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1533 _krb5_pk_rd_pa_reply(krb5_context context,
1534 const char *realm,
1535 void *c,
1536 krb5_enctype etype,
1537 const krb5_krbhst_info *hi,
1538 unsigned nonce,
1539 const krb5_data *req_buffer,
1540 PA_DATA *pa,
1541 krb5_keyblock **key)
1543 krb5_pk_init_ctx ctx = c;
1544 krb5_error_code ret;
1545 size_t size;
1547 /* Check for IETF PK-INIT first */
1548 if (ctx->type == PKINIT_27) {
1549 PA_PK_AS_REP rep;
1550 heim_octet_string os, data;
1551 heim_oid oid;
1553 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1554 krb5_set_error_message(context, EINVAL,
1555 N_("PKINIT: wrong padata recv", ""));
1556 return EINVAL;
1559 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1560 pa->padata_value.length,
1561 &rep,
1562 &size);
1563 if (ret) {
1564 krb5_set_error_message(context, ret,
1565 N_("Failed to decode pkinit AS rep", ""));
1566 return ret;
1569 switch (rep.element) {
1570 case choice_PA_PK_AS_REP_dhInfo:
1571 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1572 os = rep.u.dhInfo.dhSignedData;
1573 break;
1574 case choice_PA_PK_AS_REP_encKeyPack:
1575 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1576 os = rep.u.encKeyPack;
1577 break;
1578 default: {
1579 PA_PK_AS_REP_BTMM btmm;
1580 free_PA_PK_AS_REP(&rep);
1581 memset(&rep, 0, sizeof(rep));
1583 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1585 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1586 pa->padata_value.length,
1587 &btmm,
1588 &size);
1589 if (ret) {
1590 krb5_set_error_message(context, EINVAL,
1591 N_("PKINIT: -27 reply "
1592 "invalid content type", ""));
1593 return EINVAL;
1596 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1597 free_PA_PK_AS_REP_BTMM(&btmm);
1598 ret = EINVAL;
1599 krb5_set_error_message(context, ret,
1600 N_("DH mode not supported for BTMM mode", ""));
1601 return ret;
1605 * Transform to IETF style PK-INIT reply so that free works below
1608 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1609 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1610 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1611 btmm.encKeyPack->data = NULL;
1612 btmm.encKeyPack->length = 0;
1613 free_PA_PK_AS_REP_BTMM(&btmm);
1614 os = rep.u.encKeyPack;
1618 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1619 if (ret) {
1620 free_PA_PK_AS_REP(&rep);
1621 krb5_set_error_message(context, ret,
1622 N_("PKINIT: failed to unwrap CI", ""));
1623 return ret;
1626 switch (rep.element) {
1627 case choice_PA_PK_AS_REP_dhInfo:
1628 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1629 ctx->clientDHNonce,
1630 rep.u.dhInfo.serverDHNonce,
1631 nonce, pa, key);
1632 break;
1633 case choice_PA_PK_AS_REP_encKeyPack:
1634 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1635 ctx, etype, hi, nonce, req_buffer, pa, key);
1636 break;
1637 default:
1638 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1640 der_free_octet_string(&data);
1641 der_free_oid(&oid);
1642 free_PA_PK_AS_REP(&rep);
1644 } else if (ctx->type == PKINIT_WIN2K) {
1645 PA_PK_AS_REP_Win2k w2krep;
1647 /* Check for Windows encoding of the AS-REP pa data */
1649 #if 0 /* should this be ? */
1650 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1651 krb5_set_error_message(context, EINVAL,
1652 "PKINIT: wrong padata recv");
1653 return EINVAL;
1655 #endif
1657 memset(&w2krep, 0, sizeof(w2krep));
1659 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1660 pa->padata_value.length,
1661 &w2krep,
1662 &size);
1663 if (ret) {
1664 krb5_set_error_message(context, ret,
1665 N_("PKINIT: Failed decoding windows "
1666 "pkinit reply %d", ""), (int)ret);
1667 return ret;
1670 krb5_clear_error_message(context);
1672 switch (w2krep.element) {
1673 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1674 heim_octet_string data;
1675 heim_oid oid;
1677 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1678 &oid, &data, NULL);
1679 free_PA_PK_AS_REP_Win2k(&w2krep);
1680 if (ret) {
1681 krb5_set_error_message(context, ret,
1682 N_("PKINIT: failed to unwrap CI", ""));
1683 return ret;
1686 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1687 ctx, etype, hi, nonce, req_buffer, pa, key);
1688 der_free_octet_string(&data);
1689 der_free_oid(&oid);
1691 break;
1693 default:
1694 free_PA_PK_AS_REP_Win2k(&w2krep);
1695 ret = EINVAL;
1696 krb5_set_error_message(context, ret,
1697 N_("PKINIT: win2k reply invalid "
1698 "content type", ""));
1699 break;
1702 } else {
1703 ret = EINVAL;
1704 krb5_set_error_message(context, ret,
1705 N_("PKINIT: unknown reply type", ""));
1708 return ret;
1711 struct prompter {
1712 krb5_context context;
1713 krb5_prompter_fct prompter;
1714 void *prompter_data;
1717 static int
1718 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1720 krb5_error_code ret;
1721 krb5_prompt prompt;
1722 krb5_data password_data;
1723 struct prompter *p = data;
1725 password_data.data = prompter->reply.data;
1726 password_data.length = prompter->reply.length;
1728 prompt.prompt = prompter->prompt;
1729 prompt.hidden = hx509_prompt_hidden(prompter->type);
1730 prompt.reply = &password_data;
1732 switch (prompter->type) {
1733 case HX509_PROMPT_TYPE_INFO:
1734 prompt.type = KRB5_PROMPT_TYPE_INFO;
1735 break;
1736 case HX509_PROMPT_TYPE_PASSWORD:
1737 case HX509_PROMPT_TYPE_QUESTION:
1738 default:
1739 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1740 break;
1743 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1744 if (ret) {
1745 memset (prompter->reply.data, 0, prompter->reply.length);
1746 return 1;
1748 return 0;
1751 static krb5_error_code
1752 _krb5_pk_set_user_id(krb5_context context,
1753 krb5_principal principal,
1754 krb5_pk_init_ctx ctx,
1755 struct hx509_certs_data *certs)
1757 hx509_certs c = hx509_certs_ref(certs);
1758 hx509_query *q = NULL;
1759 int ret;
1761 if (ctx->id->certs)
1762 hx509_certs_free(&ctx->id->certs);
1763 if (ctx->id->cert) {
1764 hx509_cert_free(ctx->id->cert);
1765 ctx->id->cert = NULL;
1768 ctx->id->certs = c;
1769 ctx->anonymous = 0;
1771 ret = hx509_query_alloc(context->hx509ctx, &q);
1772 if (ret) {
1773 pk_copy_error(context, context->hx509ctx, ret,
1774 "Allocate query to find signing certificate");
1775 return ret;
1778 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1779 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1781 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1782 ctx->id->flags |= PKINIT_BTMM;
1785 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1786 hx509_query_free(context->hx509ctx, q);
1788 if (ret == 0 && _krb5_have_debug(context, 2)) {
1789 hx509_name name;
1790 char *str, *sn;
1791 heim_integer i;
1793 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1794 if (ret)
1795 goto out;
1797 ret = hx509_name_to_string(name, &str);
1798 hx509_name_free(&name);
1799 if (ret)
1800 goto out;
1802 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1803 if (ret) {
1804 free(str);
1805 goto out;
1808 ret = der_print_hex_heim_integer(&i, &sn);
1809 der_free_heim_integer(&i);
1810 if (ret) {
1811 free(name);
1812 goto out;
1815 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1816 free(str);
1817 free(sn);
1819 out:
1821 return ret;
1824 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1825 _krb5_pk_load_id(krb5_context context,
1826 struct krb5_pk_identity **ret_id,
1827 const char *user_id,
1828 const char *anchor_id,
1829 char * const *chain_list,
1830 char * const *revoke_list,
1831 krb5_prompter_fct prompter,
1832 void *prompter_data,
1833 char *password)
1835 struct krb5_pk_identity *id = NULL;
1836 struct prompter p;
1837 int ret;
1839 *ret_id = NULL;
1841 if (anchor_id == NULL) {
1842 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1843 N_("PKINIT: No anchor given", ""));
1844 return HEIM_PKINIT_NO_VALID_CA;
1847 /* load cert */
1849 id = calloc(1, sizeof(*id));
1850 if (id == NULL)
1851 return krb5_enomem(context);
1853 if (user_id) {
1854 hx509_lock lock;
1856 ret = hx509_lock_init(context->hx509ctx, &lock);
1857 if (ret) {
1858 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1859 goto out;
1862 if (password && password[0])
1863 hx509_lock_add_password(lock, password);
1865 if (prompter) {
1866 p.context = context;
1867 p.prompter = prompter;
1868 p.prompter_data = prompter_data;
1870 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1871 if (ret) {
1872 hx509_lock_free(lock);
1873 goto out;
1877 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1878 hx509_lock_free(lock);
1879 if (ret) {
1880 pk_copy_error(context, context->hx509ctx, ret,
1881 "Failed to init cert certs");
1882 goto out;
1884 } else {
1885 id->certs = NULL;
1888 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1889 if (ret) {
1890 pk_copy_error(context, context->hx509ctx, ret,
1891 "Failed to init anchors");
1892 goto out;
1895 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1896 0, NULL, &id->certpool);
1897 if (ret) {
1898 pk_copy_error(context, context->hx509ctx, ret,
1899 "Failed to init chain");
1900 goto out;
1903 while (chain_list && *chain_list) {
1904 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1905 NULL, *chain_list);
1906 if (ret) {
1907 pk_copy_error(context, context->hx509ctx, ret,
1908 "Failed to laod chain %s",
1909 *chain_list);
1910 goto out;
1912 chain_list++;
1915 if (revoke_list) {
1916 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1917 if (ret) {
1918 pk_copy_error(context, context->hx509ctx, ret,
1919 "Failed init revoke list");
1920 goto out;
1923 while (*revoke_list) {
1924 ret = hx509_revoke_add_crl(context->hx509ctx,
1925 id->revokectx,
1926 *revoke_list);
1927 if (ret) {
1928 pk_copy_error(context, context->hx509ctx, ret,
1929 "Failed load revoke list");
1930 goto out;
1932 revoke_list++;
1934 } else
1935 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1937 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1938 if (ret) {
1939 pk_copy_error(context, context->hx509ctx, ret,
1940 "Failed init verify context");
1941 goto out;
1944 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1945 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1947 out:
1948 if (ret) {
1949 hx509_verify_destroy_ctx(id->verify_ctx);
1950 hx509_certs_free(&id->certs);
1951 hx509_certs_free(&id->anchors);
1952 hx509_certs_free(&id->certpool);
1953 hx509_revoke_free(&id->revokectx);
1954 free(id);
1955 } else
1956 *ret_id = id;
1958 return ret;
1965 static void
1966 pk_copy_error(krb5_context context,
1967 hx509_context hx509ctx,
1968 int hxret,
1969 const char *fmt,
1970 ...)
1972 va_list va;
1973 char *s, *f;
1974 int ret;
1976 va_start(va, fmt);
1977 ret = vasprintf(&f, fmt, va);
1978 va_end(va);
1979 if (ret == -1 || f == NULL) {
1980 krb5_clear_error_message(context);
1981 return;
1984 s = hx509_get_error_string(hx509ctx, hxret);
1985 if (s == NULL) {
1986 krb5_clear_error_message(context);
1987 free(f);
1988 return;
1990 krb5_set_error_message(context, hxret, "%s: %s", f, s);
1991 free(s);
1992 free(f);
1995 static int
1996 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1997 const char *name, heim_integer *integer)
1999 int ret;
2000 char *p1;
2001 p1 = strsep(p, " \t");
2002 if (p1 == NULL) {
2003 krb5_set_error_message(context, EINVAL,
2004 N_("moduli file %s missing %s on line %d", ""),
2005 file, name, lineno);
2006 return EINVAL;
2008 ret = der_parse_hex_heim_integer(p1, integer);
2009 if (ret) {
2010 krb5_set_error_message(context, ret,
2011 N_("moduli file %s failed parsing %s "
2012 "on line %d", ""),
2013 file, name, lineno);
2014 return ret;
2017 return 0;
2020 krb5_error_code
2021 _krb5_parse_moduli_line(krb5_context context,
2022 const char *file,
2023 int lineno,
2024 char *p,
2025 struct krb5_dh_moduli **m)
2027 struct krb5_dh_moduli *m1;
2028 char *p1;
2029 int ret;
2031 *m = NULL;
2033 m1 = calloc(1, sizeof(*m1));
2034 if (m1 == NULL)
2035 return krb5_enomem(context);
2037 while (isspace((unsigned char)*p))
2038 p++;
2039 if (*p == '#') {
2040 free(m1);
2041 return 0;
2043 ret = EINVAL;
2045 p1 = strsep(&p, " \t");
2046 if (p1 == NULL) {
2047 krb5_set_error_message(context, ret,
2048 N_("moduli file %s missing name on line %d", ""),
2049 file, lineno);
2050 goto out;
2052 m1->name = strdup(p1);
2053 if (m1->name == NULL) {
2054 ret = krb5_enomem(context);
2055 goto out;
2058 p1 = strsep(&p, " \t");
2059 if (p1 == NULL) {
2060 krb5_set_error_message(context, ret,
2061 N_("moduli file %s missing bits on line %d", ""),
2062 file, lineno);
2063 goto out;
2066 m1->bits = atoi(p1);
2067 if (m1->bits == 0) {
2068 krb5_set_error_message(context, ret,
2069 N_("moduli file %s have un-parsable "
2070 "bits on line %d", ""), file, lineno);
2071 goto out;
2074 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2075 if (ret)
2076 goto out;
2077 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2078 if (ret)
2079 goto out;
2080 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2081 if (ret)
2082 goto out;
2084 *m = m1;
2086 return 0;
2087 out:
2088 free(m1->name);
2089 der_free_heim_integer(&m1->p);
2090 der_free_heim_integer(&m1->g);
2091 der_free_heim_integer(&m1->q);
2092 free(m1);
2093 return ret;
2096 void
2097 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2099 int i;
2100 for (i = 0; moduli[i] != NULL; i++) {
2101 free(moduli[i]->name);
2102 der_free_heim_integer(&moduli[i]->p);
2103 der_free_heim_integer(&moduli[i]->g);
2104 der_free_heim_integer(&moduli[i]->q);
2105 free(moduli[i]);
2107 free(moduli);
2110 static const char *default_moduli_RFC2412_MODP_group2 =
2111 /* name */
2112 "RFC2412-MODP-group2 "
2113 /* bits */
2114 "1024 "
2115 /* p */
2116 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2117 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2118 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2119 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2120 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2121 "FFFFFFFF" "FFFFFFFF "
2122 /* g */
2123 "02 "
2124 /* q */
2125 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2126 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2127 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2128 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2129 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2130 "FFFFFFFF" "FFFFFFFF";
2132 static const char *default_moduli_rfc3526_MODP_group14 =
2133 /* name */
2134 "rfc3526-MODP-group14 "
2135 /* bits */
2136 "1760 "
2137 /* p */
2138 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2139 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2140 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2141 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2142 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2143 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2144 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2145 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2146 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2147 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2148 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2149 /* g */
2150 "02 "
2151 /* q */
2152 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2153 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2154 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2155 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2156 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2157 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2158 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2159 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2160 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2161 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2162 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2164 krb5_error_code
2165 _krb5_parse_moduli(krb5_context context, const char *file,
2166 struct krb5_dh_moduli ***moduli)
2168 /* name bits P G Q */
2169 krb5_error_code ret;
2170 struct krb5_dh_moduli **m = NULL, **m2;
2171 char buf[4096];
2172 FILE *f;
2173 int lineno = 0, n = 0;
2175 *moduli = NULL;
2177 m = calloc(1, sizeof(m[0]) * 3);
2178 if (m == NULL)
2179 return krb5_enomem(context);
2181 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2182 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2183 if (ret) {
2184 _krb5_free_moduli(m);
2185 return ret;
2187 n++;
2189 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2190 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2191 if (ret) {
2192 _krb5_free_moduli(m);
2193 return ret;
2195 n++;
2198 if (file == NULL)
2199 file = MODULI_FILE;
2201 #ifdef KRB5_USE_PATH_TOKENS
2203 char * exp_file;
2205 if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2206 f = fopen(exp_file, "r");
2207 krb5_xfree(exp_file);
2208 } else {
2209 f = NULL;
2212 #else
2213 f = fopen(file, "r");
2214 #endif
2216 if (f == NULL) {
2217 *moduli = m;
2218 return 0;
2220 rk_cloexec_file(f);
2222 while(fgets(buf, sizeof(buf), f) != NULL) {
2223 struct krb5_dh_moduli *element;
2225 buf[strcspn(buf, "\n")] = '\0';
2226 lineno++;
2228 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2229 if (m2 == NULL) {
2230 _krb5_free_moduli(m);
2231 return krb5_enomem(context);
2233 m = m2;
2235 m[n] = NULL;
2237 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2238 if (ret) {
2239 _krb5_free_moduli(m);
2240 return ret;
2242 if (element == NULL)
2243 continue;
2245 m[n] = element;
2246 m[n + 1] = NULL;
2247 n++;
2249 *moduli = m;
2250 return 0;
2253 krb5_error_code
2254 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2255 heim_integer *p, heim_integer *g, heim_integer *q,
2256 struct krb5_dh_moduli **moduli,
2257 char **name)
2259 int i;
2261 if (name)
2262 *name = NULL;
2264 for (i = 0; moduli[i] != NULL; i++) {
2265 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2266 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2267 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2269 if (bits && bits > moduli[i]->bits) {
2270 krb5_set_error_message(context,
2271 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2272 N_("PKINIT: DH group parameter %s "
2273 "no accepted, not enough bits "
2274 "generated", ""),
2275 moduli[i]->name);
2276 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2278 if (name)
2279 *name = strdup(moduli[i]->name);
2280 return 0;
2283 krb5_set_error_message(context,
2284 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2285 N_("PKINIT: DH group parameter no ok", ""));
2286 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2288 #endif /* PKINIT */
2290 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2291 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2293 #ifdef PKINIT
2294 krb5_pk_init_ctx ctx;
2296 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2297 return;
2298 ctx = opt->opt_private->pk_init_ctx;
2299 switch (ctx->keyex) {
2300 case USE_DH:
2301 if (ctx->u.dh)
2302 DH_free(ctx->u.dh);
2303 break;
2304 case USE_RSA:
2305 break;
2306 case USE_ECDH:
2307 #ifdef HAVE_OPENSSL
2308 if (ctx->u.eckey)
2309 EC_KEY_free(ctx->u.eckey);
2310 #endif
2311 break;
2313 if (ctx->id) {
2314 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2315 hx509_certs_free(&ctx->id->certs);
2316 hx509_cert_free(ctx->id->cert);
2317 hx509_certs_free(&ctx->id->anchors);
2318 hx509_certs_free(&ctx->id->certpool);
2320 if (ctx->clientDHNonce) {
2321 krb5_free_data(NULL, ctx->clientDHNonce);
2322 ctx->clientDHNonce = NULL;
2324 if (ctx->m)
2325 _krb5_free_moduli(ctx->m);
2326 free(ctx->id);
2327 ctx->id = NULL;
2329 free(opt->opt_private->pk_init_ctx);
2330 opt->opt_private->pk_init_ctx = NULL;
2331 #endif
2334 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2335 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2336 krb5_get_init_creds_opt *opt,
2337 krb5_principal principal,
2338 const char *user_id,
2339 const char *x509_anchors,
2340 char * const * pool,
2341 char * const * pki_revoke,
2342 int flags,
2343 krb5_prompter_fct prompter,
2344 void *prompter_data,
2345 char *password)
2347 #ifdef PKINIT
2348 krb5_error_code ret;
2349 char *anchors = NULL;
2351 if (opt->opt_private == NULL) {
2352 krb5_set_error_message(context, EINVAL,
2353 N_("PKINIT: on non extendable opt", ""));
2354 return EINVAL;
2357 opt->opt_private->pk_init_ctx =
2358 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2359 if (opt->opt_private->pk_init_ctx == NULL)
2360 return krb5_enomem(context);
2361 opt->opt_private->pk_init_ctx->require_binding = 0;
2362 opt->opt_private->pk_init_ctx->require_eku = 1;
2363 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2364 opt->opt_private->pk_init_ctx->peer = NULL;
2366 /* XXX implement krb5_appdefault_strings */
2367 if (pool == NULL)
2368 pool = krb5_config_get_strings(context, NULL,
2369 "appdefaults",
2370 "pkinit_pool",
2371 NULL);
2373 if (pki_revoke == NULL)
2374 pki_revoke = krb5_config_get_strings(context, NULL,
2375 "appdefaults",
2376 "pkinit_revoke",
2377 NULL);
2379 if (x509_anchors == NULL) {
2380 krb5_appdefault_string(context, "kinit",
2381 krb5_principal_get_realm(context, principal),
2382 "pkinit_anchors", NULL, &anchors);
2383 x509_anchors = anchors;
2386 if (flags & 4)
2387 opt->opt_private->pk_init_ctx->anonymous = 1;
2389 ret = _krb5_pk_load_id(context,
2390 &opt->opt_private->pk_init_ctx->id,
2391 user_id,
2392 x509_anchors,
2393 pool,
2394 pki_revoke,
2395 prompter,
2396 prompter_data,
2397 password);
2398 if (ret) {
2399 free(opt->opt_private->pk_init_ctx);
2400 opt->opt_private->pk_init_ctx = NULL;
2401 return ret;
2404 if (opt->opt_private->pk_init_ctx->id->certs) {
2405 _krb5_pk_set_user_id(context,
2406 principal,
2407 opt->opt_private->pk_init_ctx,
2408 opt->opt_private->pk_init_ctx->id->certs);
2409 } else
2410 opt->opt_private->pk_init_ctx->id->cert = NULL;
2412 if ((flags & 2) == 0) {
2413 hx509_context hx509ctx = context->hx509ctx;
2414 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2416 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2419 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2421 if (cert) {
2422 AlgorithmIdentifier alg;
2424 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2425 if (ret == 0) {
2426 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2427 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2428 free_AlgorithmIdentifier(&alg);
2432 } else {
2433 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2435 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2436 krb5_set_error_message(context, EINVAL,
2437 N_("No anonymous pkinit support in RSA mode", ""));
2438 return EINVAL;
2442 return 0;
2443 #else
2444 krb5_set_error_message(context, EINVAL,
2445 N_("no support for PKINIT compiled in", ""));
2446 return EINVAL;
2447 #endif
2450 krb5_error_code KRB5_LIB_FUNCTION
2451 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2452 krb5_get_init_creds_opt *opt,
2453 struct hx509_certs_data *certs)
2455 #ifdef PKINIT
2456 if (opt->opt_private == NULL) {
2457 krb5_set_error_message(context, EINVAL,
2458 N_("PKINIT: on non extendable opt", ""));
2459 return EINVAL;
2461 if (opt->opt_private->pk_init_ctx == NULL) {
2462 krb5_set_error_message(context, EINVAL,
2463 N_("PKINIT: on pkinit context", ""));
2464 return EINVAL;
2467 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2469 return 0;
2470 #else
2471 krb5_set_error_message(context, EINVAL,
2472 N_("no support for PKINIT compiled in", ""));
2473 return EINVAL;
2474 #endif
2477 #ifdef PKINIT
2479 static int
2480 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2482 hx509_octet_string_list list;
2483 int ret;
2485 *upn = NULL;
2487 ret = hx509_cert_find_subjectAltName_otherName(context,
2488 cert,
2489 &asn1_oid_id_pkinit_ms_san,
2490 &list);
2491 if (ret)
2492 return 0;
2494 if (list.len > 0 && list.val[0].length > 0)
2495 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2496 upn, NULL);
2497 else
2498 ret = 1;
2499 hx509_free_octet_string_list(&list);
2501 return ret;
2504 static int
2505 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2507 char *upn;
2508 int ret;
2510 ret = get_ms_san(context, cert, &upn);
2511 if (ret == 0)
2512 free(upn);
2513 return ret;
2518 #endif
2521 * Private since it need to be redesigned using krb5_get_init_creds()
2524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2525 krb5_pk_enterprise_cert(krb5_context context,
2526 const char *user_id,
2527 krb5_const_realm realm,
2528 krb5_principal *principal,
2529 struct hx509_certs_data **res)
2531 #ifdef PKINIT
2532 krb5_error_code ret;
2533 hx509_certs certs, result;
2534 hx509_cert cert = NULL;
2535 hx509_query *q;
2536 char *name;
2538 *principal = NULL;
2539 if (res)
2540 *res = NULL;
2542 if (user_id == NULL) {
2543 krb5_set_error_message(context, ENOENT, "no user id");
2544 return ENOENT;
2547 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2548 if (ret) {
2549 pk_copy_error(context, context->hx509ctx, ret,
2550 "Failed to init cert certs");
2551 goto out;
2554 ret = hx509_query_alloc(context->hx509ctx, &q);
2555 if (ret) {
2556 krb5_set_error_message(context, ret, "out of memory");
2557 hx509_certs_free(&certs);
2558 goto out;
2561 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2562 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2563 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2564 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2566 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2567 hx509_query_free(context->hx509ctx, q);
2568 hx509_certs_free(&certs);
2569 if (ret) {
2570 pk_copy_error(context, context->hx509ctx, ret,
2571 "Failed to find PKINIT certificate");
2572 return ret;
2575 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2576 hx509_certs_free(&result);
2577 if (ret) {
2578 pk_copy_error(context, context->hx509ctx, ret,
2579 "Failed to get one cert");
2580 goto out;
2583 ret = get_ms_san(context->hx509ctx, cert, &name);
2584 if (ret) {
2585 pk_copy_error(context, context->hx509ctx, ret,
2586 "Failed to get MS SAN");
2587 goto out;
2590 ret = krb5_make_principal(context, principal, realm, name, NULL);
2591 free(name);
2592 if (ret)
2593 goto out;
2595 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2597 if (res) {
2598 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2599 if (ret)
2600 goto out;
2602 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2603 if (ret) {
2604 hx509_certs_free(res);
2605 goto out;
2609 out:
2610 hx509_cert_free(cert);
2612 return ret;
2613 #else
2614 krb5_set_error_message(context, EINVAL,
2615 N_("no support for PKINIT compiled in", ""));
2616 return EINVAL;
2617 #endif