s3: Add vfs_aio_posix
[Samba/gebeck_regimport.git] / source4 / heimdal / lib / krb5 / pkinit.c
blob1103a17807be9e1240339580775c203073669244
1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 struct krb5_dh_moduli {
39 char *name;
40 unsigned long bits;
41 heim_integer p;
42 heim_integer g;
43 heim_integer q;
46 #ifdef PKINIT
48 #include <cms_asn1.h>
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
53 #include <asn1_err.h>
55 #include <der.h>
57 struct krb5_pk_cert {
58 hx509_cert cert;
61 struct krb5_pk_init_ctx_data {
62 struct krb5_pk_identity *id;
63 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
64 union {
65 DH *dh;
66 #ifdef HAVE_OPENSSL
67 EC_KEY *eckey;
68 #endif
69 } u;
70 krb5_data *clientDHNonce;
71 struct krb5_dh_moduli **m;
72 hx509_peer_info peer;
73 enum krb5_pk_type type;
74 unsigned int require_binding:1;
75 unsigned int require_eku:1;
76 unsigned int require_krbtgt_otherName:1;
77 unsigned int require_hostname_match:1;
78 unsigned int trustedCertifiers:1;
79 unsigned int anonymous:1;
82 static void
83 pk_copy_error(krb5_context context,
84 hx509_context hx509ctx,
85 int hxret,
86 const char *fmt,
87 ...)
88 __attribute__ ((format (printf, 4, 5)));
94 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
95 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
97 if (cert->cert) {
98 hx509_cert_free(cert->cert);
100 free(cert);
103 static krb5_error_code
104 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
106 integer->length = BN_num_bytes(bn);
107 integer->data = malloc(integer->length);
108 if (integer->data == NULL) {
109 krb5_clear_error_message(context);
110 return ENOMEM;
112 BN_bn2bin(bn, integer->data);
113 integer->negative = BN_is_negative(bn);
114 return 0;
117 static BIGNUM *
118 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
120 BIGNUM *bn;
122 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
123 if (bn == NULL) {
124 krb5_set_error_message(context, ENOMEM,
125 N_("PKINIT: parsing BN failed %s", ""), field);
126 return NULL;
128 BN_set_negative(bn, f->negative);
129 return bn;
132 static krb5_error_code
133 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
134 struct krb5_dh_moduli **moduli)
136 const struct krb5_dh_moduli *m;
138 if (bits == 0) {
139 m = moduli[1]; /* XXX */
140 if (m == NULL)
141 m = moduli[0]; /* XXX */
142 } else {
143 int i;
144 for (i = 0; moduli[i] != NULL; i++) {
145 if (bits < moduli[i]->bits)
146 break;
148 if (moduli[i] == NULL) {
149 krb5_set_error_message(context, EINVAL,
150 N_("Did not find a DH group parameter "
151 "matching requirement of %lu bits", ""),
152 bits);
153 return EINVAL;
155 m = moduli[i];
158 dh->p = integer_to_BN(context, "p", &m->p);
159 if (dh->p == NULL)
160 return ENOMEM;
161 dh->g = integer_to_BN(context, "g", &m->g);
162 if (dh->g == NULL)
163 return ENOMEM;
164 dh->q = integer_to_BN(context, "q", &m->q);
165 if (dh->q == NULL)
166 return ENOMEM;
168 return 0;
171 struct certfind {
172 const char *type;
173 const heim_oid *oid;
177 * Try searchin the key by to use by first looking for for PK-INIT
178 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
181 static krb5_error_code
182 find_cert(krb5_context context, struct krb5_pk_identity *id,
183 hx509_query *q, hx509_cert *cert)
185 struct certfind cf[4] = {
186 { "MobileMe EKU" },
187 { "PKINIT EKU" },
188 { "MS EKU" },
189 { "any (or no)" }
191 int 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 krb5_set_error_message(context, ENOMEM,
403 N_("malloc: out of memory", ""));
404 return ENOMEM;
407 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
408 checksum.checksum.data, checksum.checksum.length);
409 free_Checksum(&checksum);
410 if (ret)
411 return ret;
413 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
414 const char *moduli_file;
415 unsigned long dh_min_bits;
416 krb5_data dhbuf;
417 size_t size = 0;
419 krb5_data_zero(&dhbuf);
423 moduli_file = krb5_config_get_string(context, NULL,
424 "libdefaults",
425 "moduli",
426 NULL);
428 dh_min_bits =
429 krb5_config_get_int_default(context, NULL, 0,
430 "libdefaults",
431 "pkinit_dh_min_bits",
432 NULL);
434 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
435 if (ret)
436 return ret;
438 ctx->u.dh = DH_new();
439 if (ctx->u.dh == NULL) {
440 krb5_set_error_message(context, ENOMEM,
441 N_("malloc: out of memory", ""));
442 return ENOMEM;
445 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
446 if (ret)
447 return ret;
449 if (DH_generate_key(ctx->u.dh) != 1) {
450 krb5_set_error_message(context, ENOMEM,
451 N_("pkinit: failed to generate DH key", ""));
452 return ENOMEM;
456 if (1 /* support_cached_dh */) {
457 ALLOC(a->clientDHNonce, 1);
458 if (a->clientDHNonce == NULL) {
459 krb5_clear_error_message(context);
460 return ENOMEM;
462 ret = krb5_data_alloc(a->clientDHNonce, 40);
463 if (a->clientDHNonce == NULL) {
464 krb5_clear_error_message(context);
465 return ret;
467 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
468 ret = krb5_copy_data(context, a->clientDHNonce,
469 &ctx->clientDHNonce);
470 if (ret)
471 return ret;
474 ALLOC(a->clientPublicValue, 1);
475 if (a->clientPublicValue == NULL)
476 return ENOMEM;
478 if (ctx->keyex == USE_DH) {
479 DH *dh = ctx->u.dh;
480 DomainParameters dp;
481 heim_integer dh_pub_key;
483 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
484 &a->clientPublicValue->algorithm.algorithm);
485 if (ret)
486 return ret;
488 memset(&dp, 0, sizeof(dp));
490 ret = BN_to_integer(context, dh->p, &dp.p);
491 if (ret) {
492 free_DomainParameters(&dp);
493 return ret;
495 ret = BN_to_integer(context, dh->g, &dp.g);
496 if (ret) {
497 free_DomainParameters(&dp);
498 return ret;
500 ret = BN_to_integer(context, dh->q, &dp.q);
501 if (ret) {
502 free_DomainParameters(&dp);
503 return ret;
505 dp.j = NULL;
506 dp.validationParms = NULL;
508 a->clientPublicValue->algorithm.parameters =
509 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
510 if (a->clientPublicValue->algorithm.parameters == NULL) {
511 free_DomainParameters(&dp);
512 return ret;
515 ASN1_MALLOC_ENCODE(DomainParameters,
516 a->clientPublicValue->algorithm.parameters->data,
517 a->clientPublicValue->algorithm.parameters->length,
518 &dp, &size, ret);
519 free_DomainParameters(&dp);
520 if (ret)
521 return ret;
522 if (size != a->clientPublicValue->algorithm.parameters->length)
523 krb5_abortx(context, "Internal ASN1 encoder error");
525 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
526 if (ret)
527 return ret;
529 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
530 &dh_pub_key, &size, ret);
531 der_free_heim_integer(&dh_pub_key);
532 if (ret)
533 return ret;
534 if (size != dhbuf.length)
535 krb5_abortx(context, "asn1 internal error");
536 } else if (ctx->keyex == USE_ECDH) {
537 #ifdef HAVE_OPENSSL
538 ECParameters ecp;
539 unsigned char *p;
540 int xlen;
542 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
544 ecp.element = choice_ECParameters_namedCurve;
545 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
546 &ecp.u.namedCurve);
547 if (ret)
548 return ret;
550 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
551 if (a->clientPublicValue->algorithm.parameters == NULL) {
552 free_ECParameters(&ecp);
553 return ENOMEM;
555 ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
556 free_ECParameters(&ecp);
557 if (ret)
558 return ret;
559 if ((int)size != xlen)
560 krb5_abortx(context, "asn1 internal error");
562 a->clientPublicValue->algorithm.parameters->data = p;
563 a->clientPublicValue->algorithm.parameters->length = size;
565 /* copy in public key */
567 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
568 &a->clientPublicValue->algorithm.algorithm);
569 if (ret)
570 return ret;
572 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
573 if (ctx->u.eckey == NULL)
574 return ENOMEM;
576 ret = EC_KEY_generate_key(ctx->u.eckey);
577 if (ret != 1)
578 return EINVAL;
580 /* encode onto dhkey */
582 xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
583 if (xlen <= 0)
584 abort();
586 dhbuf.data = malloc(xlen);
587 if (dhbuf.data == NULL)
588 abort();
589 dhbuf.length = xlen;
590 p = dhbuf.data;
592 xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
593 if (xlen <= 0)
594 abort();
596 /* XXX verify that this is right with RFC3279 */
597 #else
598 return EINVAL;
599 #endif
600 } else
601 krb5_abortx(context, "internal error");
602 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
603 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
607 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
608 if (a->supportedCMSTypes == NULL)
609 return ENOMEM;
611 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
612 ctx->id->cert,
613 &a->supportedCMSTypes->val,
614 &a->supportedCMSTypes->len);
615 if (ret)
616 return ret;
619 return ret;
622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
623 _krb5_pk_mk_ContentInfo(krb5_context context,
624 const krb5_data *buf,
625 const heim_oid *oid,
626 struct ContentInfo *content_info)
628 krb5_error_code ret;
630 ret = der_copy_oid(oid, &content_info->contentType);
631 if (ret)
632 return ret;
633 ALLOC(content_info->content, 1);
634 if (content_info->content == NULL)
635 return ENOMEM;
636 content_info->content->data = malloc(buf->length);
637 if (content_info->content->data == NULL)
638 return ENOMEM;
639 memcpy(content_info->content->data, buf->data, buf->length);
640 content_info->content->length = buf->length;
641 return 0;
644 static krb5_error_code
645 pk_mk_padata(krb5_context context,
646 krb5_pk_init_ctx ctx,
647 const KDC_REQ_BODY *req_body,
648 unsigned nonce,
649 METHOD_DATA *md)
651 struct ContentInfo content_info;
652 krb5_error_code ret;
653 const heim_oid *oid = NULL;
654 size_t size = 0;
655 krb5_data buf, sd_buf;
656 int pa_type = -1;
658 krb5_data_zero(&buf);
659 krb5_data_zero(&sd_buf);
660 memset(&content_info, 0, sizeof(content_info));
662 if (ctx->type == PKINIT_WIN2K) {
663 AuthPack_Win2k ap;
664 krb5_timestamp sec;
665 int32_t usec;
667 memset(&ap, 0, sizeof(ap));
669 /* fill in PKAuthenticator */
670 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
671 if (ret) {
672 free_AuthPack_Win2k(&ap);
673 krb5_clear_error_message(context);
674 goto out;
676 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
677 if (ret) {
678 free_AuthPack_Win2k(&ap);
679 krb5_clear_error_message(context);
680 goto out;
683 krb5_us_timeofday(context, &sec, &usec);
684 ap.pkAuthenticator.ctime = sec;
685 ap.pkAuthenticator.cusec = usec;
686 ap.pkAuthenticator.nonce = nonce;
688 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
689 &ap, &size, ret);
690 free_AuthPack_Win2k(&ap);
691 if (ret) {
692 krb5_set_error_message(context, ret,
693 N_("Failed encoding AuthPackWin: %d", ""),
694 (int)ret);
695 goto out;
697 if (buf.length != size)
698 krb5_abortx(context, "internal ASN1 encoder error");
700 oid = &asn1_oid_id_pkcs7_data;
701 } else if (ctx->type == PKINIT_27) {
702 AuthPack ap;
704 memset(&ap, 0, sizeof(ap));
706 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
707 if (ret) {
708 free_AuthPack(&ap);
709 goto out;
712 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
713 free_AuthPack(&ap);
714 if (ret) {
715 krb5_set_error_message(context, ret,
716 N_("Failed encoding AuthPack: %d", ""),
717 (int)ret);
718 goto out;
720 if (buf.length != size)
721 krb5_abortx(context, "internal ASN1 encoder error");
723 oid = &asn1_oid_id_pkauthdata;
724 } else
725 krb5_abortx(context, "internal pkinit error");
727 ret = create_signature(context, oid, &buf, ctx->id,
728 ctx->peer, &sd_buf);
729 krb5_data_free(&buf);
730 if (ret)
731 goto out;
733 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
734 krb5_data_free(&sd_buf);
735 if (ret) {
736 krb5_set_error_message(context, ret,
737 N_("ContentInfo wrapping of signedData failed",""));
738 goto out;
741 if (ctx->type == PKINIT_WIN2K) {
742 PA_PK_AS_REQ_Win2k winreq;
744 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
746 memset(&winreq, 0, sizeof(winreq));
748 winreq.signed_auth_pack = buf;
750 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
751 &winreq, &size, ret);
752 free_PA_PK_AS_REQ_Win2k(&winreq);
754 } else if (ctx->type == PKINIT_27) {
755 PA_PK_AS_REQ req;
757 pa_type = KRB5_PADATA_PK_AS_REQ;
759 memset(&req, 0, sizeof(req));
760 req.signedAuthPack = buf;
762 if (ctx->trustedCertifiers) {
764 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
765 if (req.trustedCertifiers == NULL) {
766 ret = ENOMEM;
767 krb5_set_error_message(context, ret,
768 N_("malloc: out of memory", ""));
769 free_PA_PK_AS_REQ(&req);
770 goto out;
772 ret = build_edi(context, context->hx509ctx,
773 ctx->id->anchors, req.trustedCertifiers);
774 if (ret) {
775 krb5_set_error_message(context, ret,
776 N_("pk-init: failed to build "
777 "trustedCertifiers", ""));
778 free_PA_PK_AS_REQ(&req);
779 goto out;
782 req.kdcPkId = NULL;
784 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
785 &req, &size, ret);
787 free_PA_PK_AS_REQ(&req);
789 } else
790 krb5_abortx(context, "internal pkinit error");
791 if (ret) {
792 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
793 goto out;
795 if (buf.length != size)
796 krb5_abortx(context, "Internal ASN1 encoder error");
798 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
799 if (ret)
800 free(buf.data);
802 if (ret == 0)
803 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
805 out:
806 free_ContentInfo(&content_info);
808 return ret;
812 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
813 _krb5_pk_mk_padata(krb5_context context,
814 void *c,
815 int ic_flags,
816 int win2k,
817 const KDC_REQ_BODY *req_body,
818 unsigned nonce,
819 METHOD_DATA *md)
821 krb5_pk_init_ctx ctx = c;
822 int win2k_compat;
824 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
825 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
826 N_("PKINIT: No user certificate given", ""));
827 return HEIM_PKINIT_NO_PRIVATE_KEY;
830 win2k_compat = krb5_config_get_bool_default(context, NULL,
831 win2k,
832 "realms",
833 req_body->realm,
834 "pkinit_win2k",
835 NULL);
837 if (win2k_compat) {
838 ctx->require_binding =
839 krb5_config_get_bool_default(context, NULL,
840 TRUE,
841 "realms",
842 req_body->realm,
843 "pkinit_win2k_require_binding",
844 NULL);
845 ctx->type = PKINIT_WIN2K;
846 } else
847 ctx->type = PKINIT_27;
849 ctx->require_eku =
850 krb5_config_get_bool_default(context, NULL,
851 TRUE,
852 "realms",
853 req_body->realm,
854 "pkinit_require_eku",
855 NULL);
856 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
857 ctx->require_eku = 0;
858 if (ctx->id->flags & PKINIT_BTMM)
859 ctx->require_eku = 0;
861 ctx->require_krbtgt_otherName =
862 krb5_config_get_bool_default(context, NULL,
863 TRUE,
864 "realms",
865 req_body->realm,
866 "pkinit_require_krbtgt_otherName",
867 NULL);
869 ctx->require_hostname_match =
870 krb5_config_get_bool_default(context, NULL,
871 FALSE,
872 "realms",
873 req_body->realm,
874 "pkinit_require_hostname_match",
875 NULL);
877 ctx->trustedCertifiers =
878 krb5_config_get_bool_default(context, NULL,
879 TRUE,
880 "realms",
881 req_body->realm,
882 "pkinit_trustedCertifiers",
883 NULL);
885 return pk_mk_padata(context, ctx, req_body, nonce, md);
888 static krb5_error_code
889 pk_verify_sign(krb5_context context,
890 const void *data,
891 size_t length,
892 struct krb5_pk_identity *id,
893 heim_oid *contentType,
894 krb5_data *content,
895 struct krb5_pk_cert **signer)
897 hx509_certs signer_certs;
898 int ret, flags = 0;
900 /* BTMM is broken in Leo and SnowLeo */
901 if (id->flags & PKINIT_BTMM) {
902 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
903 flags |= HX509_CMS_VS_NO_KU_CHECK;
904 flags |= HX509_CMS_VS_NO_VALIDATE;
907 *signer = NULL;
909 ret = hx509_cms_verify_signed(context->hx509ctx,
910 id->verify_ctx,
911 flags,
912 data,
913 length,
914 NULL,
915 id->certpool,
916 contentType,
917 content,
918 &signer_certs);
919 if (ret) {
920 pk_copy_error(context, context->hx509ctx, ret,
921 "CMS verify signed failed");
922 return ret;
925 *signer = calloc(1, sizeof(**signer));
926 if (*signer == NULL) {
927 krb5_clear_error_message(context);
928 ret = ENOMEM;
929 goto out;
932 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
933 if (ret) {
934 pk_copy_error(context, context->hx509ctx, ret,
935 "Failed to get on of the signer certs");
936 goto out;
939 out:
940 hx509_certs_free(&signer_certs);
941 if (ret) {
942 if (*signer) {
943 hx509_cert_free((*signer)->cert);
944 free(*signer);
945 *signer = NULL;
949 return ret;
952 static krb5_error_code
953 get_reply_key_win(krb5_context context,
954 const krb5_data *content,
955 unsigned nonce,
956 krb5_keyblock **key)
958 ReplyKeyPack_Win2k key_pack;
959 krb5_error_code ret;
960 size_t size;
962 ret = decode_ReplyKeyPack_Win2k(content->data,
963 content->length,
964 &key_pack,
965 &size);
966 if (ret) {
967 krb5_set_error_message(context, ret,
968 N_("PKINIT decoding reply key failed", ""));
969 free_ReplyKeyPack_Win2k(&key_pack);
970 return ret;
973 if ((unsigned)key_pack.nonce != nonce) {
974 krb5_set_error_message(context, ret,
975 N_("PKINIT enckey nonce is wrong", ""));
976 free_ReplyKeyPack_Win2k(&key_pack);
977 return KRB5KRB_AP_ERR_MODIFIED;
980 *key = malloc (sizeof (**key));
981 if (*key == NULL) {
982 free_ReplyKeyPack_Win2k(&key_pack);
983 krb5_set_error_message(context, ENOMEM,
984 N_("malloc: out of memory", ""));
985 return ENOMEM;
988 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
989 free_ReplyKeyPack_Win2k(&key_pack);
990 if (ret) {
991 krb5_set_error_message(context, ret,
992 N_("PKINIT failed copying reply key", ""));
993 free(*key);
994 *key = NULL;
997 return ret;
1000 static krb5_error_code
1001 get_reply_key(krb5_context context,
1002 const krb5_data *content,
1003 const krb5_data *req_buffer,
1004 krb5_keyblock **key)
1006 ReplyKeyPack key_pack;
1007 krb5_error_code ret;
1008 size_t size;
1010 ret = decode_ReplyKeyPack(content->data,
1011 content->length,
1012 &key_pack,
1013 &size);
1014 if (ret) {
1015 krb5_set_error_message(context, ret,
1016 N_("PKINIT decoding reply key failed", ""));
1017 free_ReplyKeyPack(&key_pack);
1018 return ret;
1022 krb5_crypto crypto;
1025 * XXX Verify kp.replyKey is a allowed enctype in the
1026 * configuration file
1029 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1030 if (ret) {
1031 free_ReplyKeyPack(&key_pack);
1032 return ret;
1035 ret = krb5_verify_checksum(context, crypto, 6,
1036 req_buffer->data, req_buffer->length,
1037 &key_pack.asChecksum);
1038 krb5_crypto_destroy(context, crypto);
1039 if (ret) {
1040 free_ReplyKeyPack(&key_pack);
1041 return ret;
1045 *key = malloc (sizeof (**key));
1046 if (*key == NULL) {
1047 free_ReplyKeyPack(&key_pack);
1048 krb5_set_error_message(context, ENOMEM,
1049 N_("malloc: out of memory", ""));
1050 return ENOMEM;
1053 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1054 free_ReplyKeyPack(&key_pack);
1055 if (ret) {
1056 krb5_set_error_message(context, ret,
1057 N_("PKINIT failed copying reply key", ""));
1058 free(*key);
1059 *key = NULL;
1062 return ret;
1066 static krb5_error_code
1067 pk_verify_host(krb5_context context,
1068 const char *realm,
1069 const krb5_krbhst_info *hi,
1070 struct krb5_pk_init_ctx_data *ctx,
1071 struct krb5_pk_cert *host)
1073 krb5_error_code ret = 0;
1075 if (ctx->require_eku) {
1076 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1077 &asn1_oid_id_pkkdcekuoid, 0);
1078 if (ret) {
1079 krb5_set_error_message(context, ret,
1080 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1081 return ret;
1084 if (ctx->require_krbtgt_otherName) {
1085 hx509_octet_string_list list;
1086 size_t i;
1088 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1089 host->cert,
1090 &asn1_oid_id_pkinit_san,
1091 &list);
1092 if (ret) {
1093 krb5_set_error_message(context, ret,
1094 N_("Failed to find the PK-INIT "
1095 "subjectAltName in the KDC "
1096 "certificate", ""));
1098 return ret;
1101 for (i = 0; i < list.len; i++) {
1102 KRB5PrincipalName r;
1104 ret = decode_KRB5PrincipalName(list.val[i].data,
1105 list.val[i].length,
1107 NULL);
1108 if (ret) {
1109 krb5_set_error_message(context, ret,
1110 N_("Failed to decode the PK-INIT "
1111 "subjectAltName in the "
1112 "KDC certificate", ""));
1114 break;
1117 if (r.principalName.name_string.len != 2 ||
1118 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1119 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1120 strcmp(r.realm, realm) != 0)
1122 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1123 krb5_set_error_message(context, ret,
1124 N_("KDC have wrong realm name in "
1125 "the certificate", ""));
1128 free_KRB5PrincipalName(&r);
1129 if (ret)
1130 break;
1132 hx509_free_octet_string_list(&list);
1134 if (ret)
1135 return ret;
1137 if (hi) {
1138 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1139 ctx->require_hostname_match,
1140 HX509_HN_HOSTNAME,
1141 hi->hostname,
1142 hi->ai->ai_addr, hi->ai->ai_addrlen);
1144 if (ret)
1145 krb5_set_error_message(context, ret,
1146 N_("Address mismatch in "
1147 "the KDC certificate", ""));
1149 return ret;
1152 static krb5_error_code
1153 pk_rd_pa_reply_enckey(krb5_context context,
1154 int type,
1155 const heim_octet_string *indata,
1156 const heim_oid *dataType,
1157 const char *realm,
1158 krb5_pk_init_ctx ctx,
1159 krb5_enctype etype,
1160 const krb5_krbhst_info *hi,
1161 unsigned nonce,
1162 const krb5_data *req_buffer,
1163 PA_DATA *pa,
1164 krb5_keyblock **key)
1166 krb5_error_code ret;
1167 struct krb5_pk_cert *host = NULL;
1168 krb5_data content;
1169 heim_oid contentType = { 0, NULL };
1170 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1172 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1173 krb5_set_error_message(context, EINVAL,
1174 N_("PKINIT: Invalid content type", ""));
1175 return EINVAL;
1178 if (ctx->type == PKINIT_WIN2K)
1179 flags |= HX509_CMS_UE_ALLOW_WEAK;
1181 ret = hx509_cms_unenvelope(context->hx509ctx,
1182 ctx->id->certs,
1183 flags,
1184 indata->data,
1185 indata->length,
1186 NULL,
1188 &contentType,
1189 &content);
1190 if (ret) {
1191 pk_copy_error(context, context->hx509ctx, ret,
1192 "Failed to unenvelope CMS data in PK-INIT reply");
1193 return ret;
1195 der_free_oid(&contentType);
1197 /* win2k uses ContentInfo */
1198 if (type == PKINIT_WIN2K) {
1199 heim_oid type2;
1200 heim_octet_string out;
1202 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1203 if (ret) {
1204 /* windows LH with interesting CMS packets */
1205 size_t ph = 1 + der_length_len(content.length);
1206 unsigned char *ptr = malloc(content.length + ph);
1207 size_t l;
1209 memcpy(ptr + ph, content.data, content.length);
1211 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1212 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1213 if (ret)
1214 return ret;
1215 free(content.data);
1216 content.data = ptr;
1217 content.length += ph;
1219 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1220 if (ret)
1221 goto out;
1223 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1224 ret = EINVAL; /* XXX */
1225 krb5_set_error_message(context, ret,
1226 N_("PKINIT: Invalid content type", ""));
1227 der_free_oid(&type2);
1228 der_free_octet_string(&out);
1229 goto out;
1231 der_free_oid(&type2);
1232 krb5_data_free(&content);
1233 ret = krb5_data_copy(&content, out.data, out.length);
1234 der_free_octet_string(&out);
1235 if (ret) {
1236 krb5_set_error_message(context, ret,
1237 N_("malloc: out of memory", ""));
1238 goto out;
1242 ret = pk_verify_sign(context,
1243 content.data,
1244 content.length,
1245 ctx->id,
1246 &contentType,
1247 &content,
1248 &host);
1249 if (ret)
1250 goto out;
1252 /* make sure that it is the kdc's certificate */
1253 ret = pk_verify_host(context, realm, hi, ctx, host);
1254 if (ret) {
1255 goto out;
1258 #if 0
1259 if (type == PKINIT_WIN2K) {
1260 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1261 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1262 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1263 goto out;
1265 } else {
1266 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1267 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1268 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1269 goto out;
1272 #endif
1274 switch(type) {
1275 case PKINIT_WIN2K:
1276 ret = get_reply_key(context, &content, req_buffer, key);
1277 if (ret != 0 && ctx->require_binding == 0)
1278 ret = get_reply_key_win(context, &content, nonce, key);
1279 break;
1280 case PKINIT_27:
1281 ret = get_reply_key(context, &content, req_buffer, key);
1282 break;
1284 if (ret)
1285 goto out;
1287 /* XXX compare given etype with key->etype */
1289 out:
1290 if (host)
1291 _krb5_pk_cert_free(host);
1292 der_free_oid(&contentType);
1293 krb5_data_free(&content);
1295 return ret;
1298 static krb5_error_code
1299 pk_rd_pa_reply_dh(krb5_context context,
1300 const heim_octet_string *indata,
1301 const heim_oid *dataType,
1302 const char *realm,
1303 krb5_pk_init_ctx ctx,
1304 krb5_enctype etype,
1305 const krb5_krbhst_info *hi,
1306 const DHNonce *c_n,
1307 const DHNonce *k_n,
1308 unsigned nonce,
1309 PA_DATA *pa,
1310 krb5_keyblock **key)
1312 const unsigned char *p;
1313 unsigned char *dh_gen_key = NULL;
1314 struct krb5_pk_cert *host = NULL;
1315 BIGNUM *kdc_dh_pubkey = NULL;
1316 KDCDHKeyInfo kdc_dh_info;
1317 heim_oid contentType = { 0, NULL };
1318 krb5_data content;
1319 krb5_error_code ret;
1320 int dh_gen_keylen = 0;
1321 size_t size;
1323 krb5_data_zero(&content);
1324 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1326 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1327 krb5_set_error_message(context, EINVAL,
1328 N_("PKINIT: Invalid content type", ""));
1329 return EINVAL;
1332 ret = pk_verify_sign(context,
1333 indata->data,
1334 indata->length,
1335 ctx->id,
1336 &contentType,
1337 &content,
1338 &host);
1339 if (ret)
1340 goto out;
1342 /* make sure that it is the kdc's certificate */
1343 ret = pk_verify_host(context, realm, hi, ctx, host);
1344 if (ret)
1345 goto out;
1347 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1348 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1349 krb5_set_error_message(context, ret,
1350 N_("pkinit - dh reply contains wrong oid", ""));
1351 goto out;
1354 ret = decode_KDCDHKeyInfo(content.data,
1355 content.length,
1356 &kdc_dh_info,
1357 &size);
1359 if (ret) {
1360 krb5_set_error_message(context, ret,
1361 N_("pkinit - failed to decode "
1362 "KDC DH Key Info", ""));
1363 goto out;
1366 if (kdc_dh_info.nonce != nonce) {
1367 ret = KRB5KRB_AP_ERR_MODIFIED;
1368 krb5_set_error_message(context, ret,
1369 N_("PKINIT: DH nonce is wrong", ""));
1370 goto out;
1373 if (kdc_dh_info.dhKeyExpiration) {
1374 if (k_n == NULL) {
1375 ret = KRB5KRB_ERR_GENERIC;
1376 krb5_set_error_message(context, ret,
1377 N_("pkinit; got key expiration "
1378 "without server nonce", ""));
1379 goto out;
1381 if (c_n == NULL) {
1382 ret = KRB5KRB_ERR_GENERIC;
1383 krb5_set_error_message(context, ret,
1384 N_("pkinit; got DH reuse but no "
1385 "client nonce", ""));
1386 goto out;
1388 } else {
1389 if (k_n) {
1390 ret = KRB5KRB_ERR_GENERIC;
1391 krb5_set_error_message(context, ret,
1392 N_("pkinit: got server nonce "
1393 "without key expiration", ""));
1394 goto out;
1396 c_n = NULL;
1400 p = kdc_dh_info.subjectPublicKey.data;
1401 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1403 if (ctx->keyex == USE_DH) {
1404 DHPublicKey k;
1405 ret = decode_DHPublicKey(p, size, &k, NULL);
1406 if (ret) {
1407 krb5_set_error_message(context, ret,
1408 N_("pkinit: can't decode "
1409 "without key expiration", ""));
1410 goto out;
1413 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1414 free_DHPublicKey(&k);
1415 if (kdc_dh_pubkey == NULL) {
1416 ret = ENOMEM;
1417 goto out;
1421 size = DH_size(ctx->u.dh);
1423 dh_gen_key = malloc(size);
1424 if (dh_gen_key == NULL) {
1425 ret = ENOMEM;
1426 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1427 goto out;
1430 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1431 if (dh_gen_keylen == -1) {
1432 ret = KRB5KRB_ERR_GENERIC;
1433 dh_gen_keylen = 0;
1434 krb5_set_error_message(context, ret,
1435 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1436 goto out;
1438 if (dh_gen_keylen < (int)size) {
1439 size -= dh_gen_keylen;
1440 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1441 memset(dh_gen_key, 0, size);
1444 } else {
1445 #ifdef HAVE_OPENSSL
1446 const EC_GROUP *group;
1447 EC_KEY *public = NULL;
1449 group = EC_KEY_get0_group(ctx->u.eckey);
1451 public = EC_KEY_new();
1452 if (public == NULL) {
1453 ret = ENOMEM;
1454 goto out;
1456 if (EC_KEY_set_group(public, group) != 1) {
1457 EC_KEY_free(public);
1458 ret = ENOMEM;
1459 goto out;
1462 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1463 EC_KEY_free(public);
1464 ret = KRB5KRB_ERR_GENERIC;
1465 krb5_set_error_message(context, ret,
1466 N_("PKINIT: Can't parse ECDH public key", ""));
1467 goto out;
1470 size = (EC_GROUP_get_degree(group) + 7) / 8;
1471 dh_gen_key = malloc(size);
1472 if (dh_gen_key == NULL) {
1473 EC_KEY_free(public);
1474 ret = ENOMEM;
1475 krb5_set_error_message(context, ret,
1476 N_("malloc: out of memory", ""));
1477 goto out;
1479 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1480 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1481 EC_KEY_free(public);
1482 if (dh_gen_keylen == -1) {
1483 ret = KRB5KRB_ERR_GENERIC;
1484 dh_gen_keylen = 0;
1485 krb5_set_error_message(context, ret,
1486 N_("PKINIT: Can't compute ECDH public key", ""));
1487 goto out;
1489 #else
1490 ret = EINVAL;
1491 #endif
1494 if (dh_gen_keylen <= 0) {
1495 ret = EINVAL;
1496 krb5_set_error_message(context, ret,
1497 N_("PKINIT: resulting DH key <= 0", ""));
1498 dh_gen_keylen = 0;
1499 goto out;
1502 *key = malloc (sizeof (**key));
1503 if (*key == NULL) {
1504 ret = ENOMEM;
1505 krb5_set_error_message(context, ret,
1506 N_("malloc: out of memory", ""));
1507 goto out;
1510 ret = _krb5_pk_octetstring2key(context,
1511 etype,
1512 dh_gen_key, dh_gen_keylen,
1513 c_n, k_n,
1514 *key);
1515 if (ret) {
1516 krb5_set_error_message(context, ret,
1517 N_("PKINIT: can't create key from DH key", ""));
1518 free(*key);
1519 *key = NULL;
1520 goto out;
1523 out:
1524 if (kdc_dh_pubkey)
1525 BN_free(kdc_dh_pubkey);
1526 if (dh_gen_key) {
1527 memset(dh_gen_key, 0, dh_gen_keylen);
1528 free(dh_gen_key);
1530 if (host)
1531 _krb5_pk_cert_free(host);
1532 if (content.data)
1533 krb5_data_free(&content);
1534 der_free_oid(&contentType);
1535 free_KDCDHKeyInfo(&kdc_dh_info);
1537 return ret;
1540 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1541 _krb5_pk_rd_pa_reply(krb5_context context,
1542 const char *realm,
1543 void *c,
1544 krb5_enctype etype,
1545 const krb5_krbhst_info *hi,
1546 unsigned nonce,
1547 const krb5_data *req_buffer,
1548 PA_DATA *pa,
1549 krb5_keyblock **key)
1551 krb5_pk_init_ctx ctx = c;
1552 krb5_error_code ret;
1553 size_t size;
1555 /* Check for IETF PK-INIT first */
1556 if (ctx->type == PKINIT_27) {
1557 PA_PK_AS_REP rep;
1558 heim_octet_string os, data;
1559 heim_oid oid;
1561 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1562 krb5_set_error_message(context, EINVAL,
1563 N_("PKINIT: wrong padata recv", ""));
1564 return EINVAL;
1567 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1568 pa->padata_value.length,
1569 &rep,
1570 &size);
1571 if (ret) {
1572 krb5_set_error_message(context, ret,
1573 N_("Failed to decode pkinit AS rep", ""));
1574 return ret;
1577 switch (rep.element) {
1578 case choice_PA_PK_AS_REP_dhInfo:
1579 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1580 os = rep.u.dhInfo.dhSignedData;
1581 break;
1582 case choice_PA_PK_AS_REP_encKeyPack:
1583 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1584 os = rep.u.encKeyPack;
1585 break;
1586 default: {
1587 PA_PK_AS_REP_BTMM btmm;
1588 free_PA_PK_AS_REP(&rep);
1589 memset(&rep, 0, sizeof(rep));
1591 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1593 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1594 pa->padata_value.length,
1595 &btmm,
1596 &size);
1597 if (ret) {
1598 krb5_set_error_message(context, EINVAL,
1599 N_("PKINIT: -27 reply "
1600 "invalid content type", ""));
1601 return EINVAL;
1604 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1605 free_PA_PK_AS_REP_BTMM(&btmm);
1606 ret = EINVAL;
1607 krb5_set_error_message(context, ret,
1608 N_("DH mode not supported for BTMM mode", ""));
1609 return ret;
1613 * Transform to IETF style PK-INIT reply so that free works below
1616 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1617 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1618 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1619 btmm.encKeyPack->data = NULL;
1620 btmm.encKeyPack->length = 0;
1621 free_PA_PK_AS_REP_BTMM(&btmm);
1622 os = rep.u.encKeyPack;
1626 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1627 if (ret) {
1628 free_PA_PK_AS_REP(&rep);
1629 krb5_set_error_message(context, ret,
1630 N_("PKINIT: failed to unwrap CI", ""));
1631 return ret;
1634 switch (rep.element) {
1635 case choice_PA_PK_AS_REP_dhInfo:
1636 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1637 ctx->clientDHNonce,
1638 rep.u.dhInfo.serverDHNonce,
1639 nonce, pa, key);
1640 break;
1641 case choice_PA_PK_AS_REP_encKeyPack:
1642 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1643 ctx, etype, hi, nonce, req_buffer, pa, key);
1644 break;
1645 default:
1646 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1648 der_free_octet_string(&data);
1649 der_free_oid(&oid);
1650 free_PA_PK_AS_REP(&rep);
1652 } else if (ctx->type == PKINIT_WIN2K) {
1653 PA_PK_AS_REP_Win2k w2krep;
1655 /* Check for Windows encoding of the AS-REP pa data */
1657 #if 0 /* should this be ? */
1658 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1659 krb5_set_error_message(context, EINVAL,
1660 "PKINIT: wrong padata recv");
1661 return EINVAL;
1663 #endif
1665 memset(&w2krep, 0, sizeof(w2krep));
1667 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1668 pa->padata_value.length,
1669 &w2krep,
1670 &size);
1671 if (ret) {
1672 krb5_set_error_message(context, ret,
1673 N_("PKINIT: Failed decoding windows "
1674 "pkinit reply %d", ""), (int)ret);
1675 return ret;
1678 krb5_clear_error_message(context);
1680 switch (w2krep.element) {
1681 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1682 heim_octet_string data;
1683 heim_oid oid;
1685 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1686 &oid, &data, NULL);
1687 free_PA_PK_AS_REP_Win2k(&w2krep);
1688 if (ret) {
1689 krb5_set_error_message(context, ret,
1690 N_("PKINIT: failed to unwrap CI", ""));
1691 return ret;
1694 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1695 ctx, etype, hi, nonce, req_buffer, pa, key);
1696 der_free_octet_string(&data);
1697 der_free_oid(&oid);
1699 break;
1701 default:
1702 free_PA_PK_AS_REP_Win2k(&w2krep);
1703 ret = EINVAL;
1704 krb5_set_error_message(context, ret,
1705 N_("PKINIT: win2k reply invalid "
1706 "content type", ""));
1707 break;
1710 } else {
1711 ret = EINVAL;
1712 krb5_set_error_message(context, ret,
1713 N_("PKINIT: unknown reply type", ""));
1716 return ret;
1719 struct prompter {
1720 krb5_context context;
1721 krb5_prompter_fct prompter;
1722 void *prompter_data;
1725 static int
1726 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1728 krb5_error_code ret;
1729 krb5_prompt prompt;
1730 krb5_data password_data;
1731 struct prompter *p = data;
1733 password_data.data = prompter->reply.data;
1734 password_data.length = prompter->reply.length;
1736 prompt.prompt = prompter->prompt;
1737 prompt.hidden = hx509_prompt_hidden(prompter->type);
1738 prompt.reply = &password_data;
1740 switch (prompter->type) {
1741 case HX509_PROMPT_TYPE_INFO:
1742 prompt.type = KRB5_PROMPT_TYPE_INFO;
1743 break;
1744 case HX509_PROMPT_TYPE_PASSWORD:
1745 case HX509_PROMPT_TYPE_QUESTION:
1746 default:
1747 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1748 break;
1751 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1752 if (ret) {
1753 memset (prompter->reply.data, 0, prompter->reply.length);
1754 return 1;
1756 return 0;
1759 static krb5_error_code
1760 _krb5_pk_set_user_id(krb5_context context,
1761 krb5_principal principal,
1762 krb5_pk_init_ctx ctx,
1763 struct hx509_certs_data *certs)
1765 hx509_certs c = hx509_certs_ref(certs);
1766 hx509_query *q = NULL;
1767 int ret;
1769 if (ctx->id->certs)
1770 hx509_certs_free(&ctx->id->certs);
1771 if (ctx->id->cert) {
1772 hx509_cert_free(ctx->id->cert);
1773 ctx->id->cert = NULL;
1776 ctx->id->certs = c;
1777 ctx->anonymous = 0;
1779 ret = hx509_query_alloc(context->hx509ctx, &q);
1780 if (ret) {
1781 pk_copy_error(context, context->hx509ctx, ret,
1782 "Allocate query to find signing certificate");
1783 return ret;
1786 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1787 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1789 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1790 ctx->id->flags |= PKINIT_BTMM;
1793 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1794 hx509_query_free(context->hx509ctx, q);
1796 if (ret == 0 && _krb5_have_debug(context, 2)) {
1797 hx509_name name;
1798 char *str, *sn;
1799 heim_integer i;
1801 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1802 if (ret)
1803 goto out;
1805 ret = hx509_name_to_string(name, &str);
1806 hx509_name_free(&name);
1807 if (ret)
1808 goto out;
1810 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1811 if (ret) {
1812 free(str);
1813 goto out;
1816 ret = der_print_hex_heim_integer(&i, &sn);
1817 der_free_heim_integer(&i);
1818 if (ret) {
1819 free(name);
1820 goto out;
1823 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1824 free(str);
1825 free(sn);
1827 out:
1829 return ret;
1832 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1833 _krb5_pk_load_id(krb5_context context,
1834 struct krb5_pk_identity **ret_id,
1835 const char *user_id,
1836 const char *anchor_id,
1837 char * const *chain_list,
1838 char * const *revoke_list,
1839 krb5_prompter_fct prompter,
1840 void *prompter_data,
1841 char *password)
1843 struct krb5_pk_identity *id = NULL;
1844 struct prompter p;
1845 int ret;
1847 *ret_id = NULL;
1849 if (anchor_id == NULL) {
1850 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1851 N_("PKINIT: No anchor given", ""));
1852 return HEIM_PKINIT_NO_VALID_CA;
1855 /* load cert */
1857 id = calloc(1, sizeof(*id));
1858 if (id == NULL) {
1859 krb5_set_error_message(context, ENOMEM,
1860 N_("malloc: out of memory", ""));
1861 return ENOMEM;
1864 if (user_id) {
1865 hx509_lock lock;
1867 ret = hx509_lock_init(context->hx509ctx, &lock);
1868 if (ret) {
1869 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1870 goto out;
1873 if (password && password[0])
1874 hx509_lock_add_password(lock, password);
1876 if (prompter) {
1877 p.context = context;
1878 p.prompter = prompter;
1879 p.prompter_data = prompter_data;
1881 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1882 if (ret) {
1883 hx509_lock_free(lock);
1884 goto out;
1888 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1889 hx509_lock_free(lock);
1890 if (ret) {
1891 pk_copy_error(context, context->hx509ctx, ret,
1892 "Failed to init cert certs");
1893 goto out;
1895 } else {
1896 id->certs = NULL;
1899 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1900 if (ret) {
1901 pk_copy_error(context, context->hx509ctx, ret,
1902 "Failed to init anchors");
1903 goto out;
1906 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1907 0, NULL, &id->certpool);
1908 if (ret) {
1909 pk_copy_error(context, context->hx509ctx, ret,
1910 "Failed to init chain");
1911 goto out;
1914 while (chain_list && *chain_list) {
1915 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1916 NULL, *chain_list);
1917 if (ret) {
1918 pk_copy_error(context, context->hx509ctx, ret,
1919 "Failed to laod chain %s",
1920 *chain_list);
1921 goto out;
1923 chain_list++;
1926 if (revoke_list) {
1927 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1928 if (ret) {
1929 pk_copy_error(context, context->hx509ctx, ret,
1930 "Failed init revoke list");
1931 goto out;
1934 while (*revoke_list) {
1935 ret = hx509_revoke_add_crl(context->hx509ctx,
1936 id->revokectx,
1937 *revoke_list);
1938 if (ret) {
1939 pk_copy_error(context, context->hx509ctx, ret,
1940 "Failed load revoke list");
1941 goto out;
1943 revoke_list++;
1945 } else
1946 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1948 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1949 if (ret) {
1950 pk_copy_error(context, context->hx509ctx, ret,
1951 "Failed init verify context");
1952 goto out;
1955 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1956 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1958 out:
1959 if (ret) {
1960 hx509_verify_destroy_ctx(id->verify_ctx);
1961 hx509_certs_free(&id->certs);
1962 hx509_certs_free(&id->anchors);
1963 hx509_certs_free(&id->certpool);
1964 hx509_revoke_free(&id->revokectx);
1965 free(id);
1966 } else
1967 *ret_id = id;
1969 return ret;
1976 static void
1977 pk_copy_error(krb5_context context,
1978 hx509_context hx509ctx,
1979 int hxret,
1980 const char *fmt,
1981 ...)
1983 va_list va;
1984 char *s, *f;
1985 int ret;
1987 va_start(va, fmt);
1988 ret = vasprintf(&f, fmt, va);
1989 va_end(va);
1990 if (ret == -1 || f == NULL) {
1991 krb5_clear_error_message(context);
1992 return;
1995 s = hx509_get_error_string(hx509ctx, hxret);
1996 if (s == NULL) {
1997 krb5_clear_error_message(context);
1998 free(f);
1999 return;
2001 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2002 free(s);
2003 free(f);
2006 static int
2007 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2008 const char *name, heim_integer *integer)
2010 int ret;
2011 char *p1;
2012 p1 = strsep(p, " \t");
2013 if (p1 == NULL) {
2014 krb5_set_error_message(context, EINVAL,
2015 N_("moduli file %s missing %s on line %d", ""),
2016 file, name, lineno);
2017 return EINVAL;
2019 ret = der_parse_hex_heim_integer(p1, integer);
2020 if (ret) {
2021 krb5_set_error_message(context, ret,
2022 N_("moduli file %s failed parsing %s "
2023 "on line %d", ""),
2024 file, name, lineno);
2025 return ret;
2028 return 0;
2031 krb5_error_code
2032 _krb5_parse_moduli_line(krb5_context context,
2033 const char *file,
2034 int lineno,
2035 char *p,
2036 struct krb5_dh_moduli **m)
2038 struct krb5_dh_moduli *m1;
2039 char *p1;
2040 int ret;
2042 *m = NULL;
2044 m1 = calloc(1, sizeof(*m1));
2045 if (m1 == NULL) {
2046 krb5_set_error_message(context, ENOMEM,
2047 N_("malloc: out of memory", ""));
2048 return ENOMEM;
2051 while (isspace((unsigned char)*p))
2052 p++;
2053 if (*p == '#') {
2054 free(m1);
2055 return 0;
2057 ret = EINVAL;
2059 p1 = strsep(&p, " \t");
2060 if (p1 == NULL) {
2061 krb5_set_error_message(context, ret,
2062 N_("moduli file %s missing name on line %d", ""),
2063 file, lineno);
2064 goto out;
2066 m1->name = strdup(p1);
2067 if (m1->name == NULL) {
2068 ret = ENOMEM;
2069 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2070 goto out;
2073 p1 = strsep(&p, " \t");
2074 if (p1 == NULL) {
2075 krb5_set_error_message(context, ret,
2076 N_("moduli file %s missing bits on line %d", ""),
2077 file, lineno);
2078 goto out;
2081 m1->bits = atoi(p1);
2082 if (m1->bits == 0) {
2083 krb5_set_error_message(context, ret,
2084 N_("moduli file %s have un-parsable "
2085 "bits on line %d", ""), file, lineno);
2086 goto out;
2089 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2090 if (ret)
2091 goto out;
2092 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2093 if (ret)
2094 goto out;
2095 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2096 if (ret)
2097 goto out;
2099 *m = m1;
2101 return 0;
2102 out:
2103 free(m1->name);
2104 der_free_heim_integer(&m1->p);
2105 der_free_heim_integer(&m1->g);
2106 der_free_heim_integer(&m1->q);
2107 free(m1);
2108 return ret;
2111 void
2112 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2114 int i;
2115 for (i = 0; moduli[i] != NULL; i++) {
2116 free(moduli[i]->name);
2117 der_free_heim_integer(&moduli[i]->p);
2118 der_free_heim_integer(&moduli[i]->g);
2119 der_free_heim_integer(&moduli[i]->q);
2120 free(moduli[i]);
2122 free(moduli);
2125 static const char *default_moduli_RFC2412_MODP_group2 =
2126 /* name */
2127 "RFC2412-MODP-group2 "
2128 /* bits */
2129 "1024 "
2130 /* p */
2131 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2132 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2133 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2134 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2135 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2136 "FFFFFFFF" "FFFFFFFF "
2137 /* g */
2138 "02 "
2139 /* q */
2140 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2141 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2142 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2143 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2144 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2145 "FFFFFFFF" "FFFFFFFF";
2147 static const char *default_moduli_rfc3526_MODP_group14 =
2148 /* name */
2149 "rfc3526-MODP-group14 "
2150 /* bits */
2151 "1760 "
2152 /* p */
2153 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2154 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2155 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2156 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2157 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2158 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2159 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2160 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2161 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2162 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2163 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2164 /* g */
2165 "02 "
2166 /* q */
2167 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2168 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2169 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2170 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2171 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2172 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2173 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2174 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2175 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2176 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2177 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2179 krb5_error_code
2180 _krb5_parse_moduli(krb5_context context, const char *file,
2181 struct krb5_dh_moduli ***moduli)
2183 /* name bits P G Q */
2184 krb5_error_code ret;
2185 struct krb5_dh_moduli **m = NULL, **m2;
2186 char buf[4096];
2187 FILE *f;
2188 int lineno = 0, n = 0;
2190 *moduli = NULL;
2192 m = calloc(1, sizeof(m[0]) * 3);
2193 if (m == NULL) {
2194 krb5_set_error_message(context, ENOMEM,
2195 N_("malloc: out of memory", ""));
2196 return ENOMEM;
2199 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2200 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2201 if (ret) {
2202 _krb5_free_moduli(m);
2203 return ret;
2205 n++;
2207 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2208 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2209 if (ret) {
2210 _krb5_free_moduli(m);
2211 return ret;
2213 n++;
2216 if (file == NULL)
2217 file = MODULI_FILE;
2219 #ifdef KRB5_USE_PATH_TOKENS
2221 char * exp_file;
2223 if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2224 f = fopen(exp_file, "r");
2225 krb5_xfree(exp_file);
2226 } else {
2227 f = NULL;
2230 #else
2231 f = fopen(file, "r");
2232 #endif
2234 if (f == NULL) {
2235 *moduli = m;
2236 return 0;
2238 rk_cloexec_file(f);
2240 while(fgets(buf, sizeof(buf), f) != NULL) {
2241 struct krb5_dh_moduli *element;
2243 buf[strcspn(buf, "\n")] = '\0';
2244 lineno++;
2246 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2247 if (m2 == NULL) {
2248 _krb5_free_moduli(m);
2249 krb5_set_error_message(context, ENOMEM,
2250 N_("malloc: out of memory", ""));
2251 return ENOMEM;
2253 m = m2;
2255 m[n] = NULL;
2257 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2258 if (ret) {
2259 _krb5_free_moduli(m);
2260 return ret;
2262 if (element == NULL)
2263 continue;
2265 m[n] = element;
2266 m[n + 1] = NULL;
2267 n++;
2269 *moduli = m;
2270 return 0;
2273 krb5_error_code
2274 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2275 heim_integer *p, heim_integer *g, heim_integer *q,
2276 struct krb5_dh_moduli **moduli,
2277 char **name)
2279 int i;
2281 if (name)
2282 *name = NULL;
2284 for (i = 0; moduli[i] != NULL; i++) {
2285 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2286 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2287 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2289 if (bits && bits > moduli[i]->bits) {
2290 krb5_set_error_message(context,
2291 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2292 N_("PKINIT: DH group parameter %s "
2293 "no accepted, not enough bits "
2294 "generated", ""),
2295 moduli[i]->name);
2296 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2298 if (name)
2299 *name = strdup(moduli[i]->name);
2300 return 0;
2303 krb5_set_error_message(context,
2304 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2305 N_("PKINIT: DH group parameter no ok", ""));
2306 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2308 #endif /* PKINIT */
2310 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2311 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2313 #ifdef PKINIT
2314 krb5_pk_init_ctx ctx;
2316 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2317 return;
2318 ctx = opt->opt_private->pk_init_ctx;
2319 switch (ctx->keyex) {
2320 case USE_DH:
2321 if (ctx->u.dh)
2322 DH_free(ctx->u.dh);
2323 break;
2324 case USE_RSA:
2325 break;
2326 case USE_ECDH:
2327 #ifdef HAVE_OPENSSL
2328 if (ctx->u.eckey)
2329 EC_KEY_free(ctx->u.eckey);
2330 #endif
2331 break;
2333 if (ctx->id) {
2334 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2335 hx509_certs_free(&ctx->id->certs);
2336 hx509_cert_free(ctx->id->cert);
2337 hx509_certs_free(&ctx->id->anchors);
2338 hx509_certs_free(&ctx->id->certpool);
2340 if (ctx->clientDHNonce) {
2341 krb5_free_data(NULL, ctx->clientDHNonce);
2342 ctx->clientDHNonce = NULL;
2344 if (ctx->m)
2345 _krb5_free_moduli(ctx->m);
2346 free(ctx->id);
2347 ctx->id = NULL;
2349 free(opt->opt_private->pk_init_ctx);
2350 opt->opt_private->pk_init_ctx = NULL;
2351 #endif
2354 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2355 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2356 krb5_get_init_creds_opt *opt,
2357 krb5_principal principal,
2358 const char *user_id,
2359 const char *x509_anchors,
2360 char * const * pool,
2361 char * const * pki_revoke,
2362 int flags,
2363 krb5_prompter_fct prompter,
2364 void *prompter_data,
2365 char *password)
2367 #ifdef PKINIT
2368 krb5_error_code ret;
2369 char *anchors = NULL;
2371 if (opt->opt_private == NULL) {
2372 krb5_set_error_message(context, EINVAL,
2373 N_("PKINIT: on non extendable opt", ""));
2374 return EINVAL;
2377 opt->opt_private->pk_init_ctx =
2378 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2379 if (opt->opt_private->pk_init_ctx == NULL) {
2380 krb5_set_error_message(context, ENOMEM,
2381 N_("malloc: out of memory", ""));
2382 return ENOMEM;
2384 opt->opt_private->pk_init_ctx->require_binding = 0;
2385 opt->opt_private->pk_init_ctx->require_eku = 1;
2386 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2387 opt->opt_private->pk_init_ctx->peer = NULL;
2389 /* XXX implement krb5_appdefault_strings */
2390 if (pool == NULL)
2391 pool = krb5_config_get_strings(context, NULL,
2392 "appdefaults",
2393 "pkinit_pool",
2394 NULL);
2396 if (pki_revoke == NULL)
2397 pki_revoke = krb5_config_get_strings(context, NULL,
2398 "appdefaults",
2399 "pkinit_revoke",
2400 NULL);
2402 if (x509_anchors == NULL) {
2403 krb5_appdefault_string(context, "kinit",
2404 krb5_principal_get_realm(context, principal),
2405 "pkinit_anchors", NULL, &anchors);
2406 x509_anchors = anchors;
2409 if (flags & 4)
2410 opt->opt_private->pk_init_ctx->anonymous = 1;
2412 ret = _krb5_pk_load_id(context,
2413 &opt->opt_private->pk_init_ctx->id,
2414 user_id,
2415 x509_anchors,
2416 pool,
2417 pki_revoke,
2418 prompter,
2419 prompter_data,
2420 password);
2421 if (ret) {
2422 free(opt->opt_private->pk_init_ctx);
2423 opt->opt_private->pk_init_ctx = NULL;
2424 return ret;
2427 if (opt->opt_private->pk_init_ctx->id->certs) {
2428 _krb5_pk_set_user_id(context,
2429 principal,
2430 opt->opt_private->pk_init_ctx,
2431 opt->opt_private->pk_init_ctx->id->certs);
2432 } else
2433 opt->opt_private->pk_init_ctx->id->cert = NULL;
2435 if ((flags & 2) == 0) {
2436 hx509_context hx509ctx = context->hx509ctx;
2437 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2439 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2442 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2444 if (cert) {
2445 AlgorithmIdentifier alg;
2447 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2448 if (ret == 0) {
2449 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2450 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2451 free_AlgorithmIdentifier(&alg);
2455 } else {
2456 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2458 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2459 krb5_set_error_message(context, EINVAL,
2460 N_("No anonymous pkinit support in RSA mode", ""));
2461 return EINVAL;
2465 return 0;
2466 #else
2467 krb5_set_error_message(context, EINVAL,
2468 N_("no support for PKINIT compiled in", ""));
2469 return EINVAL;
2470 #endif
2473 krb5_error_code KRB5_LIB_FUNCTION
2474 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2475 krb5_get_init_creds_opt *opt,
2476 struct hx509_certs_data *certs)
2478 #ifdef PKINIT
2479 if (opt->opt_private == NULL) {
2480 krb5_set_error_message(context, EINVAL,
2481 N_("PKINIT: on non extendable opt", ""));
2482 return EINVAL;
2484 if (opt->opt_private->pk_init_ctx == NULL) {
2485 krb5_set_error_message(context, EINVAL,
2486 N_("PKINIT: on pkinit context", ""));
2487 return EINVAL;
2490 _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2492 return 0;
2493 #else
2494 krb5_set_error_message(context, EINVAL,
2495 N_("no support for PKINIT compiled in", ""));
2496 return EINVAL;
2497 #endif
2500 #ifdef PKINIT
2502 static int
2503 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2505 hx509_octet_string_list list;
2506 int ret;
2508 *upn = NULL;
2510 ret = hx509_cert_find_subjectAltName_otherName(context,
2511 cert,
2512 &asn1_oid_id_pkinit_ms_san,
2513 &list);
2514 if (ret)
2515 return 0;
2517 if (list.len > 0 && list.val[0].length > 0)
2518 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2519 upn, NULL);
2520 else
2521 ret = 1;
2522 hx509_free_octet_string_list(&list);
2524 return ret;
2527 static int
2528 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2530 char *upn;
2531 int ret;
2533 ret = get_ms_san(context, cert, &upn);
2534 if (ret == 0)
2535 free(upn);
2536 return ret;
2541 #endif
2544 * Private since it need to be redesigned using krb5_get_init_creds()
2547 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2548 krb5_pk_enterprise_cert(krb5_context context,
2549 const char *user_id,
2550 krb5_const_realm realm,
2551 krb5_principal *principal,
2552 struct hx509_certs_data **res)
2554 #ifdef PKINIT
2555 krb5_error_code ret;
2556 hx509_certs certs, result;
2557 hx509_cert cert = NULL;
2558 hx509_query *q;
2559 char *name;
2561 *principal = NULL;
2562 if (res)
2563 *res = NULL;
2565 if (user_id == NULL) {
2566 krb5_set_error_message(context, ENOENT, "no user id");
2567 return ENOENT;
2570 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2571 if (ret) {
2572 pk_copy_error(context, context->hx509ctx, ret,
2573 "Failed to init cert certs");
2574 goto out;
2577 ret = hx509_query_alloc(context->hx509ctx, &q);
2578 if (ret) {
2579 krb5_set_error_message(context, ret, "out of memory");
2580 hx509_certs_free(&certs);
2581 goto out;
2584 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2585 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2586 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2587 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2589 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2590 hx509_query_free(context->hx509ctx, q);
2591 hx509_certs_free(&certs);
2592 if (ret) {
2593 pk_copy_error(context, context->hx509ctx, ret,
2594 "Failed to find PKINIT certificate");
2595 return ret;
2598 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2599 hx509_certs_free(&result);
2600 if (ret) {
2601 pk_copy_error(context, context->hx509ctx, ret,
2602 "Failed to get one cert");
2603 goto out;
2606 ret = get_ms_san(context->hx509ctx, cert, &name);
2607 if (ret) {
2608 pk_copy_error(context, context->hx509ctx, ret,
2609 "Failed to get MS SAN");
2610 goto out;
2613 ret = krb5_make_principal(context, principal, realm, name, NULL);
2614 free(name);
2615 if (ret)
2616 goto out;
2618 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2620 if (res) {
2621 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2622 if (ret)
2623 goto out;
2625 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2626 if (ret) {
2627 hx509_certs_free(res);
2628 goto out;
2632 out:
2633 hx509_cert_free(cert);
2635 return ret;
2636 #else
2637 krb5_set_error_message(context, EINVAL,
2638 N_("no support for PKINIT compiled in", ""));
2639 return EINVAL;
2640 #endif