Remove building with NOCRYPTO option
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / pkinit.c
blob4341499369fc602500534fcd6fbede91d299084a
1 /* $NetBSD: pkinit.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
3 /*
4 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "krb5_locl.h"
40 struct krb5_dh_moduli {
41 char *name;
42 unsigned long bits;
43 heim_integer p;
44 heim_integer g;
45 heim_integer q;
48 #ifdef PKINIT
50 #include <krb5/cms_asn1.h>
51 #include <krb5/pkcs8_asn1.h>
52 #include <krb5/pkcs9_asn1.h>
53 #include <krb5/pkcs12_asn1.h>
54 #include <krb5/pkinit_asn1.h>
55 #include <krb5/asn1_err.h>
57 #include <krb5/der.h>
59 struct krb5_pk_cert {
60 hx509_cert cert;
63 struct krb5_pk_init_ctx_data {
64 struct krb5_pk_identity *id;
65 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
66 union {
67 DH *dh;
68 #ifdef HAVE_OPENSSL
69 EC_KEY *eckey;
70 #endif
71 } u;
72 krb5_data *clientDHNonce;
73 struct krb5_dh_moduli **m;
74 hx509_peer_info peer;
75 enum krb5_pk_type type;
76 unsigned int require_binding:1;
77 unsigned int require_eku:1;
78 unsigned int require_krbtgt_otherName:1;
79 unsigned int require_hostname_match:1;
80 unsigned int trustedCertifiers:1;
81 unsigned int anonymous:1;
84 static void
85 pk_copy_error(krb5_context context,
86 hx509_context hx509ctx,
87 int hxret,
88 const char *fmt,
89 ...)
90 __attribute__ ((format (printf, 4, 5)));
96 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
97 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
99 if (cert->cert) {
100 hx509_cert_free(cert->cert);
102 free(cert);
105 static krb5_error_code
106 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
108 integer->length = BN_num_bytes(bn);
109 integer->data = malloc(integer->length);
110 if (integer->data == NULL) {
111 krb5_clear_error_message(context);
112 return ENOMEM;
114 BN_bn2bin(bn, integer->data);
115 integer->negative = BN_is_negative(bn);
116 return 0;
119 static BIGNUM *
120 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
122 BIGNUM *bn;
124 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
125 if (bn == NULL) {
126 krb5_set_error_message(context, ENOMEM,
127 N_("PKINIT: parsing BN failed %s", ""), field);
128 return NULL;
130 BN_set_negative(bn, f->negative);
131 return bn;
134 static krb5_error_code
135 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
136 struct krb5_dh_moduli **moduli)
138 const struct krb5_dh_moduli *m;
140 if (bits == 0) {
141 m = moduli[1]; /* XXX */
142 if (m == NULL)
143 m = moduli[0]; /* XXX */
144 } else {
145 int i;
146 for (i = 0; moduli[i] != NULL; i++) {
147 if (bits < moduli[i]->bits)
148 break;
150 if (moduli[i] == NULL) {
151 krb5_set_error_message(context, EINVAL,
152 N_("Did not find a DH group parameter "
153 "matching requirement of %lu bits", ""),
154 bits);
155 return EINVAL;
157 m = moduli[i];
160 dh->p = integer_to_BN(context, "p", &m->p);
161 if (dh->p == NULL)
162 return ENOMEM;
163 dh->g = integer_to_BN(context, "g", &m->g);
164 if (dh->g == NULL)
165 return ENOMEM;
166 dh->q = integer_to_BN(context, "q", &m->q);
167 if (dh->q == NULL)
168 return ENOMEM;
170 return 0;
173 struct certfind {
174 const char *type;
175 const heim_oid *oid;
179 * Try searchin the key by to use by first looking for for PK-INIT
180 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
183 static krb5_error_code
184 find_cert(krb5_context context, struct krb5_pk_identity *id,
185 hx509_query *q, hx509_cert *cert)
187 struct certfind cf[4] = {
188 { "MobileMe EKU" },
189 { "PKINIT EKU" },
190 { "MS EKU" },
191 { "any (or no)" }
193 int ret = HX509_CERT_NOT_FOUND;
194 size_t i, start = 1;
195 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
196 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
199 if (id->flags & PKINIT_BTMM)
200 start = 0;
202 cf[0].oid = &mobileMe;
203 cf[1].oid = &asn1_oid_id_pkekuoid;
204 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
205 cf[3].oid = NULL;
207 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
208 ret = hx509_query_match_eku(q, cf[i].oid);
209 if (ret) {
210 pk_copy_error(context, context->hx509ctx, ret,
211 "Failed setting %s OID", cf[i].type);
212 return ret;
215 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
216 if (ret == 0)
217 break;
218 pk_copy_error(context, context->hx509ctx, ret,
219 "Failed finding certificate with %s OID", cf[i].type);
221 return ret;
225 static krb5_error_code
226 create_signature(krb5_context context,
227 const heim_oid *eContentType,
228 krb5_data *eContent,
229 struct krb5_pk_identity *id,
230 hx509_peer_info peer,
231 krb5_data *sd_data)
233 int ret, flags = 0;
235 if (id->cert == NULL)
236 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
238 ret = hx509_cms_create_signed_1(context->hx509ctx,
239 flags,
240 eContentType,
241 eContent->data,
242 eContent->length,
243 NULL,
244 id->cert,
245 peer,
246 NULL,
247 id->certs,
248 sd_data);
249 if (ret) {
250 pk_copy_error(context, context->hx509ctx, ret,
251 "Create CMS signedData");
252 return ret;
255 return 0;
258 static int
259 cert2epi(hx509_context context, void *ctx, hx509_cert c)
261 ExternalPrincipalIdentifiers *ids = ctx;
262 ExternalPrincipalIdentifier id;
263 hx509_name subject = NULL;
264 void *p;
265 int ret;
267 if (ids->len > 10)
268 return 0;
270 memset(&id, 0, sizeof(id));
272 ret = hx509_cert_get_subject(c, &subject);
273 if (ret)
274 return ret;
276 if (hx509_name_is_null_p(subject) != 0) {
278 id.subjectName = calloc(1, sizeof(*id.subjectName));
279 if (id.subjectName == NULL) {
280 hx509_name_free(&subject);
281 free_ExternalPrincipalIdentifier(&id);
282 return ENOMEM;
285 ret = hx509_name_binary(subject, id.subjectName);
286 if (ret) {
287 hx509_name_free(&subject);
288 free_ExternalPrincipalIdentifier(&id);
289 return ret;
292 hx509_name_free(&subject);
295 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
296 if (id.issuerAndSerialNumber == NULL) {
297 free_ExternalPrincipalIdentifier(&id);
298 return ENOMEM;
302 IssuerAndSerialNumber iasn;
303 hx509_name issuer;
304 size_t size = 0;
306 memset(&iasn, 0, sizeof(iasn));
308 ret = hx509_cert_get_issuer(c, &issuer);
309 if (ret) {
310 free_ExternalPrincipalIdentifier(&id);
311 return ret;
314 ret = hx509_name_to_Name(issuer, &iasn.issuer);
315 hx509_name_free(&issuer);
316 if (ret) {
317 free_ExternalPrincipalIdentifier(&id);
318 return ret;
321 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
322 if (ret) {
323 free_IssuerAndSerialNumber(&iasn);
324 free_ExternalPrincipalIdentifier(&id);
325 return ret;
328 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
329 id.issuerAndSerialNumber->data,
330 id.issuerAndSerialNumber->length,
331 &iasn, &size, ret);
332 free_IssuerAndSerialNumber(&iasn);
333 if (ret)
334 return ret;
335 if (id.issuerAndSerialNumber->length != size)
336 abort();
339 id.subjectKeyIdentifier = NULL;
341 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
342 if (p == NULL) {
343 free_ExternalPrincipalIdentifier(&id);
344 return ENOMEM;
347 ids->val = p;
348 ids->val[ids->len] = id;
349 ids->len++;
351 return 0;
354 static krb5_error_code
355 build_edi(krb5_context context,
356 hx509_context hx509ctx,
357 hx509_certs certs,
358 ExternalPrincipalIdentifiers *ids)
360 return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
363 static krb5_error_code
364 build_auth_pack(krb5_context context,
365 unsigned nonce,
366 krb5_pk_init_ctx ctx,
367 const KDC_REQ_BODY *body,
368 AuthPack *a)
370 size_t buf_size, len = 0;
371 krb5_error_code ret;
372 void *buf;
373 krb5_timestamp sec;
374 int32_t usec;
375 Checksum checksum;
377 krb5_clear_error_message(context);
379 memset(&checksum, 0, sizeof(checksum));
381 krb5_us_timeofday(context, &sec, &usec);
382 a->pkAuthenticator.ctime = sec;
383 a->pkAuthenticator.nonce = nonce;
385 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
386 if (ret)
387 return ret;
388 if (buf_size != len)
389 krb5_abortx(context, "internal error in ASN.1 encoder");
391 ret = krb5_create_checksum(context,
392 NULL,
394 CKSUMTYPE_SHA1,
395 buf,
396 len,
397 &checksum);
398 free(buf);
399 if (ret)
400 return ret;
402 ALLOC(a->pkAuthenticator.paChecksum, 1);
403 if (a->pkAuthenticator.paChecksum == NULL) {
404 krb5_set_error_message(context, ENOMEM,
405 N_("malloc: out of memory", ""));
406 return ENOMEM;
409 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
410 checksum.checksum.data, checksum.checksum.length);
411 free_Checksum(&checksum);
412 if (ret)
413 return ret;
415 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
416 const char *moduli_file;
417 unsigned long dh_min_bits;
418 krb5_data dhbuf;
419 size_t size = 0;
421 krb5_data_zero(&dhbuf);
425 moduli_file = krb5_config_get_string(context, NULL,
426 "libdefaults",
427 "moduli",
428 NULL);
430 dh_min_bits =
431 krb5_config_get_int_default(context, NULL, 0,
432 "libdefaults",
433 "pkinit_dh_min_bits",
434 NULL);
436 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
437 if (ret)
438 return ret;
440 ctx->u.dh = DH_new();
441 if (ctx->u.dh == NULL) {
442 krb5_set_error_message(context, ENOMEM,
443 N_("malloc: out of memory", ""));
444 return ENOMEM;
447 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
448 if (ret)
449 return ret;
451 if (DH_generate_key(ctx->u.dh) != 1) {
452 krb5_set_error_message(context, ENOMEM,
453 N_("pkinit: failed to generate DH key", ""));
454 return ENOMEM;
458 if (1 /* support_cached_dh */) {
459 ALLOC(a->clientDHNonce, 1);
460 if (a->clientDHNonce == NULL) {
461 krb5_clear_error_message(context);
462 return ENOMEM;
464 ret = krb5_data_alloc(a->clientDHNonce, 40);
465 if (a->clientDHNonce == NULL) {
466 krb5_clear_error_message(context);
467 return ret;
469 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
470 ret = krb5_copy_data(context, a->clientDHNonce,
471 &ctx->clientDHNonce);
472 if (ret)
473 return ret;
476 ALLOC(a->clientPublicValue, 1);
477 if (a->clientPublicValue == NULL)
478 return ENOMEM;
480 if (ctx->keyex == USE_DH) {
481 DH *dh = ctx->u.dh;
482 DomainParameters dp;
483 heim_integer dh_pub_key;
485 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
486 &a->clientPublicValue->algorithm.algorithm);
487 if (ret)
488 return ret;
490 memset(&dp, 0, sizeof(dp));
492 ret = BN_to_integer(context, dh->p, &dp.p);
493 if (ret) {
494 free_DomainParameters(&dp);
495 return ret;
497 ret = BN_to_integer(context, dh->g, &dp.g);
498 if (ret) {
499 free_DomainParameters(&dp);
500 return ret;
502 dp.q = calloc(1, sizeof(*dp.q));
503 if (dp.q == NULL) {
504 free_DomainParameters(&dp);
505 return ENOMEM;
507 ret = BN_to_integer(context, dh->q, dp.q);
508 if (ret) {
509 free_DomainParameters(&dp);
510 return ret;
512 dp.j = NULL;
513 dp.validationParms = NULL;
515 a->clientPublicValue->algorithm.parameters =
516 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
517 if (a->clientPublicValue->algorithm.parameters == NULL) {
518 free_DomainParameters(&dp);
519 return ret;
522 ASN1_MALLOC_ENCODE(DomainParameters,
523 a->clientPublicValue->algorithm.parameters->data,
524 a->clientPublicValue->algorithm.parameters->length,
525 &dp, &size, ret);
526 free_DomainParameters(&dp);
527 if (ret)
528 return ret;
529 if (size != a->clientPublicValue->algorithm.parameters->length)
530 krb5_abortx(context, "Internal ASN1 encoder error");
532 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
533 if (ret)
534 return ret;
536 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
537 &dh_pub_key, &size, ret);
538 der_free_heim_integer(&dh_pub_key);
539 if (ret)
540 return ret;
541 if (size != dhbuf.length)
542 krb5_abortx(context, "asn1 internal error");
543 } else if (ctx->keyex == USE_ECDH) {
544 #ifdef HAVE_OPENSSL
545 ECParameters ecp;
546 unsigned char *p;
547 int xlen;
549 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
551 ecp.element = choice_ECParameters_namedCurve;
552 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
553 &ecp.u.namedCurve);
554 if (ret)
555 return ret;
557 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
558 if (a->clientPublicValue->algorithm.parameters == NULL) {
559 free_ECParameters(&ecp);
560 return ENOMEM;
562 ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
563 free_ECParameters(&ecp);
564 if (ret)
565 return ret;
566 if ((int)size != xlen)
567 krb5_abortx(context, "asn1 internal error");
569 a->clientPublicValue->algorithm.parameters->data = p;
570 a->clientPublicValue->algorithm.parameters->length = size;
572 /* copy in public key */
574 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
575 &a->clientPublicValue->algorithm.algorithm);
576 if (ret)
577 return ret;
579 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
580 if (ctx->u.eckey == NULL)
581 return ENOMEM;
583 ret = EC_KEY_generate_key(ctx->u.eckey);
584 if (ret != 1)
585 return EINVAL;
587 /* encode onto dhkey */
589 xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
590 if (xlen <= 0)
591 abort();
593 dhbuf.data = malloc(xlen);
594 if (dhbuf.data == NULL)
595 abort();
596 dhbuf.length = xlen;
597 p = dhbuf.data;
599 xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
600 if (xlen <= 0)
601 abort();
603 /* XXX verify that this is right with RFC3279 */
604 #else
605 return EINVAL;
606 #endif
607 } else
608 krb5_abortx(context, "internal error");
609 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
610 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
614 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
615 if (a->supportedCMSTypes == NULL)
616 return ENOMEM;
618 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
619 ctx->id->cert,
620 &a->supportedCMSTypes->val,
621 &a->supportedCMSTypes->len);
622 if (ret)
623 return ret;
626 return ret;
629 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
630 _krb5_pk_mk_ContentInfo(krb5_context context,
631 const krb5_data *buf,
632 const heim_oid *oid,
633 struct ContentInfo *content_info)
635 krb5_error_code ret;
637 ret = der_copy_oid(oid, &content_info->contentType);
638 if (ret)
639 return ret;
640 ALLOC(content_info->content, 1);
641 if (content_info->content == NULL)
642 return ENOMEM;
643 content_info->content->data = malloc(buf->length);
644 if (content_info->content->data == NULL)
645 return ENOMEM;
646 memcpy(content_info->content->data, buf->data, buf->length);
647 content_info->content->length = buf->length;
648 return 0;
651 static krb5_error_code
652 pk_mk_padata(krb5_context context,
653 krb5_pk_init_ctx ctx,
654 const KDC_REQ_BODY *req_body,
655 unsigned nonce,
656 METHOD_DATA *md)
658 struct ContentInfo content_info;
659 krb5_error_code ret;
660 const heim_oid *oid = NULL;
661 size_t size = 0;
662 krb5_data buf, sd_buf;
663 int pa_type = -1;
665 krb5_data_zero(&buf);
666 krb5_data_zero(&sd_buf);
667 memset(&content_info, 0, sizeof(content_info));
669 if (ctx->type == PKINIT_WIN2K) {
670 AuthPack_Win2k ap;
671 krb5_timestamp sec;
672 int32_t usec;
674 memset(&ap, 0, sizeof(ap));
676 /* fill in PKAuthenticator */
677 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
678 if (ret) {
679 free_AuthPack_Win2k(&ap);
680 krb5_clear_error_message(context);
681 goto out;
683 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
684 if (ret) {
685 free_AuthPack_Win2k(&ap);
686 krb5_clear_error_message(context);
687 goto out;
690 krb5_us_timeofday(context, &sec, &usec);
691 ap.pkAuthenticator.ctime = sec;
692 ap.pkAuthenticator.cusec = usec;
693 ap.pkAuthenticator.nonce = nonce;
695 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
696 &ap, &size, ret);
697 free_AuthPack_Win2k(&ap);
698 if (ret) {
699 krb5_set_error_message(context, ret,
700 N_("Failed encoding AuthPackWin: %d", ""),
701 (int)ret);
702 goto out;
704 if (buf.length != size)
705 krb5_abortx(context, "internal ASN1 encoder error");
707 oid = &asn1_oid_id_pkcs7_data;
708 } else if (ctx->type == PKINIT_27) {
709 AuthPack ap;
711 memset(&ap, 0, sizeof(ap));
713 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
714 if (ret) {
715 free_AuthPack(&ap);
716 goto out;
719 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
720 free_AuthPack(&ap);
721 if (ret) {
722 krb5_set_error_message(context, ret,
723 N_("Failed encoding AuthPack: %d", ""),
724 (int)ret);
725 goto out;
727 if (buf.length != size)
728 krb5_abortx(context, "internal ASN1 encoder error");
730 oid = &asn1_oid_id_pkauthdata;
731 } else
732 krb5_abortx(context, "internal pkinit error");
734 ret = create_signature(context, oid, &buf, ctx->id,
735 ctx->peer, &sd_buf);
736 krb5_data_free(&buf);
737 if (ret)
738 goto out;
740 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
741 krb5_data_free(&sd_buf);
742 if (ret) {
743 krb5_set_error_message(context, ret,
744 N_("ContentInfo wrapping of signedData failed",""));
745 goto out;
748 if (ctx->type == PKINIT_WIN2K) {
749 PA_PK_AS_REQ_Win2k winreq;
751 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
753 memset(&winreq, 0, sizeof(winreq));
755 winreq.signed_auth_pack = buf;
757 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
758 &winreq, &size, ret);
759 free_PA_PK_AS_REQ_Win2k(&winreq);
761 } else if (ctx->type == PKINIT_27) {
762 PA_PK_AS_REQ req;
764 pa_type = KRB5_PADATA_PK_AS_REQ;
766 memset(&req, 0, sizeof(req));
767 req.signedAuthPack = buf;
769 if (ctx->trustedCertifiers) {
771 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
772 if (req.trustedCertifiers == NULL) {
773 ret = ENOMEM;
774 krb5_set_error_message(context, ret,
775 N_("malloc: out of memory", ""));
776 free_PA_PK_AS_REQ(&req);
777 goto out;
779 ret = build_edi(context, context->hx509ctx,
780 ctx->id->anchors, req.trustedCertifiers);
781 if (ret) {
782 krb5_set_error_message(context, ret,
783 N_("pk-init: failed to build "
784 "trustedCertifiers", ""));
785 free_PA_PK_AS_REQ(&req);
786 goto out;
789 req.kdcPkId = NULL;
791 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
792 &req, &size, ret);
794 free_PA_PK_AS_REQ(&req);
796 } else
797 krb5_abortx(context, "internal pkinit error");
798 if (ret) {
799 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
800 goto out;
802 if (buf.length != size)
803 krb5_abortx(context, "Internal ASN1 encoder error");
805 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
806 if (ret)
807 free(buf.data);
809 if (ret == 0)
810 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
812 out:
813 free_ContentInfo(&content_info);
815 return ret;
819 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
820 _krb5_pk_mk_padata(krb5_context context,
821 void *c,
822 int ic_flags,
823 int win2k,
824 const KDC_REQ_BODY *req_body,
825 unsigned nonce,
826 METHOD_DATA *md)
828 krb5_pk_init_ctx ctx = c;
829 int win2k_compat;
831 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
832 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
833 N_("PKINIT: No user certificate given", ""));
834 return HEIM_PKINIT_NO_PRIVATE_KEY;
837 win2k_compat = krb5_config_get_bool_default(context, NULL,
838 win2k,
839 "realms",
840 req_body->realm,
841 "pkinit_win2k",
842 NULL);
844 if (win2k_compat) {
845 ctx->require_binding =
846 krb5_config_get_bool_default(context, NULL,
847 TRUE,
848 "realms",
849 req_body->realm,
850 "pkinit_win2k_require_binding",
851 NULL);
852 ctx->type = PKINIT_WIN2K;
853 } else
854 ctx->type = PKINIT_27;
856 ctx->require_eku =
857 krb5_config_get_bool_default(context, NULL,
858 TRUE,
859 "realms",
860 req_body->realm,
861 "pkinit_require_eku",
862 NULL);
863 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
864 ctx->require_eku = 0;
865 if (ctx->id->flags & PKINIT_BTMM)
866 ctx->require_eku = 0;
868 ctx->require_krbtgt_otherName =
869 krb5_config_get_bool_default(context, NULL,
870 TRUE,
871 "realms",
872 req_body->realm,
873 "pkinit_require_krbtgt_otherName",
874 NULL);
876 ctx->require_hostname_match =
877 krb5_config_get_bool_default(context, NULL,
878 FALSE,
879 "realms",
880 req_body->realm,
881 "pkinit_require_hostname_match",
882 NULL);
884 ctx->trustedCertifiers =
885 krb5_config_get_bool_default(context, NULL,
886 TRUE,
887 "realms",
888 req_body->realm,
889 "pkinit_trustedCertifiers",
890 NULL);
892 return pk_mk_padata(context, ctx, req_body, nonce, md);
895 static krb5_error_code
896 pk_verify_sign(krb5_context context,
897 const void *data,
898 size_t length,
899 struct krb5_pk_identity *id,
900 heim_oid *contentType,
901 krb5_data *content,
902 struct krb5_pk_cert **signer)
904 hx509_certs signer_certs;
905 int ret, flags = 0;
907 /* BTMM is broken in Leo and SnowLeo */
908 if (id->flags & PKINIT_BTMM) {
909 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
910 flags |= HX509_CMS_VS_NO_KU_CHECK;
911 flags |= HX509_CMS_VS_NO_VALIDATE;
914 *signer = NULL;
916 ret = hx509_cms_verify_signed(context->hx509ctx,
917 id->verify_ctx,
918 flags,
919 data,
920 length,
921 NULL,
922 id->certpool,
923 contentType,
924 content,
925 &signer_certs);
926 if (ret) {
927 pk_copy_error(context, context->hx509ctx, ret,
928 "CMS verify signed failed");
929 return ret;
932 *signer = calloc(1, sizeof(**signer));
933 if (*signer == NULL) {
934 krb5_clear_error_message(context);
935 ret = ENOMEM;
936 goto out;
939 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
940 if (ret) {
941 pk_copy_error(context, context->hx509ctx, ret,
942 "Failed to get on of the signer certs");
943 goto out;
946 out:
947 hx509_certs_free(&signer_certs);
948 if (ret) {
949 if (*signer) {
950 hx509_cert_free((*signer)->cert);
951 free(*signer);
952 *signer = NULL;
956 return ret;
959 static krb5_error_code
960 get_reply_key_win(krb5_context context,
961 const krb5_data *content,
962 unsigned nonce,
963 krb5_keyblock **key)
965 ReplyKeyPack_Win2k key_pack;
966 krb5_error_code ret;
967 size_t size;
969 ret = decode_ReplyKeyPack_Win2k(content->data,
970 content->length,
971 &key_pack,
972 &size);
973 if (ret) {
974 krb5_set_error_message(context, ret,
975 N_("PKINIT decoding reply key failed", ""));
976 free_ReplyKeyPack_Win2k(&key_pack);
977 return ret;
980 if ((unsigned)key_pack.nonce != nonce) {
981 krb5_set_error_message(context, ret,
982 N_("PKINIT enckey nonce is wrong", ""));
983 free_ReplyKeyPack_Win2k(&key_pack);
984 return KRB5KRB_AP_ERR_MODIFIED;
987 *key = malloc (sizeof (**key));
988 if (*key == NULL) {
989 free_ReplyKeyPack_Win2k(&key_pack);
990 krb5_set_error_message(context, ENOMEM,
991 N_("malloc: out of memory", ""));
992 return ENOMEM;
995 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
996 free_ReplyKeyPack_Win2k(&key_pack);
997 if (ret) {
998 krb5_set_error_message(context, ret,
999 N_("PKINIT failed copying reply key", ""));
1000 free(*key);
1001 *key = NULL;
1004 return ret;
1007 static krb5_error_code
1008 get_reply_key(krb5_context context,
1009 const krb5_data *content,
1010 const krb5_data *req_buffer,
1011 krb5_keyblock **key)
1013 ReplyKeyPack key_pack;
1014 krb5_error_code ret;
1015 size_t size;
1017 ret = decode_ReplyKeyPack(content->data,
1018 content->length,
1019 &key_pack,
1020 &size);
1021 if (ret) {
1022 krb5_set_error_message(context, ret,
1023 N_("PKINIT decoding reply key failed", ""));
1024 free_ReplyKeyPack(&key_pack);
1025 return ret;
1029 krb5_crypto crypto;
1032 * XXX Verify kp.replyKey is a allowed enctype in the
1033 * configuration file
1036 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1037 if (ret) {
1038 free_ReplyKeyPack(&key_pack);
1039 return ret;
1042 ret = krb5_verify_checksum(context, crypto, 6,
1043 req_buffer->data, req_buffer->length,
1044 &key_pack.asChecksum);
1045 krb5_crypto_destroy(context, crypto);
1046 if (ret) {
1047 free_ReplyKeyPack(&key_pack);
1048 return ret;
1052 *key = malloc (sizeof (**key));
1053 if (*key == NULL) {
1054 free_ReplyKeyPack(&key_pack);
1055 krb5_set_error_message(context, ENOMEM,
1056 N_("malloc: out of memory", ""));
1057 return ENOMEM;
1060 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1061 free_ReplyKeyPack(&key_pack);
1062 if (ret) {
1063 krb5_set_error_message(context, ret,
1064 N_("PKINIT failed copying reply key", ""));
1065 free(*key);
1066 *key = NULL;
1069 return ret;
1073 static krb5_error_code
1074 pk_verify_host(krb5_context context,
1075 const char *realm,
1076 const krb5_krbhst_info *hi,
1077 struct krb5_pk_init_ctx_data *ctx,
1078 struct krb5_pk_cert *host)
1080 krb5_error_code ret = 0;
1082 if (ctx->require_eku) {
1083 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1084 &asn1_oid_id_pkkdcekuoid, 0);
1085 if (ret) {
1086 krb5_set_error_message(context, ret,
1087 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1088 return ret;
1091 if (ctx->require_krbtgt_otherName) {
1092 hx509_octet_string_list list;
1093 size_t i;
1095 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1096 host->cert,
1097 &asn1_oid_id_pkinit_san,
1098 &list);
1099 if (ret) {
1100 krb5_set_error_message(context, ret,
1101 N_("Failed to find the PK-INIT "
1102 "subjectAltName in the KDC "
1103 "certificate", ""));
1105 return ret;
1108 for (i = 0; i < list.len; i++) {
1109 KRB5PrincipalName r;
1111 ret = decode_KRB5PrincipalName(list.val[i].data,
1112 list.val[i].length,
1114 NULL);
1115 if (ret) {
1116 krb5_set_error_message(context, ret,
1117 N_("Failed to decode the PK-INIT "
1118 "subjectAltName in the "
1119 "KDC certificate", ""));
1121 break;
1124 if (r.principalName.name_string.len != 2 ||
1125 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1126 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1127 strcmp(r.realm, realm) != 0)
1129 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1130 krb5_set_error_message(context, ret,
1131 N_("KDC have wrong realm name in "
1132 "the certificate", ""));
1135 free_KRB5PrincipalName(&r);
1136 if (ret)
1137 break;
1139 hx509_free_octet_string_list(&list);
1141 if (ret)
1142 return ret;
1144 if (hi) {
1145 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1146 ctx->require_hostname_match,
1147 HX509_HN_HOSTNAME,
1148 hi->hostname,
1149 hi->ai->ai_addr, hi->ai->ai_addrlen);
1151 if (ret)
1152 krb5_set_error_message(context, ret,
1153 N_("Address mismatch in "
1154 "the KDC certificate", ""));
1156 return ret;
1159 static krb5_error_code
1160 pk_rd_pa_reply_enckey(krb5_context context,
1161 int type,
1162 const heim_octet_string *indata,
1163 const heim_oid *dataType,
1164 const char *realm,
1165 krb5_pk_init_ctx ctx,
1166 krb5_enctype etype,
1167 const krb5_krbhst_info *hi,
1168 unsigned nonce,
1169 const krb5_data *req_buffer,
1170 PA_DATA *pa,
1171 krb5_keyblock **key)
1173 krb5_error_code ret;
1174 struct krb5_pk_cert *host = NULL;
1175 krb5_data content;
1176 heim_oid contentType = { 0, NULL };
1177 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1179 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1180 krb5_set_error_message(context, EINVAL,
1181 N_("PKINIT: Invalid content type", ""));
1182 return EINVAL;
1185 if (ctx->type == PKINIT_WIN2K)
1186 flags |= HX509_CMS_UE_ALLOW_WEAK;
1188 ret = hx509_cms_unenvelope(context->hx509ctx,
1189 ctx->id->certs,
1190 flags,
1191 indata->data,
1192 indata->length,
1193 NULL,
1195 &contentType,
1196 &content);
1197 if (ret) {
1198 pk_copy_error(context, context->hx509ctx, ret,
1199 "Failed to unenvelope CMS data in PK-INIT reply");
1200 return ret;
1202 der_free_oid(&contentType);
1204 /* win2k uses ContentInfo */
1205 if (type == PKINIT_WIN2K) {
1206 heim_oid type2;
1207 heim_octet_string out;
1209 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1210 if (ret) {
1211 /* windows LH with interesting CMS packets */
1212 size_t ph = 1 + der_length_len(content.length);
1213 unsigned char *ptr = malloc(content.length + ph);
1214 size_t l;
1216 memcpy(ptr + ph, content.data, content.length);
1218 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1219 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1220 if (ret)
1221 return ret;
1222 free(content.data);
1223 content.data = ptr;
1224 content.length += ph;
1226 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1227 if (ret)
1228 goto out;
1230 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1231 ret = EINVAL; /* XXX */
1232 krb5_set_error_message(context, ret,
1233 N_("PKINIT: Invalid content type", ""));
1234 der_free_oid(&type2);
1235 der_free_octet_string(&out);
1236 goto out;
1238 der_free_oid(&type2);
1239 krb5_data_free(&content);
1240 ret = krb5_data_copy(&content, out.data, out.length);
1241 der_free_octet_string(&out);
1242 if (ret) {
1243 krb5_set_error_message(context, ret,
1244 N_("malloc: out of memory", ""));
1245 goto out;
1249 ret = pk_verify_sign(context,
1250 content.data,
1251 content.length,
1252 ctx->id,
1253 &contentType,
1254 &content,
1255 &host);
1256 if (ret)
1257 goto out;
1259 /* make sure that it is the kdc's certificate */
1260 ret = pk_verify_host(context, realm, hi, ctx, host);
1261 if (ret) {
1262 goto out;
1265 #if 0
1266 if (type == PKINIT_WIN2K) {
1267 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1268 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1269 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1270 goto out;
1272 } else {
1273 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1274 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1275 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1276 goto out;
1279 #endif
1281 switch(type) {
1282 case PKINIT_WIN2K:
1283 ret = get_reply_key(context, &content, req_buffer, key);
1284 if (ret != 0 && ctx->require_binding == 0)
1285 ret = get_reply_key_win(context, &content, nonce, key);
1286 break;
1287 case PKINIT_27:
1288 ret = get_reply_key(context, &content, req_buffer, key);
1289 break;
1291 if (ret)
1292 goto out;
1294 /* XXX compare given etype with key->etype */
1296 out:
1297 if (host)
1298 _krb5_pk_cert_free(host);
1299 der_free_oid(&contentType);
1300 krb5_data_free(&content);
1302 return ret;
1305 static krb5_error_code
1306 pk_rd_pa_reply_dh(krb5_context context,
1307 const heim_octet_string *indata,
1308 const heim_oid *dataType,
1309 const char *realm,
1310 krb5_pk_init_ctx ctx,
1311 krb5_enctype etype,
1312 const krb5_krbhst_info *hi,
1313 const DHNonce *c_n,
1314 const DHNonce *k_n,
1315 unsigned nonce,
1316 PA_DATA *pa,
1317 krb5_keyblock **key)
1319 const unsigned char *p;
1320 unsigned char *dh_gen_key = NULL;
1321 struct krb5_pk_cert *host = NULL;
1322 BIGNUM *kdc_dh_pubkey = NULL;
1323 KDCDHKeyInfo kdc_dh_info;
1324 heim_oid contentType = { 0, NULL };
1325 krb5_data content;
1326 krb5_error_code ret;
1327 int dh_gen_keylen = 0;
1328 size_t size;
1330 krb5_data_zero(&content);
1331 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1333 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1334 krb5_set_error_message(context, EINVAL,
1335 N_("PKINIT: Invalid content type", ""));
1336 return EINVAL;
1339 ret = pk_verify_sign(context,
1340 indata->data,
1341 indata->length,
1342 ctx->id,
1343 &contentType,
1344 &content,
1345 &host);
1346 if (ret)
1347 goto out;
1349 /* make sure that it is the kdc's certificate */
1350 ret = pk_verify_host(context, realm, hi, ctx, host);
1351 if (ret)
1352 goto out;
1354 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1355 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1356 krb5_set_error_message(context, ret,
1357 N_("pkinit - dh reply contains wrong oid", ""));
1358 goto out;
1361 ret = decode_KDCDHKeyInfo(content.data,
1362 content.length,
1363 &kdc_dh_info,
1364 &size);
1366 if (ret) {
1367 krb5_set_error_message(context, ret,
1368 N_("pkinit - failed to decode "
1369 "KDC DH Key Info", ""));
1370 goto out;
1373 if (kdc_dh_info.nonce != nonce) {
1374 ret = KRB5KRB_AP_ERR_MODIFIED;
1375 krb5_set_error_message(context, ret,
1376 N_("PKINIT: DH nonce is wrong", ""));
1377 goto out;
1380 if (kdc_dh_info.dhKeyExpiration) {
1381 if (k_n == NULL) {
1382 ret = KRB5KRB_ERR_GENERIC;
1383 krb5_set_error_message(context, ret,
1384 N_("pkinit; got key expiration "
1385 "without server nonce", ""));
1386 goto out;
1388 if (c_n == NULL) {
1389 ret = KRB5KRB_ERR_GENERIC;
1390 krb5_set_error_message(context, ret,
1391 N_("pkinit; got DH reuse but no "
1392 "client nonce", ""));
1393 goto out;
1395 } else {
1396 if (k_n) {
1397 ret = KRB5KRB_ERR_GENERIC;
1398 krb5_set_error_message(context, ret,
1399 N_("pkinit: got server nonce "
1400 "without key expiration", ""));
1401 goto out;
1403 c_n = NULL;
1407 p = kdc_dh_info.subjectPublicKey.data;
1408 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1410 if (ctx->keyex == USE_DH) {
1411 DHPublicKey k;
1412 ret = decode_DHPublicKey(p, size, &k, NULL);
1413 if (ret) {
1414 krb5_set_error_message(context, ret,
1415 N_("pkinit: can't decode "
1416 "without key expiration", ""));
1417 goto out;
1420 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1421 free_DHPublicKey(&k);
1422 if (kdc_dh_pubkey == NULL) {
1423 ret = ENOMEM;
1424 goto out;
1428 size = DH_size(ctx->u.dh);
1430 dh_gen_key = malloc(size);
1431 if (dh_gen_key == NULL) {
1432 ret = ENOMEM;
1433 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1434 goto out;
1437 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1438 if (dh_gen_keylen == -1) {
1439 ret = KRB5KRB_ERR_GENERIC;
1440 dh_gen_keylen = 0;
1441 krb5_set_error_message(context, ret,
1442 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1443 goto out;
1445 if (dh_gen_keylen < (int)size) {
1446 size -= dh_gen_keylen;
1447 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1448 memset(dh_gen_key, 0, size);
1451 } else {
1452 #ifdef HAVE_OPENSSL
1453 const EC_GROUP *group;
1454 EC_KEY *public = NULL;
1456 group = EC_KEY_get0_group(ctx->u.eckey);
1458 public = EC_KEY_new();
1459 if (public == NULL) {
1460 ret = ENOMEM;
1461 goto out;
1463 if (EC_KEY_set_group(public, group) != 1) {
1464 EC_KEY_free(public);
1465 ret = ENOMEM;
1466 goto out;
1469 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1470 EC_KEY_free(public);
1471 ret = KRB5KRB_ERR_GENERIC;
1472 krb5_set_error_message(context, ret,
1473 N_("PKINIT: Can't parse ECDH public key", ""));
1474 goto out;
1477 size = (EC_GROUP_get_degree(group) + 7) / 8;
1478 dh_gen_key = malloc(size);
1479 if (dh_gen_key == NULL) {
1480 EC_KEY_free(public);
1481 ret = ENOMEM;
1482 krb5_set_error_message(context, ret,
1483 N_("malloc: out of memory", ""));
1484 goto out;
1486 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1487 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1488 EC_KEY_free(public);
1489 if (dh_gen_keylen == -1) {
1490 ret = KRB5KRB_ERR_GENERIC;
1491 dh_gen_keylen = 0;
1492 krb5_set_error_message(context, ret,
1493 N_("PKINIT: Can't compute ECDH public key", ""));
1494 goto out;
1496 #else
1497 ret = EINVAL;
1498 #endif
1501 if (dh_gen_keylen <= 0) {
1502 ret = EINVAL;
1503 krb5_set_error_message(context, ret,
1504 N_("PKINIT: resulting DH key <= 0", ""));
1505 dh_gen_keylen = 0;
1506 goto out;
1509 *key = malloc (sizeof (**key));
1510 if (*key == NULL) {
1511 ret = ENOMEM;
1512 krb5_set_error_message(context, ret,
1513 N_("malloc: out of memory", ""));
1514 goto out;
1517 ret = _krb5_pk_octetstring2key(context,
1518 etype,
1519 dh_gen_key, dh_gen_keylen,
1520 c_n, k_n,
1521 *key);
1522 if (ret) {
1523 krb5_set_error_message(context, ret,
1524 N_("PKINIT: can't create key from DH key", ""));
1525 free(*key);
1526 *key = NULL;
1527 goto out;
1530 out:
1531 if (kdc_dh_pubkey)
1532 BN_free(kdc_dh_pubkey);
1533 if (dh_gen_key) {
1534 memset(dh_gen_key, 0, dh_gen_keylen);
1535 free(dh_gen_key);
1537 if (host)
1538 _krb5_pk_cert_free(host);
1539 if (content.data)
1540 krb5_data_free(&content);
1541 der_free_oid(&contentType);
1542 free_KDCDHKeyInfo(&kdc_dh_info);
1544 return ret;
1547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1548 _krb5_pk_rd_pa_reply(krb5_context context,
1549 const char *realm,
1550 void *c,
1551 krb5_enctype etype,
1552 const krb5_krbhst_info *hi,
1553 unsigned nonce,
1554 const krb5_data *req_buffer,
1555 PA_DATA *pa,
1556 krb5_keyblock **key)
1558 krb5_pk_init_ctx ctx = c;
1559 krb5_error_code ret;
1560 size_t size;
1562 /* Check for IETF PK-INIT first */
1563 if (ctx->type == PKINIT_27) {
1564 PA_PK_AS_REP rep;
1565 heim_octet_string os, data;
1566 heim_oid oid;
1568 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1569 krb5_set_error_message(context, EINVAL,
1570 N_("PKINIT: wrong padata recv", ""));
1571 return EINVAL;
1574 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1575 pa->padata_value.length,
1576 &rep,
1577 &size);
1578 if (ret) {
1579 krb5_set_error_message(context, ret,
1580 N_("Failed to decode pkinit AS rep", ""));
1581 return ret;
1584 switch (rep.element) {
1585 case choice_PA_PK_AS_REP_dhInfo:
1586 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1587 os = rep.u.dhInfo.dhSignedData;
1588 break;
1589 case choice_PA_PK_AS_REP_encKeyPack:
1590 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1591 os = rep.u.encKeyPack;
1592 break;
1593 default: {
1594 PA_PK_AS_REP_BTMM btmm;
1595 free_PA_PK_AS_REP(&rep);
1596 memset(&rep, 0, sizeof(rep));
1598 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1600 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1601 pa->padata_value.length,
1602 &btmm,
1603 &size);
1604 if (ret) {
1605 krb5_set_error_message(context, EINVAL,
1606 N_("PKINIT: -27 reply "
1607 "invalid content type", ""));
1608 return EINVAL;
1611 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1612 free_PA_PK_AS_REP_BTMM(&btmm);
1613 ret = EINVAL;
1614 krb5_set_error_message(context, ret,
1615 N_("DH mode not supported for BTMM mode", ""));
1616 return ret;
1620 * Transform to IETF style PK-INIT reply so that free works below
1623 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1624 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1625 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1626 btmm.encKeyPack->data = NULL;
1627 btmm.encKeyPack->length = 0;
1628 free_PA_PK_AS_REP_BTMM(&btmm);
1629 os = rep.u.encKeyPack;
1633 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1634 if (ret) {
1635 free_PA_PK_AS_REP(&rep);
1636 krb5_set_error_message(context, ret,
1637 N_("PKINIT: failed to unwrap CI", ""));
1638 return ret;
1641 switch (rep.element) {
1642 case choice_PA_PK_AS_REP_dhInfo:
1643 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1644 ctx->clientDHNonce,
1645 rep.u.dhInfo.serverDHNonce,
1646 nonce, pa, key);
1647 break;
1648 case choice_PA_PK_AS_REP_encKeyPack:
1649 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1650 ctx, etype, hi, nonce, req_buffer, pa, key);
1651 break;
1652 default:
1653 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1655 der_free_octet_string(&data);
1656 der_free_oid(&oid);
1657 free_PA_PK_AS_REP(&rep);
1659 } else if (ctx->type == PKINIT_WIN2K) {
1660 PA_PK_AS_REP_Win2k w2krep;
1662 /* Check for Windows encoding of the AS-REP pa data */
1664 #if 0 /* should this be ? */
1665 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1666 krb5_set_error_message(context, EINVAL,
1667 "PKINIT: wrong padata recv");
1668 return EINVAL;
1670 #endif
1672 memset(&w2krep, 0, sizeof(w2krep));
1674 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1675 pa->padata_value.length,
1676 &w2krep,
1677 &size);
1678 if (ret) {
1679 krb5_set_error_message(context, ret,
1680 N_("PKINIT: Failed decoding windows "
1681 "pkinit reply %d", ""), (int)ret);
1682 return ret;
1685 krb5_clear_error_message(context);
1687 switch (w2krep.element) {
1688 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1689 heim_octet_string data;
1690 heim_oid oid;
1692 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1693 &oid, &data, NULL);
1694 free_PA_PK_AS_REP_Win2k(&w2krep);
1695 if (ret) {
1696 krb5_set_error_message(context, ret,
1697 N_("PKINIT: failed to unwrap CI", ""));
1698 return ret;
1701 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1702 ctx, etype, hi, nonce, req_buffer, pa, key);
1703 der_free_octet_string(&data);
1704 der_free_oid(&oid);
1706 break;
1708 default:
1709 free_PA_PK_AS_REP_Win2k(&w2krep);
1710 ret = EINVAL;
1711 krb5_set_error_message(context, ret,
1712 N_("PKINIT: win2k reply invalid "
1713 "content type", ""));
1714 break;
1717 } else {
1718 ret = EINVAL;
1719 krb5_set_error_message(context, ret,
1720 N_("PKINIT: unknown reply type", ""));
1723 return ret;
1726 struct prompter {
1727 krb5_context context;
1728 krb5_prompter_fct prompter;
1729 void *prompter_data;
1732 static int
1733 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1735 krb5_error_code ret;
1736 krb5_prompt prompt;
1737 krb5_data password_data;
1738 struct prompter *p = data;
1740 password_data.data = prompter->reply.data;
1741 password_data.length = prompter->reply.length;
1743 prompt.prompt = prompter->prompt;
1744 prompt.hidden = hx509_prompt_hidden(prompter->type);
1745 prompt.reply = &password_data;
1747 switch (prompter->type) {
1748 case HX509_PROMPT_TYPE_INFO:
1749 prompt.type = KRB5_PROMPT_TYPE_INFO;
1750 break;
1751 case HX509_PROMPT_TYPE_PASSWORD:
1752 case HX509_PROMPT_TYPE_QUESTION:
1753 default:
1754 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1755 break;
1758 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1759 if (ret) {
1760 memset (prompter->reply.data, 0, prompter->reply.length);
1761 return 1;
1763 return 0;
1766 static krb5_error_code
1767 _krb5_pk_set_user_id(krb5_context context,
1768 krb5_principal principal,
1769 krb5_pk_init_ctx ctx,
1770 struct hx509_certs_data *certs)
1772 hx509_certs c = hx509_certs_ref(certs);
1773 hx509_query *q = NULL;
1774 int ret;
1776 if (ctx->id->certs)
1777 hx509_certs_free(&ctx->id->certs);
1778 if (ctx->id->cert) {
1779 hx509_cert_free(ctx->id->cert);
1780 ctx->id->cert = NULL;
1783 ctx->id->certs = c;
1784 ctx->anonymous = 0;
1786 ret = hx509_query_alloc(context->hx509ctx, &q);
1787 if (ret) {
1788 pk_copy_error(context, context->hx509ctx, ret,
1789 "Allocate query to find signing certificate");
1790 return ret;
1793 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1794 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1796 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1797 ctx->id->flags |= PKINIT_BTMM;
1800 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1801 hx509_query_free(context->hx509ctx, q);
1803 if (ret == 0 && _krb5_have_debug(context, 2)) {
1804 hx509_name name;
1805 char *str, *sn;
1806 heim_integer i;
1808 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1809 if (ret)
1810 goto out;
1812 ret = hx509_name_to_string(name, &str);
1813 hx509_name_free(&name);
1814 if (ret)
1815 goto out;
1817 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1818 if (ret) {
1819 free(str);
1820 goto out;
1823 ret = der_print_hex_heim_integer(&i, &sn);
1824 der_free_heim_integer(&i);
1825 if (ret) {
1826 free(name);
1827 goto out;
1830 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1831 free(str);
1832 free(sn);
1834 out:
1836 return ret;
1839 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1840 _krb5_pk_load_id(krb5_context context,
1841 struct krb5_pk_identity **ret_id,
1842 const char *user_id,
1843 const char *anchor_id,
1844 char * const *chain_list,
1845 char * const *revoke_list,
1846 krb5_prompter_fct prompter,
1847 void *prompter_data,
1848 char *password)
1850 struct krb5_pk_identity *id = NULL;
1851 struct prompter p;
1852 int ret;
1854 *ret_id = NULL;
1856 if (anchor_id == NULL) {
1857 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1858 N_("PKINIT: No anchor given", ""));
1859 return HEIM_PKINIT_NO_VALID_CA;
1862 /* load cert */
1864 id = calloc(1, sizeof(*id));
1865 if (id == NULL) {
1866 krb5_set_error_message(context, ENOMEM,
1867 N_("malloc: out of memory", ""));
1868 return ENOMEM;
1871 if (user_id) {
1872 hx509_lock lock;
1874 ret = hx509_lock_init(context->hx509ctx, &lock);
1875 if (ret) {
1876 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1877 goto out;
1880 if (password && password[0])
1881 hx509_lock_add_password(lock, password);
1883 if (prompter) {
1884 p.context = context;
1885 p.prompter = prompter;
1886 p.prompter_data = prompter_data;
1888 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1889 if (ret) {
1890 hx509_lock_free(lock);
1891 goto out;
1895 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1896 hx509_lock_free(lock);
1897 if (ret) {
1898 pk_copy_error(context, context->hx509ctx, ret,
1899 "Failed to init cert certs");
1900 goto out;
1902 } else {
1903 id->certs = NULL;
1906 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1907 if (ret) {
1908 pk_copy_error(context, context->hx509ctx, ret,
1909 "Failed to init anchors");
1910 goto out;
1913 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1914 0, NULL, &id->certpool);
1915 if (ret) {
1916 pk_copy_error(context, context->hx509ctx, ret,
1917 "Failed to init chain");
1918 goto out;
1921 while (chain_list && *chain_list) {
1922 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1923 NULL, *chain_list);
1924 if (ret) {
1925 pk_copy_error(context, context->hx509ctx, ret,
1926 "Failed to laod chain %s",
1927 *chain_list);
1928 goto out;
1930 chain_list++;
1933 if (revoke_list) {
1934 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1935 if (ret) {
1936 pk_copy_error(context, context->hx509ctx, ret,
1937 "Failed init revoke list");
1938 goto out;
1941 while (*revoke_list) {
1942 ret = hx509_revoke_add_crl(context->hx509ctx,
1943 id->revokectx,
1944 *revoke_list);
1945 if (ret) {
1946 pk_copy_error(context, context->hx509ctx, ret,
1947 "Failed load revoke list");
1948 goto out;
1950 revoke_list++;
1952 } else
1953 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1955 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1956 if (ret) {
1957 pk_copy_error(context, context->hx509ctx, ret,
1958 "Failed init verify context");
1959 goto out;
1962 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1963 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1965 out:
1966 if (ret) {
1967 hx509_verify_destroy_ctx(id->verify_ctx);
1968 hx509_certs_free(&id->certs);
1969 hx509_certs_free(&id->anchors);
1970 hx509_certs_free(&id->certpool);
1971 hx509_revoke_free(&id->revokectx);
1972 free(id);
1973 } else
1974 *ret_id = id;
1976 return ret;
1983 static void
1984 pk_copy_error(krb5_context context,
1985 hx509_context hx509ctx,
1986 int hxret,
1987 const char *fmt,
1988 ...)
1990 va_list va;
1991 char *s, *f;
1992 int ret;
1994 va_start(va, fmt);
1995 ret = vasprintf(&f, fmt, va);
1996 va_end(va);
1997 if (ret == -1 || f == NULL) {
1998 krb5_clear_error_message(context);
1999 return;
2002 s = hx509_get_error_string(hx509ctx, hxret);
2003 if (s == NULL) {
2004 krb5_clear_error_message(context);
2005 free(f);
2006 return;
2008 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2009 free(s);
2010 free(f);
2013 static int
2014 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2015 const char *name, heim_integer *integer)
2017 int ret;
2018 char *p1;
2019 p1 = strsep(p, " \t");
2020 if (p1 == NULL) {
2021 krb5_set_error_message(context, EINVAL,
2022 N_("moduli file %s missing %s on line %d", ""),
2023 file, name, lineno);
2024 return EINVAL;
2026 ret = der_parse_hex_heim_integer(p1, integer);
2027 if (ret) {
2028 krb5_set_error_message(context, ret,
2029 N_("moduli file %s failed parsing %s "
2030 "on line %d", ""),
2031 file, name, lineno);
2032 return ret;
2035 return 0;
2038 krb5_error_code
2039 _krb5_parse_moduli_line(krb5_context context,
2040 const char *file,
2041 int lineno,
2042 char *p,
2043 struct krb5_dh_moduli **m)
2045 struct krb5_dh_moduli *m1;
2046 char *p1;
2047 int ret;
2049 *m = NULL;
2051 m1 = calloc(1, sizeof(*m1));
2052 if (m1 == NULL) {
2053 krb5_set_error_message(context, ENOMEM,
2054 N_("malloc: out of memory", ""));
2055 return ENOMEM;
2058 while (isspace((unsigned char)*p))
2059 p++;
2060 if (*p == '#') {
2061 free(m1);
2062 return 0;
2064 ret = EINVAL;
2066 p1 = strsep(&p, " \t");
2067 if (p1 == NULL) {
2068 krb5_set_error_message(context, ret,
2069 N_("moduli file %s missing name on line %d", ""),
2070 file, lineno);
2071 goto out;
2073 m1->name = strdup(p1);
2074 if (m1->name == NULL) {
2075 ret = ENOMEM;
2076 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2077 goto out;
2080 p1 = strsep(&p, " \t");
2081 if (p1 == NULL) {
2082 krb5_set_error_message(context, ret,
2083 N_("moduli file %s missing bits on line %d", ""),
2084 file, lineno);
2085 goto out;
2088 m1->bits = atoi(p1);
2089 if (m1->bits == 0) {
2090 krb5_set_error_message(context, ret,
2091 N_("moduli file %s have un-parsable "
2092 "bits on line %d", ""), file, lineno);
2093 goto out;
2096 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2097 if (ret)
2098 goto out;
2099 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2100 if (ret)
2101 goto out;
2102 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2103 if (ret)
2104 goto out;
2106 *m = m1;
2108 return 0;
2109 out:
2110 free(m1->name);
2111 der_free_heim_integer(&m1->p);
2112 der_free_heim_integer(&m1->g);
2113 der_free_heim_integer(&m1->q);
2114 free(m1);
2115 return ret;
2118 void
2119 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2121 int i;
2122 for (i = 0; moduli[i] != NULL; i++) {
2123 free(moduli[i]->name);
2124 der_free_heim_integer(&moduli[i]->p);
2125 der_free_heim_integer(&moduli[i]->g);
2126 der_free_heim_integer(&moduli[i]->q);
2127 free(moduli[i]);
2129 free(moduli);
2132 static const char *default_moduli_RFC2412_MODP_group2 =
2133 /* name */
2134 "RFC2412-MODP-group2 "
2135 /* bits */
2136 "1024 "
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" "ECE65381"
2143 "FFFFFFFF" "FFFFFFFF "
2144 /* g */
2145 "02 "
2146 /* q */
2147 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2148 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2149 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2150 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2151 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2152 "FFFFFFFF" "FFFFFFFF";
2154 static const char *default_moduli_rfc3526_MODP_group14 =
2155 /* name */
2156 "rfc3526-MODP-group14 "
2157 /* bits */
2158 "1760 "
2159 /* p */
2160 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2161 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2162 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2163 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2164 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2165 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2166 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2167 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2168 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2169 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2170 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2171 /* g */
2172 "02 "
2173 /* q */
2174 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2175 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2176 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2177 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2178 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2179 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2180 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2181 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2182 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2183 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2184 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2186 krb5_error_code
2187 _krb5_parse_moduli(krb5_context context, const char *file,
2188 struct krb5_dh_moduli ***moduli)
2190 /* name bits P G Q */
2191 krb5_error_code ret;
2192 struct krb5_dh_moduli **m = NULL, **m2;
2193 char buf[4096];
2194 FILE *f;
2195 int lineno = 0, n = 0;
2197 *moduli = NULL;
2199 m = calloc(1, sizeof(m[0]) * 3);
2200 if (m == NULL) {
2201 krb5_set_error_message(context, ENOMEM,
2202 N_("malloc: out of memory", ""));
2203 return ENOMEM;
2206 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2207 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2208 if (ret) {
2209 _krb5_free_moduli(m);
2210 return ret;
2212 n++;
2214 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2215 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2216 if (ret) {
2217 _krb5_free_moduli(m);
2218 return ret;
2220 n++;
2223 if (file == NULL)
2224 file = MODULI_FILE;
2226 #ifdef KRB5_USE_PATH_TOKENS
2228 char * exp_file;
2230 if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2231 f = fopen(exp_file, "r");
2232 krb5_xfree(exp_file);
2233 } else {
2234 f = NULL;
2237 #else
2238 f = fopen(file, "r");
2239 #endif
2241 if (f == NULL) {
2242 *moduli = m;
2243 return 0;
2245 rk_cloexec_file(f);
2247 while(fgets(buf, sizeof(buf), f) != NULL) {
2248 struct krb5_dh_moduli *element;
2250 buf[strcspn(buf, "\n")] = '\0';
2251 lineno++;
2253 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2254 if (m2 == NULL) {
2255 _krb5_free_moduli(m);
2256 krb5_set_error_message(context, ENOMEM,
2257 N_("malloc: out of memory", ""));
2258 return ENOMEM;
2260 m = m2;
2262 m[n] = NULL;
2264 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2265 if (ret) {
2266 _krb5_free_moduli(m);
2267 return ret;
2269 if (element == NULL)
2270 continue;
2272 m[n] = element;
2273 m[n + 1] = NULL;
2274 n++;
2276 *moduli = m;
2277 return 0;
2280 krb5_error_code
2281 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2282 heim_integer *p, heim_integer *g, heim_integer *q,
2283 struct krb5_dh_moduli **moduli,
2284 char **name)
2286 int i;
2288 if (name)
2289 *name = NULL;
2291 for (i = 0; moduli[i] != NULL; i++) {
2292 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2293 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2294 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2296 if (bits && bits > moduli[i]->bits) {
2297 krb5_set_error_message(context,
2298 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2299 N_("PKINIT: DH group parameter %s "
2300 "no accepted, not enough bits "
2301 "generated", ""),
2302 moduli[i]->name);
2303 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2305 if (name)
2306 *name = strdup(moduli[i]->name);
2307 return 0;
2310 krb5_set_error_message(context,
2311 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2312 N_("PKINIT: DH group parameter no ok", ""));
2313 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2315 #endif /* PKINIT */
2317 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2318 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2320 #ifdef PKINIT
2321 krb5_pk_init_ctx ctx;
2323 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2324 return;
2325 ctx = opt->opt_private->pk_init_ctx;
2326 switch (ctx->keyex) {
2327 case USE_DH:
2328 if (ctx->u.dh)
2329 DH_free(ctx->u.dh);
2330 break;
2331 case USE_RSA:
2332 break;
2333 case USE_ECDH:
2334 #ifdef HAVE_OPENSSL
2335 if (ctx->u.eckey)
2336 EC_KEY_free(ctx->u.eckey);
2337 #endif
2338 break;
2340 if (ctx->id) {
2341 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2342 hx509_certs_free(&ctx->id->certs);
2343 hx509_cert_free(ctx->id->cert);
2344 hx509_certs_free(&ctx->id->anchors);
2345 hx509_certs_free(&ctx->id->certpool);
2347 if (ctx->clientDHNonce) {
2348 krb5_free_data(NULL, ctx->clientDHNonce);
2349 ctx->clientDHNonce = NULL;
2351 if (ctx->m)
2352 _krb5_free_moduli(ctx->m);
2353 free(ctx->id);
2354 ctx->id = NULL;
2356 free(opt->opt_private->pk_init_ctx);
2357 opt->opt_private->pk_init_ctx = NULL;
2358 #endif
2361 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2362 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2363 krb5_get_init_creds_opt *opt,
2364 krb5_principal principal,
2365 const char *user_id,
2366 const char *x509_anchors,
2367 char * const * pool,
2368 char * const * pki_revoke,
2369 int flags,
2370 krb5_prompter_fct prompter,
2371 void *prompter_data,
2372 char *password)
2374 #ifdef PKINIT
2375 krb5_error_code ret;
2376 char *anchors = NULL;
2378 if (opt->opt_private == NULL) {
2379 krb5_set_error_message(context, EINVAL,
2380 N_("PKINIT: on non extendable opt", ""));
2381 return EINVAL;
2384 opt->opt_private->pk_init_ctx =
2385 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2386 if (opt->opt_private->pk_init_ctx == NULL) {
2387 krb5_set_error_message(context, ENOMEM,
2388 N_("malloc: out of memory", ""));
2389 return ENOMEM;
2391 opt->opt_private->pk_init_ctx->require_binding = 0;
2392 opt->opt_private->pk_init_ctx->require_eku = 1;
2393 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2394 opt->opt_private->pk_init_ctx->peer = NULL;
2396 /* XXX implement krb5_appdefault_strings */
2397 if (pool == NULL)
2398 pool = krb5_config_get_strings(context, NULL,
2399 "appdefaults",
2400 "pkinit_pool",
2401 NULL);
2403 if (pki_revoke == NULL)
2404 pki_revoke = krb5_config_get_strings(context, NULL,
2405 "appdefaults",
2406 "pkinit_revoke",
2407 NULL);
2409 if (x509_anchors == NULL) {
2410 krb5_appdefault_string(context, "kinit",
2411 krb5_principal_get_realm(context, principal),
2412 "pkinit_anchors", NULL, &anchors);
2413 x509_anchors = anchors;
2416 if (flags & 4)
2417 opt->opt_private->pk_init_ctx->anonymous = 1;
2419 ret = _krb5_pk_load_id(context,
2420 &opt->opt_private->pk_init_ctx->id,
2421 user_id,
2422 x509_anchors,
2423 pool,
2424 pki_revoke,
2425 prompter,
2426 prompter_data,
2427 password);
2428 if (ret) {
2429 free(opt->opt_private->pk_init_ctx);
2430 opt->opt_private->pk_init_ctx = NULL;
2431 return ret;
2434 if (opt->opt_private->pk_init_ctx->id->certs) {
2435 _krb5_pk_set_user_id(context,
2436 principal,
2437 opt->opt_private->pk_init_ctx,
2438 opt->opt_private->pk_init_ctx->id->certs);
2439 } else
2440 opt->opt_private->pk_init_ctx->id->cert = NULL;
2442 if ((flags & 2) == 0) {
2443 hx509_context hx509ctx = context->hx509ctx;
2444 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2446 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2449 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2451 if (cert) {
2452 AlgorithmIdentifier alg;
2454 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2455 if (ret == 0) {
2456 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2457 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2458 free_AlgorithmIdentifier(&alg);
2462 } else {
2463 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2465 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2466 krb5_set_error_message(context, EINVAL,
2467 N_("No anonymous pkinit support in RSA mode", ""));
2468 return EINVAL;
2472 return 0;
2473 #else
2474 krb5_set_error_message(context, EINVAL,
2475 N_("no support for PKINIT compiled in", ""));
2476 return EINVAL;
2477 #endif
2480 krb5_error_code KRB5_LIB_FUNCTION
2481 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2482 krb5_get_init_creds_opt *opt,
2483 struct hx509_certs_data *certs)
2485 #ifdef PKINIT
2486 if (opt->opt_private == NULL) {
2487 krb5_set_error_message(context, EINVAL,
2488 N_("PKINIT: on non extendable opt", ""));
2489 return EINVAL;
2491 if (opt->opt_private->pk_init_ctx == NULL) {
2492 krb5_set_error_message(context, EINVAL,
2493 N_("PKINIT: on pkinit context", ""));
2494 return EINVAL;
2497 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2499 return 0;
2500 #else
2501 krb5_set_error_message(context, EINVAL,
2502 N_("no support for PKINIT compiled in", ""));
2503 return EINVAL;
2504 #endif
2507 #ifdef PKINIT
2509 static int
2510 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2512 hx509_octet_string_list list;
2513 int ret;
2515 *upn = NULL;
2517 ret = hx509_cert_find_subjectAltName_otherName(context,
2518 cert,
2519 &asn1_oid_id_pkinit_ms_san,
2520 &list);
2521 if (ret)
2522 return 0;
2524 if (list.len > 0 && list.val[0].length > 0)
2525 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2526 upn, NULL);
2527 else
2528 ret = 1;
2529 hx509_free_octet_string_list(&list);
2531 return ret;
2534 static int
2535 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2537 char *upn;
2538 int ret;
2540 ret = get_ms_san(context, cert, &upn);
2541 if (ret == 0)
2542 free(upn);
2543 return ret;
2548 #endif
2551 * Private since it need to be redesigned using krb5_get_init_creds()
2554 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2555 krb5_pk_enterprise_cert(krb5_context context,
2556 const char *user_id,
2557 krb5_const_realm realm,
2558 krb5_principal *principal,
2559 struct hx509_certs_data **res)
2561 #ifdef PKINIT
2562 krb5_error_code ret;
2563 hx509_certs certs, result;
2564 hx509_cert cert = NULL;
2565 hx509_query *q;
2566 char *name;
2568 *principal = NULL;
2569 if (res)
2570 *res = NULL;
2572 if (user_id == NULL) {
2573 krb5_set_error_message(context, ENOENT, "no user id");
2574 return ENOENT;
2577 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2578 if (ret) {
2579 pk_copy_error(context, context->hx509ctx, ret,
2580 "Failed to init cert certs");
2581 goto out;
2584 ret = hx509_query_alloc(context->hx509ctx, &q);
2585 if (ret) {
2586 krb5_set_error_message(context, ret, "out of memory");
2587 hx509_certs_free(&certs);
2588 goto out;
2591 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2592 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2593 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2594 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2596 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2597 hx509_query_free(context->hx509ctx, q);
2598 hx509_certs_free(&certs);
2599 if (ret) {
2600 pk_copy_error(context, context->hx509ctx, ret,
2601 "Failed to find PKINIT certificate");
2602 return ret;
2605 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2606 hx509_certs_free(&result);
2607 if (ret) {
2608 pk_copy_error(context, context->hx509ctx, ret,
2609 "Failed to get one cert");
2610 goto out;
2613 ret = get_ms_san(context->hx509ctx, cert, &name);
2614 if (ret) {
2615 pk_copy_error(context, context->hx509ctx, ret,
2616 "Failed to get MS SAN");
2617 goto out;
2620 ret = krb5_make_principal(context, principal, realm, name, NULL);
2621 free(name);
2622 if (ret)
2623 goto out;
2625 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2627 if (res) {
2628 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2629 if (ret)
2630 goto out;
2632 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2633 if (ret) {
2634 hx509_certs_free(res);
2635 goto out;
2639 out:
2640 hx509_cert_free(cert);
2642 return ret;
2643 #else
2644 krb5_set_error_message(context, EINVAL,
2645 N_("no support for PKINIT compiled in", ""));
2646 return EINVAL;
2647 #endif