Simplify subkey usage for tgs-req, don't rewrite tgs-rep-sub-key keyuage for arcfour...
[heimdal.git] / lib / krb5 / pkinit.c
blobf6457aa5c9712680f41b02e2841dde8df7e56389
1 /*
2 * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include "krb5_locl.h"
36 struct krb5_dh_moduli {
37 char *name;
38 unsigned long bits;
39 heim_integer p;
40 heim_integer g;
41 heim_integer q;
44 #ifdef PKINIT
46 #include <cms_asn1.h>
47 #include <pkcs8_asn1.h>
48 #include <pkcs9_asn1.h>
49 #include <pkcs12_asn1.h>
50 #include <pkinit_asn1.h>
51 #include <asn1_err.h>
53 #include <der.h>
55 struct krb5_pk_cert {
56 hx509_cert cert;
59 struct krb5_pk_init_ctx_data {
60 struct krb5_pk_identity *id;
61 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
62 union {
63 DH *dh;
64 #ifdef HAVE_OPENSSL
65 EC_KEY *eckey;
66 #endif
67 } u;
68 krb5_data *clientDHNonce;
69 struct krb5_dh_moduli **m;
70 hx509_peer_info peer;
71 enum krb5_pk_type type;
72 unsigned int require_binding:1;
73 unsigned int require_eku:1;
74 unsigned int require_krbtgt_otherName:1;
75 unsigned int require_hostname_match:1;
76 unsigned int trustedCertifiers:1;
77 unsigned int anonymous:1;
80 static void
81 pk_copy_error(krb5_context context,
82 hx509_context hx509ctx,
83 int hxret,
84 const char *fmt,
85 ...)
86 __attribute__ ((format (printf, 4, 5)));
92 void KRB5_LIB_FUNCTION
93 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
95 if (cert->cert) {
96 hx509_cert_free(cert->cert);
98 free(cert);
101 static krb5_error_code
102 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
104 integer->length = BN_num_bytes(bn);
105 integer->data = malloc(integer->length);
106 if (integer->data == NULL) {
107 krb5_clear_error_message(context);
108 return ENOMEM;
110 BN_bn2bin(bn, integer->data);
111 integer->negative = BN_is_negative(bn);
112 return 0;
115 static BIGNUM *
116 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
118 BIGNUM *bn;
120 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
121 if (bn == NULL) {
122 krb5_set_error_message(context, ENOMEM,
123 N_("PKINIT: parsing BN failed %s", ""), field);
124 return NULL;
126 BN_set_negative(bn, f->negative);
127 return bn;
130 static krb5_error_code
131 select_dh_group(krb5_context context, DH *dh, unsigned long bits,
132 struct krb5_dh_moduli **moduli)
134 const struct krb5_dh_moduli *m;
136 if (bits == 0) {
137 m = moduli[1]; /* XXX */
138 if (m == NULL)
139 m = moduli[0]; /* XXX */
140 } else {
141 int i;
142 for (i = 0; moduli[i] != NULL; i++) {
143 if (bits < moduli[i]->bits)
144 break;
146 if (moduli[i] == NULL) {
147 krb5_set_error_message(context, EINVAL,
148 N_("Did not find a DH group parameter "
149 "matching requirement of %lu bits", ""),
150 bits);
151 return EINVAL;
153 m = moduli[i];
156 dh->p = integer_to_BN(context, "p", &m->p);
157 if (dh->p == NULL)
158 return ENOMEM;
159 dh->g = integer_to_BN(context, "g", &m->g);
160 if (dh->g == NULL)
161 return ENOMEM;
162 dh->q = integer_to_BN(context, "q", &m->q);
163 if (dh->q == NULL)
164 return ENOMEM;
166 return 0;
169 struct certfind {
170 const char *type;
171 const heim_oid *oid;
175 * Try searchin the key by to use by first looking for for PK-INIT
176 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
179 static krb5_error_code
180 find_cert(krb5_context context, struct krb5_pk_identity *id,
181 hx509_query *q, hx509_cert *cert)
183 struct certfind cf[3] = {
184 { "PKINIT EKU" },
185 { "MS EKU" },
186 { "any (or no)" }
188 int i, ret;
190 cf[0].oid = &asn1_oid_id_pkekuoid;
191 cf[1].oid = &asn1_oid_id_pkinit_ms_eku;
192 cf[2].oid = NULL;
194 for (i = 0; i < sizeof(cf)/sizeof(cf[0]); i++) {
195 ret = hx509_query_match_eku(q, cf[i].oid);
196 if (ret) {
197 pk_copy_error(context, context->hx509ctx, ret,
198 "Failed setting %s OID", cf[i].type);
199 return ret;
202 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
203 if (ret == 0)
204 break;
205 pk_copy_error(context, context->hx509ctx, ret,
206 "Failed finding certificate with %s OID", cf[i].type);
208 return ret;
212 static krb5_error_code
213 create_signature(krb5_context context,
214 const heim_oid *eContentType,
215 krb5_data *eContent,
216 struct krb5_pk_identity *id,
217 hx509_peer_info peer,
218 krb5_data *sd_data)
220 int ret, flags = 0;
222 if (id->cert == NULL)
223 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
225 ret = hx509_cms_create_signed_1(context->hx509ctx,
226 flags,
227 eContentType,
228 eContent->data,
229 eContent->length,
230 NULL,
231 id->cert,
232 peer,
233 NULL,
234 id->certs,
235 sd_data);
236 if (ret) {
237 pk_copy_error(context, context->hx509ctx, ret,
238 "Create CMS signedData");
239 return ret;
242 return 0;
245 static int
246 cert2epi(hx509_context context, void *ctx, hx509_cert c)
248 ExternalPrincipalIdentifiers *ids = ctx;
249 ExternalPrincipalIdentifier id;
250 hx509_name subject = NULL;
251 void *p;
252 int ret;
254 if (ids->len > 10)
255 return 0;
257 memset(&id, 0, sizeof(id));
259 ret = hx509_cert_get_subject(c, &subject);
260 if (ret)
261 return ret;
263 if (hx509_name_is_null_p(subject) != 0) {
265 id.subjectName = calloc(1, sizeof(*id.subjectName));
266 if (id.subjectName == NULL) {
267 hx509_name_free(&subject);
268 free_ExternalPrincipalIdentifier(&id);
269 return ENOMEM;
272 ret = hx509_name_binary(subject, id.subjectName);
273 if (ret) {
274 hx509_name_free(&subject);
275 free_ExternalPrincipalIdentifier(&id);
276 return ret;
279 hx509_name_free(&subject);
282 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
283 if (id.issuerAndSerialNumber == NULL) {
284 free_ExternalPrincipalIdentifier(&id);
285 return ENOMEM;
289 IssuerAndSerialNumber iasn;
290 hx509_name issuer;
291 size_t size;
293 memset(&iasn, 0, sizeof(iasn));
295 ret = hx509_cert_get_issuer(c, &issuer);
296 if (ret) {
297 free_ExternalPrincipalIdentifier(&id);
298 return ret;
301 ret = hx509_name_to_Name(issuer, &iasn.issuer);
302 hx509_name_free(&issuer);
303 if (ret) {
304 free_ExternalPrincipalIdentifier(&id);
305 return ret;
308 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
309 if (ret) {
310 free_IssuerAndSerialNumber(&iasn);
311 free_ExternalPrincipalIdentifier(&id);
312 return ret;
315 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
316 id.issuerAndSerialNumber->data,
317 id.issuerAndSerialNumber->length,
318 &iasn, &size, ret);
319 free_IssuerAndSerialNumber(&iasn);
320 if (ret)
321 return ret;
322 if (id.issuerAndSerialNumber->length != size)
323 abort();
326 id.subjectKeyIdentifier = NULL;
328 p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
329 if (p == NULL) {
330 free_ExternalPrincipalIdentifier(&id);
331 return ENOMEM;
334 ids->val = p;
335 ids->val[ids->len] = id;
336 ids->len++;
338 return 0;
341 static krb5_error_code
342 build_edi(krb5_context context,
343 hx509_context hx509ctx,
344 hx509_certs certs,
345 ExternalPrincipalIdentifiers *ids)
347 return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
350 static krb5_error_code
351 build_auth_pack(krb5_context context,
352 unsigned nonce,
353 krb5_pk_init_ctx ctx,
354 const KDC_REQ_BODY *body,
355 AuthPack *a)
357 size_t buf_size, len;
358 krb5_error_code ret;
359 void *buf;
360 krb5_timestamp sec;
361 int32_t usec;
362 Checksum checksum;
364 krb5_clear_error_message(context);
366 memset(&checksum, 0, sizeof(checksum));
368 krb5_us_timeofday(context, &sec, &usec);
369 a->pkAuthenticator.ctime = sec;
370 a->pkAuthenticator.nonce = nonce;
372 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
373 if (ret)
374 return ret;
375 if (buf_size != len)
376 krb5_abortx(context, "internal error in ASN.1 encoder");
378 ret = krb5_create_checksum(context,
379 NULL,
381 CKSUMTYPE_SHA1,
382 buf,
383 len,
384 &checksum);
385 free(buf);
386 if (ret)
387 return ret;
389 ALLOC(a->pkAuthenticator.paChecksum, 1);
390 if (a->pkAuthenticator.paChecksum == NULL) {
391 krb5_set_error_message(context, ENOMEM,
392 N_("malloc: out of memory", ""));
393 return ENOMEM;
396 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
397 checksum.checksum.data, checksum.checksum.length);
398 free_Checksum(&checksum);
399 if (ret)
400 return ret;
402 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
403 const char *moduli_file;
404 unsigned long dh_min_bits;
405 krb5_data dhbuf;
406 size_t size;
408 krb5_data_zero(&dhbuf);
412 moduli_file = krb5_config_get_string(context, NULL,
413 "libdefaults",
414 "moduli",
415 NULL);
417 dh_min_bits =
418 krb5_config_get_int_default(context, NULL, 0,
419 "libdefaults",
420 "pkinit_dh_min_bits",
421 NULL);
423 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
424 if (ret)
425 return ret;
427 ctx->u.dh = DH_new();
428 if (ctx->u.dh == NULL) {
429 krb5_set_error_message(context, ENOMEM,
430 N_("malloc: out of memory", ""));
431 return ENOMEM;
434 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
435 if (ret)
436 return ret;
438 if (DH_generate_key(ctx->u.dh) != 1) {
439 krb5_set_error_message(context, ENOMEM,
440 N_("pkinit: failed to generate DH key", ""));
441 return ENOMEM;
445 if (1 /* support_cached_dh */) {
446 ALLOC(a->clientDHNonce, 1);
447 if (a->clientDHNonce == NULL) {
448 krb5_clear_error_message(context);
449 return ENOMEM;
451 ret = krb5_data_alloc(a->clientDHNonce, 40);
452 if (a->clientDHNonce == NULL) {
453 krb5_clear_error_message(context);
454 return ret;
456 RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
457 ret = krb5_copy_data(context, a->clientDHNonce,
458 &ctx->clientDHNonce);
459 if (ret)
460 return ret;
463 ALLOC(a->clientPublicValue, 1);
464 if (a->clientPublicValue == NULL)
465 return ENOMEM;
467 if (ctx->keyex == USE_DH) {
468 DH *dh = ctx->u.dh;
469 DomainParameters dp;
470 heim_integer dh_pub_key;
472 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
473 &a->clientPublicValue->algorithm.algorithm);
474 if (ret)
475 return ret;
477 memset(&dp, 0, sizeof(dp));
479 ret = BN_to_integer(context, dh->p, &dp.p);
480 if (ret) {
481 free_DomainParameters(&dp);
482 return ret;
484 ret = BN_to_integer(context, dh->g, &dp.g);
485 if (ret) {
486 free_DomainParameters(&dp);
487 return ret;
489 ret = BN_to_integer(context, dh->q, &dp.q);
490 if (ret) {
491 free_DomainParameters(&dp);
492 return ret;
494 dp.j = NULL;
495 dp.validationParms = NULL;
497 a->clientPublicValue->algorithm.parameters =
498 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
499 if (a->clientPublicValue->algorithm.parameters == NULL) {
500 free_DomainParameters(&dp);
501 return ret;
504 ASN1_MALLOC_ENCODE(DomainParameters,
505 a->clientPublicValue->algorithm.parameters->data,
506 a->clientPublicValue->algorithm.parameters->length,
507 &dp, &size, ret);
508 free_DomainParameters(&dp);
509 if (ret)
510 return ret;
511 if (size != a->clientPublicValue->algorithm.parameters->length)
512 krb5_abortx(context, "Internal ASN1 encoder error");
514 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
515 if (ret)
516 return ret;
518 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
519 &dh_pub_key, &size, ret);
520 der_free_heim_integer(&dh_pub_key);
521 if (ret)
522 return ret;
523 if (size != dhbuf.length)
524 krb5_abortx(context, "asn1 internal error");
525 } else if (ctx->keyex == USE_ECDH) {
526 #ifdef HAVE_OPENSSL
527 ECParameters ecp;
528 unsigned char *p;
529 int len;
531 /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
533 ecp.element = choice_ECParameters_namedCurve;
534 ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
535 &ecp.u.namedCurve);
536 if (ret)
537 return ret;
539 ALLOC(a->clientPublicValue->algorithm.parameters, 1);
540 if (a->clientPublicValue->algorithm.parameters == NULL) {
541 free_ECParameters(&ecp);
542 return ENOMEM;
544 ASN1_MALLOC_ENCODE(ECParameters, p, len, &ecp, &size, ret);
545 free_ECParameters(&ecp);
546 if (ret)
547 return ret;
548 if (size != len)
549 krb5_abortx(context, "asn1 internal error");
551 a->clientPublicValue->algorithm.parameters->data = p;
552 a->clientPublicValue->algorithm.parameters->length = size;
554 /* copy in public key */
556 ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
557 &a->clientPublicValue->algorithm.algorithm);
558 if (ret)
559 return ret;
561 ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
562 if (ctx->u.eckey == NULL)
563 return ENOMEM;
565 ret = EC_KEY_generate_key(ctx->u.eckey);
566 if (ret != 1)
567 return EINVAL;
569 /* encode onto dhkey */
571 len = i2o_ECPublicKey(ctx->u.eckey, NULL);
572 if (len <= 0)
573 abort();
575 dhbuf.data = malloc(len);
576 if (dhbuf.data == NULL)
577 abort();
578 dhbuf.length = len;
579 p = dhbuf.data;
581 len = i2o_ECPublicKey(ctx->u.eckey, &p);
582 if (len <= 0)
583 abort();
585 /* XXX verify that this is right with RFC3279 */
586 #else
587 return EINVAL;
588 #endif
589 } else
590 krb5_abortx(context, "internal error");
591 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
592 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
596 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
597 if (a->supportedCMSTypes == NULL)
598 return ENOMEM;
600 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, NULL,
601 &a->supportedCMSTypes->val,
602 &a->supportedCMSTypes->len);
603 if (ret)
604 return ret;
607 return ret;
610 krb5_error_code KRB5_LIB_FUNCTION
611 _krb5_pk_mk_ContentInfo(krb5_context context,
612 const krb5_data *buf,
613 const heim_oid *oid,
614 struct ContentInfo *content_info)
616 krb5_error_code ret;
618 ret = der_copy_oid(oid, &content_info->contentType);
619 if (ret)
620 return ret;
621 ALLOC(content_info->content, 1);
622 if (content_info->content == NULL)
623 return ENOMEM;
624 content_info->content->data = malloc(buf->length);
625 if (content_info->content->data == NULL)
626 return ENOMEM;
627 memcpy(content_info->content->data, buf->data, buf->length);
628 content_info->content->length = buf->length;
629 return 0;
632 static krb5_error_code
633 pk_mk_padata(krb5_context context,
634 krb5_pk_init_ctx ctx,
635 const KDC_REQ_BODY *req_body,
636 unsigned nonce,
637 METHOD_DATA *md)
639 struct ContentInfo content_info;
640 krb5_error_code ret;
641 const heim_oid *oid;
642 size_t size;
643 krb5_data buf, sd_buf;
644 int pa_type;
646 krb5_data_zero(&buf);
647 krb5_data_zero(&sd_buf);
648 memset(&content_info, 0, sizeof(content_info));
650 if (ctx->type == PKINIT_WIN2K) {
651 AuthPack_Win2k ap;
652 krb5_timestamp sec;
653 int32_t usec;
655 memset(&ap, 0, sizeof(ap));
657 /* fill in PKAuthenticator */
658 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
659 if (ret) {
660 free_AuthPack_Win2k(&ap);
661 krb5_clear_error_message(context);
662 goto out;
664 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
665 if (ret) {
666 free_AuthPack_Win2k(&ap);
667 krb5_clear_error_message(context);
668 goto out;
671 krb5_us_timeofday(context, &sec, &usec);
672 ap.pkAuthenticator.ctime = sec;
673 ap.pkAuthenticator.cusec = usec;
674 ap.pkAuthenticator.nonce = nonce;
676 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
677 &ap, &size, ret);
678 free_AuthPack_Win2k(&ap);
679 if (ret) {
680 krb5_set_error_message(context, ret,
681 N_("Failed encoding AuthPackWin: %d", ""),
682 (int)ret);
683 goto out;
685 if (buf.length != size)
686 krb5_abortx(context, "internal ASN1 encoder error");
688 oid = &asn1_oid_id_pkcs7_data;
689 } else if (ctx->type == PKINIT_27) {
690 AuthPack ap;
692 memset(&ap, 0, sizeof(ap));
694 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
695 if (ret) {
696 free_AuthPack(&ap);
697 goto out;
700 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
701 free_AuthPack(&ap);
702 if (ret) {
703 krb5_set_error_message(context, ret,
704 N_("Failed encoding AuthPack: %d", ""),
705 (int)ret);
706 goto out;
708 if (buf.length != size)
709 krb5_abortx(context, "internal ASN1 encoder error");
711 oid = &asn1_oid_id_pkauthdata;
712 } else
713 krb5_abortx(context, "internal pkinit error");
715 ret = create_signature(context, oid, &buf, ctx->id,
716 ctx->peer, &sd_buf);
717 krb5_data_free(&buf);
718 if (ret)
719 goto out;
721 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
722 krb5_data_free(&sd_buf);
723 if (ret) {
724 krb5_set_error_message(context, ret,
725 N_("ContentInfo wrapping of signedData failed",""));
726 goto out;
729 if (ctx->type == PKINIT_WIN2K) {
730 PA_PK_AS_REQ_Win2k winreq;
732 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
734 memset(&winreq, 0, sizeof(winreq));
736 winreq.signed_auth_pack = buf;
738 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
739 &winreq, &size, ret);
740 free_PA_PK_AS_REQ_Win2k(&winreq);
742 } else if (ctx->type == PKINIT_27) {
743 PA_PK_AS_REQ req;
745 pa_type = KRB5_PADATA_PK_AS_REQ;
747 memset(&req, 0, sizeof(req));
748 req.signedAuthPack = buf;
750 if (ctx->trustedCertifiers) {
752 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
753 if (req.trustedCertifiers == NULL) {
754 ret = ENOMEM;
755 krb5_set_error_message(context, ret,
756 N_("malloc: out of memory", ""));
757 free_PA_PK_AS_REQ(&req);
758 goto out;
760 ret = build_edi(context, context->hx509ctx,
761 ctx->id->anchors, req.trustedCertifiers);
762 if (ret) {
763 krb5_set_error_message(context, ret,
764 N_("pk-init: failed to build "
765 "trustedCertifiers", ""));
766 free_PA_PK_AS_REQ(&req);
767 goto out;
770 req.kdcPkId = NULL;
772 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
773 &req, &size, ret);
775 free_PA_PK_AS_REQ(&req);
777 } else
778 krb5_abortx(context, "internal pkinit error");
779 if (ret) {
780 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
781 goto out;
783 if (buf.length != size)
784 krb5_abortx(context, "Internal ASN1 encoder error");
786 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
787 if (ret)
788 free(buf.data);
790 if (ret == 0)
791 krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
793 out:
794 free_ContentInfo(&content_info);
796 return ret;
800 krb5_error_code KRB5_LIB_FUNCTION
801 _krb5_pk_mk_padata(krb5_context context,
802 void *c,
803 const KDC_REQ_BODY *req_body,
804 unsigned nonce,
805 METHOD_DATA *md)
807 krb5_pk_init_ctx ctx = c;
808 int win2k_compat;
810 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
811 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
812 N_("PKINIT: No user certificate given", ""));
813 return HEIM_PKINIT_NO_PRIVATE_KEY;
816 win2k_compat = krb5_config_get_bool_default(context, NULL,
817 FALSE,
818 "realms",
819 req_body->realm,
820 "pkinit_win2k",
821 NULL);
823 if (win2k_compat) {
824 ctx->require_binding =
825 krb5_config_get_bool_default(context, NULL,
826 FALSE,
827 "realms",
828 req_body->realm,
829 "pkinit_win2k_require_binding",
830 NULL);
831 ctx->type = PKINIT_WIN2K;
832 } else
833 ctx->type = PKINIT_27;
835 ctx->require_eku =
836 krb5_config_get_bool_default(context, NULL,
837 TRUE,
838 "realms",
839 req_body->realm,
840 "pkinit_require_eku",
841 NULL);
842 ctx->require_krbtgt_otherName =
843 krb5_config_get_bool_default(context, NULL,
844 TRUE,
845 "realms",
846 req_body->realm,
847 "pkinit_require_krbtgt_otherName",
848 NULL);
850 ctx->require_hostname_match =
851 krb5_config_get_bool_default(context, NULL,
852 FALSE,
853 "realms",
854 req_body->realm,
855 "pkinit_require_hostname_match",
856 NULL);
858 ctx->trustedCertifiers =
859 krb5_config_get_bool_default(context, NULL,
860 TRUE,
861 "realms",
862 req_body->realm,
863 "pkinit_trustedCertifiers",
864 NULL);
866 return pk_mk_padata(context, ctx, req_body, nonce, md);
869 static krb5_error_code
870 pk_verify_sign(krb5_context context,
871 const void *data,
872 size_t length,
873 struct krb5_pk_identity *id,
874 heim_oid *contentType,
875 krb5_data *content,
876 struct krb5_pk_cert **signer)
878 hx509_certs signer_certs;
879 int ret;
881 *signer = NULL;
883 ret = hx509_cms_verify_signed(context->hx509ctx,
884 id->verify_ctx,
885 HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH|HX509_CMS_VS_NO_KU_CHECK,
886 data,
887 length,
888 NULL,
889 id->certpool,
890 contentType,
891 content,
892 &signer_certs);
893 if (ret) {
894 pk_copy_error(context, context->hx509ctx, ret,
895 "CMS verify signed failed");
896 return ret;
899 *signer = calloc(1, sizeof(**signer));
900 if (*signer == NULL) {
901 krb5_clear_error_message(context);
902 ret = ENOMEM;
903 goto out;
906 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
907 if (ret) {
908 pk_copy_error(context, context->hx509ctx, ret,
909 "Failed to get on of the signer certs");
910 goto out;
913 out:
914 hx509_certs_free(&signer_certs);
915 if (ret) {
916 if (*signer) {
917 hx509_cert_free((*signer)->cert);
918 free(*signer);
919 *signer = NULL;
923 return ret;
926 static krb5_error_code
927 get_reply_key_win(krb5_context context,
928 const krb5_data *content,
929 unsigned nonce,
930 krb5_keyblock **key)
932 ReplyKeyPack_Win2k key_pack;
933 krb5_error_code ret;
934 size_t size;
936 ret = decode_ReplyKeyPack_Win2k(content->data,
937 content->length,
938 &key_pack,
939 &size);
940 if (ret) {
941 krb5_set_error_message(context, ret,
942 N_("PKINIT decoding reply key failed", ""));
943 free_ReplyKeyPack_Win2k(&key_pack);
944 return ret;
947 if (key_pack.nonce != nonce) {
948 krb5_set_error_message(context, ret,
949 N_("PKINIT enckey nonce is wrong", ""));
950 free_ReplyKeyPack_Win2k(&key_pack);
951 return KRB5KRB_AP_ERR_MODIFIED;
954 *key = malloc (sizeof (**key));
955 if (*key == NULL) {
956 free_ReplyKeyPack_Win2k(&key_pack);
957 krb5_set_error_message(context, ENOMEM,
958 N_("malloc: out of memory", ""));
959 return ENOMEM;
962 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
963 free_ReplyKeyPack_Win2k(&key_pack);
964 if (ret) {
965 krb5_set_error_message(context, ret,
966 N_("PKINIT failed copying reply key", ""));
967 free(*key);
968 *key = NULL;
971 return ret;
974 static krb5_error_code
975 get_reply_key(krb5_context context,
976 const krb5_data *content,
977 const krb5_data *req_buffer,
978 krb5_keyblock **key)
980 ReplyKeyPack key_pack;
981 krb5_error_code ret;
982 size_t size;
984 ret = decode_ReplyKeyPack(content->data,
985 content->length,
986 &key_pack,
987 &size);
988 if (ret) {
989 krb5_set_error_message(context, ret,
990 N_("PKINIT decoding reply key failed", ""));
991 free_ReplyKeyPack(&key_pack);
992 return ret;
996 krb5_crypto crypto;
999 * XXX Verify kp.replyKey is a allowed enctype in the
1000 * configuration file
1003 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1004 if (ret) {
1005 free_ReplyKeyPack(&key_pack);
1006 return ret;
1009 ret = krb5_verify_checksum(context, crypto, 6,
1010 req_buffer->data, req_buffer->length,
1011 &key_pack.asChecksum);
1012 krb5_crypto_destroy(context, crypto);
1013 if (ret) {
1014 free_ReplyKeyPack(&key_pack);
1015 return ret;
1019 *key = malloc (sizeof (**key));
1020 if (*key == NULL) {
1021 free_ReplyKeyPack(&key_pack);
1022 krb5_set_error_message(context, ENOMEM,
1023 N_("malloc: out of memory", ""));
1024 return ENOMEM;
1027 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1028 free_ReplyKeyPack(&key_pack);
1029 if (ret) {
1030 krb5_set_error_message(context, ret,
1031 N_("PKINIT failed copying reply key", ""));
1032 free(*key);
1033 *key = NULL;
1036 return ret;
1040 static krb5_error_code
1041 pk_verify_host(krb5_context context,
1042 const char *realm,
1043 const krb5_krbhst_info *hi,
1044 struct krb5_pk_init_ctx_data *ctx,
1045 struct krb5_pk_cert *host)
1047 krb5_error_code ret = 0;
1049 if (ctx->require_eku) {
1050 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1051 &asn1_oid_id_pkkdcekuoid, 0);
1052 if (ret) {
1053 krb5_set_error_message(context, ret,
1054 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1055 return ret;
1058 if (ctx->require_krbtgt_otherName) {
1059 hx509_octet_string_list list;
1060 int i;
1062 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1063 host->cert,
1064 &asn1_oid_id_pkinit_san,
1065 &list);
1066 if (ret) {
1067 krb5_set_error_message(context, ret,
1068 N_("Failed to find the PK-INIT "
1069 "subjectAltName in the KDC "
1070 "certificate", ""));
1072 return ret;
1075 for (i = 0; i < list.len; i++) {
1076 KRB5PrincipalName r;
1078 ret = decode_KRB5PrincipalName(list.val[i].data,
1079 list.val[i].length,
1081 NULL);
1082 if (ret) {
1083 krb5_set_error_message(context, ret,
1084 N_("Failed to decode the PK-INIT "
1085 "subjectAltName in the "
1086 "KDC certificate", ""));
1088 break;
1091 if (r.principalName.name_string.len != 2 ||
1092 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1093 strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1094 strcmp(r.realm, realm) != 0)
1096 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1097 krb5_set_error_message(context, ret,
1098 N_("KDC have wrong realm name in "
1099 "the certificate", ""));
1102 free_KRB5PrincipalName(&r);
1103 if (ret)
1104 break;
1106 hx509_free_octet_string_list(&list);
1108 if (ret)
1109 return ret;
1111 if (hi) {
1112 ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1113 ctx->require_hostname_match,
1114 HX509_HN_HOSTNAME,
1115 hi->hostname,
1116 hi->ai->ai_addr, hi->ai->ai_addrlen);
1118 if (ret)
1119 krb5_set_error_message(context, ret,
1120 N_("Address mismatch in "
1121 "the KDC certificate", ""));
1123 return ret;
1126 static krb5_error_code
1127 pk_rd_pa_reply_enckey(krb5_context context,
1128 int type,
1129 const heim_octet_string *indata,
1130 const heim_oid *dataType,
1131 const char *realm,
1132 krb5_pk_init_ctx ctx,
1133 krb5_enctype etype,
1134 const krb5_krbhst_info *hi,
1135 unsigned nonce,
1136 const krb5_data *req_buffer,
1137 PA_DATA *pa,
1138 krb5_keyblock **key)
1140 krb5_error_code ret;
1141 struct krb5_pk_cert *host = NULL;
1142 krb5_data content;
1143 heim_oid contentType = { 0, NULL };
1144 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1146 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1147 krb5_set_error_message(context, EINVAL,
1148 N_("PKINIT: Invalid content type", ""));
1149 return EINVAL;
1152 if (ctx->type == PKINIT_WIN2K)
1153 flags |= HX509_CMS_UE_ALLOW_WEAK;
1155 ret = hx509_cms_unenvelope(context->hx509ctx,
1156 ctx->id->certs,
1157 flags,
1158 indata->data,
1159 indata->length,
1160 NULL,
1162 &contentType,
1163 &content);
1164 if (ret) {
1165 pk_copy_error(context, context->hx509ctx, ret,
1166 "Failed to unenvelope CMS data in PK-INIT reply");
1167 return ret;
1169 der_free_oid(&contentType);
1171 /* win2k uses ContentInfo */
1172 if (type == PKINIT_WIN2K) {
1173 heim_oid type;
1174 heim_octet_string out;
1176 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1177 if (ret) {
1178 /* windows LH with interesting CMS packets */
1179 size_t ph = 1 + der_length_len(content.length);
1180 unsigned char *ptr = malloc(content.length + ph);
1181 size_t l;
1183 memcpy(ptr + ph, content.data, content.length);
1185 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1186 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1187 if (ret)
1188 return ret;
1189 free(content.data);
1190 content.data = ptr;
1191 content.length += ph;
1193 ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1194 if (ret)
1195 goto out;
1197 if (der_heim_oid_cmp(&type, &asn1_oid_id_pkcs7_signedData)) {
1198 ret = EINVAL; /* XXX */
1199 krb5_set_error_message(context, ret,
1200 N_("PKINIT: Invalid content type", ""));
1201 der_free_oid(&type);
1202 der_free_octet_string(&out);
1203 goto out;
1205 der_free_oid(&type);
1206 krb5_data_free(&content);
1207 ret = krb5_data_copy(&content, out.data, out.length);
1208 der_free_octet_string(&out);
1209 if (ret) {
1210 krb5_set_error_message(context, ret,
1211 N_("malloc: out of memory", ""));
1212 goto out;
1216 ret = pk_verify_sign(context,
1217 content.data,
1218 content.length,
1219 ctx->id,
1220 &contentType,
1221 &content,
1222 &host);
1223 if (ret)
1224 goto out;
1226 /* make sure that it is the kdc's certificate */
1227 ret = pk_verify_host(context, realm, hi, ctx, host);
1228 if (ret) {
1229 goto out;
1232 #if 0
1233 if (type == PKINIT_WIN2K) {
1234 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1235 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1236 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1237 goto out;
1239 } else {
1240 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1241 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1242 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1243 goto out;
1246 #endif
1248 switch(type) {
1249 case PKINIT_WIN2K:
1250 ret = get_reply_key(context, &content, req_buffer, key);
1251 if (ret != 0 && ctx->require_binding == 0)
1252 ret = get_reply_key_win(context, &content, nonce, key);
1253 break;
1254 case PKINIT_27:
1255 ret = get_reply_key(context, &content, req_buffer, key);
1256 break;
1258 if (ret)
1259 goto out;
1261 /* XXX compare given etype with key->etype */
1263 out:
1264 if (host)
1265 _krb5_pk_cert_free(host);
1266 der_free_oid(&contentType);
1267 krb5_data_free(&content);
1269 return ret;
1272 static krb5_error_code
1273 pk_rd_pa_reply_dh(krb5_context context,
1274 const heim_octet_string *indata,
1275 const heim_oid *dataType,
1276 const char *realm,
1277 krb5_pk_init_ctx ctx,
1278 krb5_enctype etype,
1279 const krb5_krbhst_info *hi,
1280 const DHNonce *c_n,
1281 const DHNonce *k_n,
1282 unsigned nonce,
1283 PA_DATA *pa,
1284 krb5_keyblock **key)
1286 const unsigned char *p;
1287 unsigned char *dh_gen_key = NULL;
1288 struct krb5_pk_cert *host = NULL;
1289 BIGNUM *kdc_dh_pubkey = NULL;
1290 KDCDHKeyInfo kdc_dh_info;
1291 heim_oid contentType = { 0, NULL };
1292 krb5_data content;
1293 krb5_error_code ret;
1294 int dh_gen_keylen = 0;
1295 size_t size;
1297 krb5_data_zero(&content);
1298 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1300 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1301 krb5_set_error_message(context, EINVAL,
1302 N_("PKINIT: Invalid content type", ""));
1303 return EINVAL;
1306 ret = pk_verify_sign(context,
1307 indata->data,
1308 indata->length,
1309 ctx->id,
1310 &contentType,
1311 &content,
1312 &host);
1313 if (ret)
1314 goto out;
1316 /* make sure that it is the kdc's certificate */
1317 ret = pk_verify_host(context, realm, hi, ctx, host);
1318 if (ret)
1319 goto out;
1321 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1322 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1323 krb5_set_error_message(context, ret,
1324 N_("pkinit - dh reply contains wrong oid", ""));
1325 goto out;
1328 ret = decode_KDCDHKeyInfo(content.data,
1329 content.length,
1330 &kdc_dh_info,
1331 &size);
1333 if (ret) {
1334 krb5_set_error_message(context, ret,
1335 N_("pkinit - failed to decode "
1336 "KDC DH Key Info", ""));
1337 goto out;
1340 if (kdc_dh_info.nonce != nonce) {
1341 ret = KRB5KRB_AP_ERR_MODIFIED;
1342 krb5_set_error_message(context, ret,
1343 N_("PKINIT: DH nonce is wrong", ""));
1344 goto out;
1347 if (kdc_dh_info.dhKeyExpiration) {
1348 if (k_n == NULL) {
1349 ret = KRB5KRB_ERR_GENERIC;
1350 krb5_set_error_message(context, ret,
1351 N_("pkinit; got key expiration "
1352 "without server nonce", ""));
1353 goto out;
1355 if (c_n == NULL) {
1356 ret = KRB5KRB_ERR_GENERIC;
1357 krb5_set_error_message(context, ret,
1358 N_("pkinit; got DH reuse but no "
1359 "client nonce", ""));
1360 goto out;
1362 } else {
1363 if (k_n) {
1364 ret = KRB5KRB_ERR_GENERIC;
1365 krb5_set_error_message(context, ret,
1366 N_("pkinit: got server nonce "
1367 "without key expiration", ""));
1368 goto out;
1370 c_n = NULL;
1374 p = kdc_dh_info.subjectPublicKey.data;
1375 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1377 if (ctx->keyex == USE_DH) {
1378 DHPublicKey k;
1379 ret = decode_DHPublicKey(p, size, &k, NULL);
1380 if (ret) {
1381 krb5_set_error_message(context, ret,
1382 N_("pkinit: can't decode "
1383 "without key expiration", ""));
1384 goto out;
1387 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1388 free_DHPublicKey(&k);
1389 if (kdc_dh_pubkey == NULL) {
1390 ret = ENOMEM;
1391 goto out;
1395 dh_gen_keylen = DH_size(ctx->u.dh);
1396 size = BN_num_bytes(ctx->u.dh->p);
1397 if (size < dh_gen_keylen)
1398 size = dh_gen_keylen;
1400 dh_gen_key = malloc(size);
1401 if (dh_gen_key == NULL) {
1402 ret = ENOMEM;
1403 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1404 goto out;
1406 memset(dh_gen_key, 0, size - dh_gen_keylen);
1408 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1409 kdc_dh_pubkey, ctx->u.dh);
1410 if (dh_gen_keylen == -1) {
1411 ret = KRB5KRB_ERR_GENERIC;
1412 dh_gen_keylen = 0;
1413 krb5_set_error_message(context, ret,
1414 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1415 goto out;
1417 } else {
1418 #ifdef HAVE_OPENSSL
1419 const EC_GROUP *group;
1420 EC_KEY *public = NULL;
1422 group = EC_KEY_get0_group(ctx->u.eckey);
1424 public = EC_KEY_new();
1425 if (public == NULL) {
1426 ret = ENOMEM;
1427 goto out;
1429 if (EC_KEY_set_group(public, group) != 1) {
1430 EC_KEY_free(public);
1431 ret = ENOMEM;
1432 goto out;
1435 if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1436 EC_KEY_free(public);
1437 ret = KRB5KRB_ERR_GENERIC;
1438 krb5_set_error_message(context, ret,
1439 N_("PKINIT: Can't parse ECDH public key", ""));
1440 goto out;
1443 size = (EC_GROUP_get_degree(group) + 7) / 8;
1444 dh_gen_key = malloc(size);
1445 if (dh_gen_key == NULL) {
1446 EC_KEY_free(public);
1447 ret = ENOMEM;
1448 krb5_set_error_message(context, ret,
1449 N_("malloc: out of memory", ""));
1450 goto out;
1452 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1453 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1454 EC_KEY_free(public);
1455 if (dh_gen_keylen == -1) {
1456 ret = KRB5KRB_ERR_GENERIC;
1457 dh_gen_keylen = 0;
1458 krb5_set_error_message(context, ret,
1459 N_("PKINIT: Can't compute ECDH public key", ""));
1460 goto out;
1462 #else
1463 ret = EINVAL;
1464 #endif
1467 if (dh_gen_keylen <= 0) {
1468 ret = EINVAL;
1469 krb5_set_error_message(context, ret,
1470 N_("PKINIT: resulting DH key <= 0", ""));
1471 dh_gen_keylen = 0;
1472 goto out;
1475 *key = malloc (sizeof (**key));
1476 if (*key == NULL) {
1477 ret = ENOMEM;
1478 krb5_set_error_message(context, ret,
1479 N_("malloc: out of memory", ""));
1480 goto out;
1483 ret = _krb5_pk_octetstring2key(context,
1484 etype,
1485 dh_gen_key, dh_gen_keylen,
1486 c_n, k_n,
1487 *key);
1488 if (ret) {
1489 krb5_set_error_message(context, ret,
1490 N_("PKINIT: can't create key from DH key", ""));
1491 free(*key);
1492 *key = NULL;
1493 goto out;
1496 out:
1497 if (kdc_dh_pubkey)
1498 BN_free(kdc_dh_pubkey);
1499 if (dh_gen_key) {
1500 memset(dh_gen_key, 0, dh_gen_keylen);
1501 free(dh_gen_key);
1503 if (host)
1504 _krb5_pk_cert_free(host);
1505 if (content.data)
1506 krb5_data_free(&content);
1507 der_free_oid(&contentType);
1508 free_KDCDHKeyInfo(&kdc_dh_info);
1510 return ret;
1513 krb5_error_code KRB5_LIB_FUNCTION
1514 _krb5_pk_rd_pa_reply(krb5_context context,
1515 const char *realm,
1516 void *c,
1517 krb5_enctype etype,
1518 const krb5_krbhst_info *hi,
1519 unsigned nonce,
1520 const krb5_data *req_buffer,
1521 PA_DATA *pa,
1522 krb5_keyblock **key)
1524 krb5_pk_init_ctx ctx = c;
1525 krb5_error_code ret;
1526 size_t size;
1528 /* Check for IETF PK-INIT first */
1529 if (ctx->type == PKINIT_27) {
1530 PA_PK_AS_REP rep;
1531 heim_octet_string os, data;
1532 heim_oid oid;
1534 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1535 krb5_set_error_message(context, EINVAL,
1536 N_("PKINIT: wrong padata recv", ""));
1537 return EINVAL;
1540 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1541 pa->padata_value.length,
1542 &rep,
1543 &size);
1544 if (ret) {
1545 krb5_set_error_message(context, ret,
1546 N_("Failed to decode pkinit AS rep", ""));
1547 return ret;
1550 switch (rep.element) {
1551 case choice_PA_PK_AS_REP_dhInfo:
1552 os = rep.u.dhInfo.dhSignedData;
1553 break;
1554 case choice_PA_PK_AS_REP_encKeyPack:
1555 os = rep.u.encKeyPack;
1556 break;
1557 default: {
1558 PA_PK_AS_REP_BTMM btmm;
1559 free_PA_PK_AS_REP(&rep);
1560 memset(&rep, 0, sizeof(rep));
1562 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1563 pa->padata_value.length,
1564 &btmm,
1565 &size);
1566 if (ret) {
1567 krb5_set_error_message(context, EINVAL,
1568 N_("PKINIT: -27 reply "
1569 "invalid content type", ""));
1570 return EINVAL;
1573 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1574 free_PA_PK_AS_REP_BTMM(&btmm);
1575 ret = EINVAL;
1576 krb5_set_error_message(context, ret,
1577 N_("DH mode not supported for BTMM mode", ""));
1578 return ret;
1582 * Transform to IETF style PK-INIT reply so that free works below
1585 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1586 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1587 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1588 btmm.encKeyPack->data = NULL;
1589 btmm.encKeyPack->length = 0;
1590 free_PA_PK_AS_REP_BTMM(&btmm);
1591 os = rep.u.encKeyPack;
1595 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1596 if (ret) {
1597 free_PA_PK_AS_REP(&rep);
1598 krb5_set_error_message(context, ret,
1599 N_("PKINIT: failed to unwrap CI", ""));
1600 return ret;
1603 switch (rep.element) {
1604 case choice_PA_PK_AS_REP_dhInfo:
1605 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1606 ctx->clientDHNonce,
1607 rep.u.dhInfo.serverDHNonce,
1608 nonce, pa, key);
1609 break;
1610 case choice_PA_PK_AS_REP_encKeyPack:
1611 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1612 ctx, etype, hi, nonce, req_buffer, pa, key);
1613 break;
1614 default:
1615 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1617 der_free_octet_string(&data);
1618 der_free_oid(&oid);
1619 free_PA_PK_AS_REP(&rep);
1621 } else if (ctx->type == PKINIT_WIN2K) {
1622 PA_PK_AS_REP_Win2k w2krep;
1624 /* Check for Windows encoding of the AS-REP pa data */
1626 #if 0 /* should this be ? */
1627 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1628 krb5_set_error_message(context, EINVAL,
1629 "PKINIT: wrong padata recv");
1630 return EINVAL;
1632 #endif
1634 memset(&w2krep, 0, sizeof(w2krep));
1636 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1637 pa->padata_value.length,
1638 &w2krep,
1639 &size);
1640 if (ret) {
1641 krb5_set_error_message(context, ret,
1642 N_("PKINIT: Failed decoding windows "
1643 "pkinit reply %d", ""), (int)ret);
1644 return ret;
1647 krb5_clear_error_message(context);
1649 switch (w2krep.element) {
1650 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1651 heim_octet_string data;
1652 heim_oid oid;
1654 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1655 &oid, &data, NULL);
1656 free_PA_PK_AS_REP_Win2k(&w2krep);
1657 if (ret) {
1658 krb5_set_error_message(context, ret,
1659 N_("PKINIT: failed to unwrap CI", ""));
1660 return ret;
1663 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1664 ctx, etype, hi, nonce, req_buffer, pa, key);
1665 der_free_octet_string(&data);
1666 der_free_oid(&oid);
1668 break;
1670 default:
1671 free_PA_PK_AS_REP_Win2k(&w2krep);
1672 ret = EINVAL;
1673 krb5_set_error_message(context, ret,
1674 N_("PKINIT: win2k reply invalid "
1675 "content type", ""));
1676 break;
1679 } else {
1680 ret = EINVAL;
1681 krb5_set_error_message(context, ret,
1682 N_("PKINIT: unknown reply type", ""));
1685 return ret;
1688 struct prompter {
1689 krb5_context context;
1690 krb5_prompter_fct prompter;
1691 void *prompter_data;
1694 static int
1695 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1697 krb5_error_code ret;
1698 krb5_prompt prompt;
1699 krb5_data password_data;
1700 struct prompter *p = data;
1702 password_data.data = prompter->reply.data;
1703 password_data.length = prompter->reply.length;
1705 prompt.prompt = prompter->prompt;
1706 prompt.hidden = hx509_prompt_hidden(prompter->type);
1707 prompt.reply = &password_data;
1709 switch (prompter->type) {
1710 case HX509_PROMPT_TYPE_INFO:
1711 prompt.type = KRB5_PROMPT_TYPE_INFO;
1712 break;
1713 case HX509_PROMPT_TYPE_PASSWORD:
1714 case HX509_PROMPT_TYPE_QUESTION:
1715 default:
1716 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1717 break;
1720 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1721 if (ret) {
1722 memset (prompter->reply.data, 0, prompter->reply.length);
1723 return 1;
1725 return 0;
1728 static krb5_error_code
1729 _krb5_pk_set_user_id(krb5_context context,
1730 krb5_pk_init_ctx ctx,
1731 struct hx509_certs_data *certs)
1733 hx509_certs c = hx509_certs_ref(certs);
1734 hx509_query *q = NULL;
1735 int ret;
1737 if (ctx->id->certs)
1738 hx509_certs_free(&ctx->id->certs);
1739 if (ctx->id->cert) {
1740 hx509_cert_free(ctx->id->cert);
1741 ctx->id->cert = NULL;
1744 ctx->id->certs = c;
1745 ctx->anonymous = 0;
1747 ret = hx509_query_alloc(context->hx509ctx, &q);
1748 if (ret) {
1749 pk_copy_error(context, context->hx509ctx, ret,
1750 "Allocate query to find signing certificate");
1751 return ret;
1754 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1755 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1757 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1758 hx509_query_free(context->hx509ctx, q);
1760 return ret;
1763 krb5_error_code KRB5_LIB_FUNCTION
1764 _krb5_pk_load_id(krb5_context context,
1765 struct krb5_pk_identity **ret_id,
1766 const char *user_id,
1767 const char *anchor_id,
1768 char * const *chain_list,
1769 char * const *revoke_list,
1770 krb5_prompter_fct prompter,
1771 void *prompter_data,
1772 char *password)
1774 struct krb5_pk_identity *id = NULL;
1775 struct prompter p;
1776 int ret;
1778 *ret_id = NULL;
1780 if (anchor_id == NULL) {
1781 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1782 N_("PKINIT: No anchor given", ""));
1783 return HEIM_PKINIT_NO_VALID_CA;
1786 /* load cert */
1788 id = calloc(1, sizeof(*id));
1789 if (id == NULL) {
1790 krb5_set_error_message(context, ENOMEM,
1791 N_("malloc: out of memory", ""));
1792 return ENOMEM;
1795 if (user_id) {
1796 hx509_lock lock;
1798 ret = hx509_lock_init(context->hx509ctx, &lock);
1799 if (ret) {
1800 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1801 goto out;
1804 if (password && password[0])
1805 hx509_lock_add_password(lock, password);
1807 if (prompter) {
1808 p.context = context;
1809 p.prompter = prompter;
1810 p.prompter_data = prompter_data;
1812 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1813 if (ret) {
1814 hx509_lock_free(lock);
1815 goto out;
1819 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1820 hx509_lock_free(lock);
1821 if (ret) {
1822 pk_copy_error(context, context->hx509ctx, ret,
1823 "Failed to init cert certs");
1824 goto out;
1826 } else {
1827 id->certs = NULL;
1830 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1831 if (ret) {
1832 pk_copy_error(context, context->hx509ctx, ret,
1833 "Failed to init anchors");
1834 goto out;
1837 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1838 0, NULL, &id->certpool);
1839 if (ret) {
1840 pk_copy_error(context, context->hx509ctx, ret,
1841 "Failed to init chain");
1842 goto out;
1845 while (chain_list && *chain_list) {
1846 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1847 NULL, *chain_list);
1848 if (ret) {
1849 pk_copy_error(context, context->hx509ctx, ret,
1850 "Failed to laod chain %s",
1851 *chain_list);
1852 goto out;
1854 chain_list++;
1857 if (revoke_list) {
1858 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1859 if (ret) {
1860 pk_copy_error(context, context->hx509ctx, ret,
1861 "Failed init revoke list");
1862 goto out;
1865 while (*revoke_list) {
1866 ret = hx509_revoke_add_crl(context->hx509ctx,
1867 id->revokectx,
1868 *revoke_list);
1869 if (ret) {
1870 pk_copy_error(context, context->hx509ctx, ret,
1871 "Failed load revoke list");
1872 goto out;
1874 revoke_list++;
1876 } else
1877 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1879 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1880 if (ret) {
1881 pk_copy_error(context, context->hx509ctx, ret,
1882 "Failed init verify context");
1883 goto out;
1886 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1887 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1889 out:
1890 if (ret) {
1891 hx509_verify_destroy_ctx(id->verify_ctx);
1892 hx509_certs_free(&id->certs);
1893 hx509_certs_free(&id->anchors);
1894 hx509_certs_free(&id->certpool);
1895 hx509_revoke_free(&id->revokectx);
1896 hx509_context_free(&context->hx509ctx);
1897 free(id);
1898 } else
1899 *ret_id = id;
1901 return ret;
1908 static void
1909 pk_copy_error(krb5_context context,
1910 hx509_context hx509ctx,
1911 int hxret,
1912 const char *fmt,
1913 ...)
1915 va_list va;
1916 char *s, *f;
1917 int ret;
1919 va_start(va, fmt);
1920 ret = vasprintf(&f, fmt, va);
1921 va_end(va);
1922 if (ret == -1 || f == NULL) {
1923 krb5_clear_error_message(context);
1924 return;
1927 s = hx509_get_error_string(hx509ctx, hxret);
1928 if (s == NULL) {
1929 krb5_clear_error_message(context);
1930 free(f);
1931 return;
1933 krb5_set_error_message(context, hxret, "%s: %s", f, s);
1934 free(s);
1935 free(f);
1938 static int
1939 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1940 const char *name, heim_integer *integer)
1942 int ret;
1943 char *p1;
1944 p1 = strsep(p, " \t");
1945 if (p1 == NULL) {
1946 krb5_set_error_message(context, EINVAL,
1947 N_("moduli file %s missing %s on line %d", ""),
1948 file, name, lineno);
1949 return EINVAL;
1951 ret = der_parse_hex_heim_integer(p1, integer);
1952 if (ret) {
1953 krb5_set_error_message(context, ret,
1954 N_("moduli file %s failed parsing %s "
1955 "on line %d", ""),
1956 file, name, lineno);
1957 return ret;
1960 return 0;
1963 krb5_error_code
1964 _krb5_parse_moduli_line(krb5_context context,
1965 const char *file,
1966 int lineno,
1967 char *p,
1968 struct krb5_dh_moduli **m)
1970 struct krb5_dh_moduli *m1;
1971 char *p1;
1972 int ret;
1974 *m = NULL;
1976 m1 = calloc(1, sizeof(*m1));
1977 if (m1 == NULL) {
1978 krb5_set_error_message(context, ENOMEM,
1979 N_("malloc: out of memory", ""));
1980 return ENOMEM;
1983 while (isspace((unsigned char)*p))
1984 p++;
1985 if (*p == '#') {
1986 free(m1);
1987 return 0;
1989 ret = EINVAL;
1991 p1 = strsep(&p, " \t");
1992 if (p1 == NULL) {
1993 krb5_set_error_message(context, ret,
1994 N_("moduli file %s missing name on line %d", ""),
1995 file, lineno);
1996 goto out;
1998 m1->name = strdup(p1);
1999 if (m1->name == NULL) {
2000 ret = ENOMEM;
2001 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2002 goto out;
2005 p1 = strsep(&p, " \t");
2006 if (p1 == NULL) {
2007 krb5_set_error_message(context, ret,
2008 N_("moduli file %s missing bits on line %d", ""),
2009 file, lineno);
2010 goto out;
2013 m1->bits = atoi(p1);
2014 if (m1->bits == 0) {
2015 krb5_set_error_message(context, ret,
2016 N_("moduli file %s have un-parsable "
2017 "bits on line %d", ""), file, lineno);
2018 goto out;
2021 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2022 if (ret)
2023 goto out;
2024 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2025 if (ret)
2026 goto out;
2027 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2028 if (ret)
2029 goto out;
2031 *m = m1;
2033 return 0;
2034 out:
2035 free(m1->name);
2036 der_free_heim_integer(&m1->p);
2037 der_free_heim_integer(&m1->g);
2038 der_free_heim_integer(&m1->q);
2039 free(m1);
2040 return ret;
2043 void
2044 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2046 int i;
2047 for (i = 0; moduli[i] != NULL; i++) {
2048 free(moduli[i]->name);
2049 der_free_heim_integer(&moduli[i]->p);
2050 der_free_heim_integer(&moduli[i]->g);
2051 der_free_heim_integer(&moduli[i]->q);
2052 free(moduli[i]);
2054 free(moduli);
2057 static const char *default_moduli_RFC2412_MODP_group2 =
2058 /* name */
2059 "RFC2412-MODP-group2 "
2060 /* bits */
2061 "1024 "
2062 /* p */
2063 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2064 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2065 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2066 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2067 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2068 "FFFFFFFF" "FFFFFFFF "
2069 /* g */
2070 "02 "
2071 /* q */
2072 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2073 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2074 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2075 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2076 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2077 "FFFFFFFF" "FFFFFFFF";
2079 static const char *default_moduli_rfc3526_MODP_group14 =
2080 /* name */
2081 "rfc3526-MODP-group14 "
2082 /* bits */
2083 "1760 "
2084 /* p */
2085 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2086 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2087 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2088 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2089 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2090 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2091 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2092 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2093 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2094 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2095 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2096 /* g */
2097 "02 "
2098 /* q */
2099 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2100 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2101 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2102 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2103 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2104 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2105 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2106 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2107 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2108 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2109 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2111 krb5_error_code
2112 _krb5_parse_moduli(krb5_context context, const char *file,
2113 struct krb5_dh_moduli ***moduli)
2115 /* name bits P G Q */
2116 krb5_error_code ret;
2117 struct krb5_dh_moduli **m = NULL, **m2;
2118 char buf[4096];
2119 FILE *f;
2120 int lineno = 0, n = 0;
2122 *moduli = NULL;
2124 m = calloc(1, sizeof(m[0]) * 3);
2125 if (m == NULL) {
2126 krb5_set_error_message(context, ENOMEM,
2127 N_("malloc: out of memory", ""));
2128 return ENOMEM;
2131 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2132 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2133 if (ret) {
2134 _krb5_free_moduli(m);
2135 return ret;
2137 n++;
2139 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2140 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2141 if (ret) {
2142 _krb5_free_moduli(m);
2143 return ret;
2145 n++;
2148 if (file == NULL)
2149 file = MODULI_FILE;
2151 f = fopen(file, "r");
2152 if (f == NULL) {
2153 *moduli = m;
2154 return 0;
2156 rk_cloexec_file(f);
2158 while(fgets(buf, sizeof(buf), f) != NULL) {
2159 struct krb5_dh_moduli *element;
2161 buf[strcspn(buf, "\n")] = '\0';
2162 lineno++;
2164 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2165 if (m2 == NULL) {
2166 _krb5_free_moduli(m);
2167 krb5_set_error_message(context, ENOMEM,
2168 N_("malloc: out of memory", ""));
2169 return ENOMEM;
2171 m = m2;
2173 m[n] = NULL;
2175 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2176 if (ret) {
2177 _krb5_free_moduli(m);
2178 return ret;
2180 if (element == NULL)
2181 continue;
2183 m[n] = element;
2184 m[n + 1] = NULL;
2185 n++;
2187 *moduli = m;
2188 return 0;
2191 krb5_error_code
2192 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2193 heim_integer *p, heim_integer *g, heim_integer *q,
2194 struct krb5_dh_moduli **moduli,
2195 char **name)
2197 int i;
2199 if (name)
2200 *name = NULL;
2202 for (i = 0; moduli[i] != NULL; i++) {
2203 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2204 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2205 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2207 if (bits && bits > moduli[i]->bits) {
2208 krb5_set_error_message(context,
2209 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2210 N_("PKINIT: DH group parameter %s "
2211 "no accepted, not enough bits "
2212 "generated", ""),
2213 moduli[i]->name);
2214 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2216 if (name)
2217 *name = strdup(moduli[i]->name);
2218 return 0;
2221 krb5_set_error_message(context,
2222 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2223 N_("PKINIT: DH group parameter no ok", ""));
2224 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2226 #endif /* PKINIT */
2228 void KRB5_LIB_FUNCTION
2229 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2231 #ifdef PKINIT
2232 krb5_pk_init_ctx ctx;
2234 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2235 return;
2236 ctx = opt->opt_private->pk_init_ctx;
2237 switch (ctx->keyex) {
2238 case USE_DH:
2239 if (ctx->u.dh)
2240 DH_free(ctx->u.dh);
2241 break;
2242 case USE_RSA:
2243 break;
2244 case USE_ECDH:
2245 #ifdef HAVE_OPENSSL
2246 if (ctx->u.eckey)
2247 EC_KEY_free(ctx->u.eckey);
2248 #endif
2249 break;
2251 if (ctx->id) {
2252 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2253 hx509_certs_free(&ctx->id->certs);
2254 hx509_cert_free(ctx->id->cert);
2255 hx509_certs_free(&ctx->id->anchors);
2256 hx509_certs_free(&ctx->id->certpool);
2258 if (ctx->clientDHNonce) {
2259 krb5_free_data(NULL, ctx->clientDHNonce);
2260 ctx->clientDHNonce = NULL;
2262 if (ctx->m)
2263 _krb5_free_moduli(ctx->m);
2264 free(ctx->id);
2265 ctx->id = NULL;
2267 free(opt->opt_private->pk_init_ctx);
2268 opt->opt_private->pk_init_ctx = NULL;
2269 #endif
2272 krb5_error_code KRB5_LIB_FUNCTION
2273 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2274 krb5_get_init_creds_opt *opt,
2275 krb5_principal principal,
2276 const char *user_id,
2277 const char *x509_anchors,
2278 char * const * pool,
2279 char * const * pki_revoke,
2280 int flags,
2281 krb5_prompter_fct prompter,
2282 void *prompter_data,
2283 char *password)
2285 #ifdef PKINIT
2286 krb5_error_code ret;
2287 char *anchors = NULL;
2289 if (opt->opt_private == NULL) {
2290 krb5_set_error_message(context, EINVAL,
2291 N_("PKINIT: on non extendable opt", ""));
2292 return EINVAL;
2295 opt->opt_private->pk_init_ctx =
2296 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2297 if (opt->opt_private->pk_init_ctx == NULL) {
2298 krb5_set_error_message(context, ENOMEM,
2299 N_("malloc: out of memory", ""));
2300 return ENOMEM;
2302 opt->opt_private->pk_init_ctx->require_binding = 0;
2303 opt->opt_private->pk_init_ctx->require_eku = 1;
2304 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2305 opt->opt_private->pk_init_ctx->peer = NULL;
2307 /* XXX implement krb5_appdefault_strings */
2308 if (pool == NULL)
2309 pool = krb5_config_get_strings(context, NULL,
2310 "appdefaults",
2311 "pkinit_pool",
2312 NULL);
2314 if (pki_revoke == NULL)
2315 pki_revoke = krb5_config_get_strings(context, NULL,
2316 "appdefaults",
2317 "pkinit_revoke",
2318 NULL);
2320 if (x509_anchors == NULL) {
2321 krb5_appdefault_string(context, "kinit",
2322 krb5_principal_get_realm(context, principal),
2323 "pkinit_anchors", NULL, &anchors);
2324 x509_anchors = anchors;
2327 if (flags & 4)
2328 opt->opt_private->pk_init_ctx->anonymous = 1;
2330 ret = _krb5_pk_load_id(context,
2331 &opt->opt_private->pk_init_ctx->id,
2332 user_id,
2333 x509_anchors,
2334 pool,
2335 pki_revoke,
2336 prompter,
2337 prompter_data,
2338 password);
2339 if (ret) {
2340 free(opt->opt_private->pk_init_ctx);
2341 opt->opt_private->pk_init_ctx = NULL;
2342 return ret;
2345 if (opt->opt_private->pk_init_ctx->id->certs) {
2346 _krb5_pk_set_user_id(context,
2347 opt->opt_private->pk_init_ctx,
2348 opt->opt_private->pk_init_ctx->id->certs);
2349 } else
2350 opt->opt_private->pk_init_ctx->id->cert = NULL;
2352 if ((flags & 2) == 0) {
2353 hx509_context hx509ctx = context->hx509ctx;
2354 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2356 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2359 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2361 if (cert) {
2362 AlgorithmIdentifier alg;
2364 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2365 if (ret == 0) {
2366 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2367 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2368 free_AlgorithmIdentifier(&alg);
2372 } else {
2373 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2375 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2376 krb5_set_error_message(context, EINVAL,
2377 N_("No anonymous pkinit support in RSA mode", ""));
2378 return EINVAL;
2382 return 0;
2383 #else
2384 krb5_set_error_message(context, EINVAL,
2385 N_("no support for PKINIT compiled in", ""));
2386 return EINVAL;
2387 #endif
2390 krb5_error_code KRB5_LIB_FUNCTION
2391 _krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2392 krb5_get_init_creds_opt *opt,
2393 struct hx509_certs_data *certs)
2395 #ifdef PKINIT
2396 if (opt->opt_private == NULL) {
2397 krb5_set_error_message(context, EINVAL,
2398 N_("PKINIT: on non extendable opt", ""));
2399 return EINVAL;
2401 if (opt->opt_private->pk_init_ctx == NULL) {
2402 krb5_set_error_message(context, EINVAL,
2403 N_("PKINIT: on pkinit context", ""));
2404 return EINVAL;
2407 _krb5_pk_set_user_id(context, opt->opt_private->pk_init_ctx, certs);
2409 return 0;
2410 #else
2411 krb5_set_error_message(context, EINVAL,
2412 N_("no support for PKINIT compiled in", ""));
2413 return EINVAL;
2414 #endif
2417 #ifdef PKINIT
2419 static int
2420 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2422 hx509_octet_string_list list;
2423 int ret;
2425 *upn = NULL;
2427 ret = hx509_cert_find_subjectAltName_otherName(context,
2428 cert,
2429 &asn1_oid_id_pkinit_ms_san,
2430 &list);
2431 if (ret)
2432 return 0;
2434 if (list.len > 0 && list.val[0].length > 0)
2435 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2436 upn, NULL);
2437 else
2438 ret = 1;
2439 hx509_free_octet_string_list(&list);
2441 return ret;
2444 static int
2445 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2447 char *upn;
2448 int ret;
2450 ret = get_ms_san(context, cert, &upn);
2451 if (ret == 0)
2452 free(upn);
2453 return ret;
2458 #endif
2461 * Private since it need to be redesigned using krb5_get_init_creds()
2464 krb5_error_code KRB5_LIB_FUNCTION
2465 _krb5_pk_enterprise_cert(krb5_context context,
2466 const char *user_id,
2467 krb5_const_realm realm,
2468 krb5_principal *principal,
2469 struct hx509_certs_data **res)
2471 #ifdef PKINIT
2472 krb5_error_code ret;
2473 hx509_certs certs, result;
2474 hx509_cert cert;
2475 hx509_query *q;
2476 char *name;
2478 *principal = NULL;
2479 if (res)
2480 *res = NULL;
2482 if (user_id == NULL) {
2483 krb5_clear_error_message(context);
2484 return ENOENT;
2487 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2488 if (ret) {
2489 pk_copy_error(context, context->hx509ctx, ret,
2490 "Failed to init cert certs");
2491 return ret;
2494 ret = hx509_query_alloc(context->hx509ctx, &q);
2495 if (ret) {
2496 krb5_set_error_message(context, ret, "out of memory");
2497 hx509_certs_free(&certs);
2498 return ret;
2501 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2502 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2503 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2504 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2506 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2507 hx509_query_free(context->hx509ctx, q);
2508 hx509_certs_free(&certs);
2509 if (ret) {
2510 pk_copy_error(context, context->hx509ctx, ret,
2511 "Failed to find PKINIT certificate");
2512 return ret;
2515 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2516 hx509_certs_free(&result);
2517 if (ret) {
2518 pk_copy_error(context, context->hx509ctx, ret,
2519 "Failed to get one cert");
2520 goto out;
2523 ret = get_ms_san(context->hx509ctx, cert, &name);
2524 if (ret) {
2525 pk_copy_error(context, context->hx509ctx, ret,
2526 "Failed to get MS SAN");
2527 goto out;
2530 ret = krb5_make_principal(context, principal, realm, name, NULL);
2531 free(name);
2532 if (ret)
2533 goto out;
2535 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2537 if (res) {
2538 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2539 if (ret) {
2540 hx509_cert_free(cert);
2541 goto out;
2544 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2545 if (ret) {
2546 hx509_certs_free(res);
2547 goto out;
2551 out:
2552 hx509_cert_free(cert);
2554 return ret;
2555 #else
2556 krb5_set_error_message(context, EINVAL,
2557 N_("no support for PKINIT compiled in", ""));
2558 return EINVAL;
2559 #endif