Allow users to specify their own configuration file ~/.krb5/config
[heimdal.git] / lib / krb5 / pkinit.c
blobbd0ec158215622c0e4ff1b6ff9d48910a57668e7
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 free(id);
1897 } else
1898 *ret_id = id;
1900 return ret;
1907 static void
1908 pk_copy_error(krb5_context context,
1909 hx509_context hx509ctx,
1910 int hxret,
1911 const char *fmt,
1912 ...)
1914 va_list va;
1915 char *s, *f;
1916 int ret;
1918 va_start(va, fmt);
1919 ret = vasprintf(&f, fmt, va);
1920 va_end(va);
1921 if (ret == -1 || f == NULL) {
1922 krb5_clear_error_message(context);
1923 return;
1926 s = hx509_get_error_string(hx509ctx, hxret);
1927 if (s == NULL) {
1928 krb5_clear_error_message(context);
1929 free(f);
1930 return;
1932 krb5_set_error_message(context, hxret, "%s: %s", f, s);
1933 free(s);
1934 free(f);
1937 static int
1938 parse_integer(krb5_context context, char **p, const char *file, int lineno,
1939 const char *name, heim_integer *integer)
1941 int ret;
1942 char *p1;
1943 p1 = strsep(p, " \t");
1944 if (p1 == NULL) {
1945 krb5_set_error_message(context, EINVAL,
1946 N_("moduli file %s missing %s on line %d", ""),
1947 file, name, lineno);
1948 return EINVAL;
1950 ret = der_parse_hex_heim_integer(p1, integer);
1951 if (ret) {
1952 krb5_set_error_message(context, ret,
1953 N_("moduli file %s failed parsing %s "
1954 "on line %d", ""),
1955 file, name, lineno);
1956 return ret;
1959 return 0;
1962 krb5_error_code
1963 _krb5_parse_moduli_line(krb5_context context,
1964 const char *file,
1965 int lineno,
1966 char *p,
1967 struct krb5_dh_moduli **m)
1969 struct krb5_dh_moduli *m1;
1970 char *p1;
1971 int ret;
1973 *m = NULL;
1975 m1 = calloc(1, sizeof(*m1));
1976 if (m1 == NULL) {
1977 krb5_set_error_message(context, ENOMEM,
1978 N_("malloc: out of memory", ""));
1979 return ENOMEM;
1982 while (isspace((unsigned char)*p))
1983 p++;
1984 if (*p == '#') {
1985 free(m1);
1986 return 0;
1988 ret = EINVAL;
1990 p1 = strsep(&p, " \t");
1991 if (p1 == NULL) {
1992 krb5_set_error_message(context, ret,
1993 N_("moduli file %s missing name on line %d", ""),
1994 file, lineno);
1995 goto out;
1997 m1->name = strdup(p1);
1998 if (m1->name == NULL) {
1999 ret = ENOMEM;
2000 krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2001 goto out;
2004 p1 = strsep(&p, " \t");
2005 if (p1 == NULL) {
2006 krb5_set_error_message(context, ret,
2007 N_("moduli file %s missing bits on line %d", ""),
2008 file, lineno);
2009 goto out;
2012 m1->bits = atoi(p1);
2013 if (m1->bits == 0) {
2014 krb5_set_error_message(context, ret,
2015 N_("moduli file %s have un-parsable "
2016 "bits on line %d", ""), file, lineno);
2017 goto out;
2020 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2021 if (ret)
2022 goto out;
2023 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2024 if (ret)
2025 goto out;
2026 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2027 if (ret)
2028 goto out;
2030 *m = m1;
2032 return 0;
2033 out:
2034 free(m1->name);
2035 der_free_heim_integer(&m1->p);
2036 der_free_heim_integer(&m1->g);
2037 der_free_heim_integer(&m1->q);
2038 free(m1);
2039 return ret;
2042 void
2043 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2045 int i;
2046 for (i = 0; moduli[i] != NULL; i++) {
2047 free(moduli[i]->name);
2048 der_free_heim_integer(&moduli[i]->p);
2049 der_free_heim_integer(&moduli[i]->g);
2050 der_free_heim_integer(&moduli[i]->q);
2051 free(moduli[i]);
2053 free(moduli);
2056 static const char *default_moduli_RFC2412_MODP_group2 =
2057 /* name */
2058 "RFC2412-MODP-group2 "
2059 /* bits */
2060 "1024 "
2061 /* p */
2062 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2063 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2064 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2065 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2066 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2067 "FFFFFFFF" "FFFFFFFF "
2068 /* g */
2069 "02 "
2070 /* q */
2071 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2072 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2073 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2074 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2075 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2076 "FFFFFFFF" "FFFFFFFF";
2078 static const char *default_moduli_rfc3526_MODP_group14 =
2079 /* name */
2080 "rfc3526-MODP-group14 "
2081 /* bits */
2082 "1760 "
2083 /* p */
2084 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2085 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2086 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2087 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2088 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2089 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2090 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2091 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2092 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2093 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2094 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2095 /* g */
2096 "02 "
2097 /* q */
2098 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2099 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2100 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2101 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2102 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2103 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2104 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2105 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2106 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2107 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2108 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2110 krb5_error_code
2111 _krb5_parse_moduli(krb5_context context, const char *file,
2112 struct krb5_dh_moduli ***moduli)
2114 /* name bits P G Q */
2115 krb5_error_code ret;
2116 struct krb5_dh_moduli **m = NULL, **m2;
2117 char buf[4096];
2118 FILE *f;
2119 int lineno = 0, n = 0;
2121 *moduli = NULL;
2123 m = calloc(1, sizeof(m[0]) * 3);
2124 if (m == NULL) {
2125 krb5_set_error_message(context, ENOMEM,
2126 N_("malloc: out of memory", ""));
2127 return ENOMEM;
2130 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2131 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2132 if (ret) {
2133 _krb5_free_moduli(m);
2134 return ret;
2136 n++;
2138 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2139 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2140 if (ret) {
2141 _krb5_free_moduli(m);
2142 return ret;
2144 n++;
2147 if (file == NULL)
2148 file = MODULI_FILE;
2150 f = fopen(file, "r");
2151 if (f == NULL) {
2152 *moduli = m;
2153 return 0;
2155 rk_cloexec_file(f);
2157 while(fgets(buf, sizeof(buf), f) != NULL) {
2158 struct krb5_dh_moduli *element;
2160 buf[strcspn(buf, "\n")] = '\0';
2161 lineno++;
2163 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2164 if (m2 == NULL) {
2165 _krb5_free_moduli(m);
2166 krb5_set_error_message(context, ENOMEM,
2167 N_("malloc: out of memory", ""));
2168 return ENOMEM;
2170 m = m2;
2172 m[n] = NULL;
2174 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2175 if (ret) {
2176 _krb5_free_moduli(m);
2177 return ret;
2179 if (element == NULL)
2180 continue;
2182 m[n] = element;
2183 m[n + 1] = NULL;
2184 n++;
2186 *moduli = m;
2187 return 0;
2190 krb5_error_code
2191 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2192 heim_integer *p, heim_integer *g, heim_integer *q,
2193 struct krb5_dh_moduli **moduli,
2194 char **name)
2196 int i;
2198 if (name)
2199 *name = NULL;
2201 for (i = 0; moduli[i] != NULL; i++) {
2202 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2203 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2204 (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2206 if (bits && bits > moduli[i]->bits) {
2207 krb5_set_error_message(context,
2208 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2209 N_("PKINIT: DH group parameter %s "
2210 "no accepted, not enough bits "
2211 "generated", ""),
2212 moduli[i]->name);
2213 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2215 if (name)
2216 *name = strdup(moduli[i]->name);
2217 return 0;
2220 krb5_set_error_message(context,
2221 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2222 N_("PKINIT: DH group parameter no ok", ""));
2223 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2225 #endif /* PKINIT */
2227 void KRB5_LIB_FUNCTION
2228 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2230 #ifdef PKINIT
2231 krb5_pk_init_ctx ctx;
2233 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2234 return;
2235 ctx = opt->opt_private->pk_init_ctx;
2236 switch (ctx->keyex) {
2237 case USE_DH:
2238 if (ctx->u.dh)
2239 DH_free(ctx->u.dh);
2240 break;
2241 case USE_RSA:
2242 break;
2243 case USE_ECDH:
2244 #ifdef HAVE_OPENSSL
2245 if (ctx->u.eckey)
2246 EC_KEY_free(ctx->u.eckey);
2247 #endif
2248 break;
2250 if (ctx->id) {
2251 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2252 hx509_certs_free(&ctx->id->certs);
2253 hx509_cert_free(ctx->id->cert);
2254 hx509_certs_free(&ctx->id->anchors);
2255 hx509_certs_free(&ctx->id->certpool);
2257 if (ctx->clientDHNonce) {
2258 krb5_free_data(NULL, ctx->clientDHNonce);
2259 ctx->clientDHNonce = NULL;
2261 if (ctx->m)
2262 _krb5_free_moduli(ctx->m);
2263 free(ctx->id);
2264 ctx->id = NULL;
2266 free(opt->opt_private->pk_init_ctx);
2267 opt->opt_private->pk_init_ctx = NULL;
2268 #endif
2271 krb5_error_code KRB5_LIB_FUNCTION
2272 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2273 krb5_get_init_creds_opt *opt,
2274 krb5_principal principal,
2275 const char *user_id,
2276 const char *x509_anchors,
2277 char * const * pool,
2278 char * const * pki_revoke,
2279 int flags,
2280 krb5_prompter_fct prompter,
2281 void *prompter_data,
2282 char *password)
2284 #ifdef PKINIT
2285 krb5_error_code ret;
2286 char *anchors = NULL;
2288 if (opt->opt_private == NULL) {
2289 krb5_set_error_message(context, EINVAL,
2290 N_("PKINIT: on non extendable opt", ""));
2291 return EINVAL;
2294 opt->opt_private->pk_init_ctx =
2295 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2296 if (opt->opt_private->pk_init_ctx == NULL) {
2297 krb5_set_error_message(context, ENOMEM,
2298 N_("malloc: out of memory", ""));
2299 return ENOMEM;
2301 opt->opt_private->pk_init_ctx->require_binding = 0;
2302 opt->opt_private->pk_init_ctx->require_eku = 1;
2303 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2304 opt->opt_private->pk_init_ctx->peer = NULL;
2306 /* XXX implement krb5_appdefault_strings */
2307 if (pool == NULL)
2308 pool = krb5_config_get_strings(context, NULL,
2309 "appdefaults",
2310 "pkinit_pool",
2311 NULL);
2313 if (pki_revoke == NULL)
2314 pki_revoke = krb5_config_get_strings(context, NULL,
2315 "appdefaults",
2316 "pkinit_revoke",
2317 NULL);
2319 if (x509_anchors == NULL) {
2320 krb5_appdefault_string(context, "kinit",
2321 krb5_principal_get_realm(context, principal),
2322 "pkinit_anchors", NULL, &anchors);
2323 x509_anchors = anchors;
2326 if (flags & 4)
2327 opt->opt_private->pk_init_ctx->anonymous = 1;
2329 ret = _krb5_pk_load_id(context,
2330 &opt->opt_private->pk_init_ctx->id,
2331 user_id,
2332 x509_anchors,
2333 pool,
2334 pki_revoke,
2335 prompter,
2336 prompter_data,
2337 password);
2338 if (ret) {
2339 free(opt->opt_private->pk_init_ctx);
2340 opt->opt_private->pk_init_ctx = NULL;
2341 return ret;
2344 if (opt->opt_private->pk_init_ctx->id->certs) {
2345 _krb5_pk_set_user_id(context,
2346 opt->opt_private->pk_init_ctx,
2347 opt->opt_private->pk_init_ctx->id->certs);
2348 } else
2349 opt->opt_private->pk_init_ctx->id->cert = NULL;
2351 if ((flags & 2) == 0) {
2352 hx509_context hx509ctx = context->hx509ctx;
2353 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2355 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2358 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2360 if (cert) {
2361 AlgorithmIdentifier alg;
2363 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2364 if (ret == 0) {
2365 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2366 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2367 free_AlgorithmIdentifier(&alg);
2371 } else {
2372 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2374 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2375 krb5_set_error_message(context, EINVAL,
2376 N_("No anonymous pkinit support in RSA mode", ""));
2377 return EINVAL;
2381 return 0;
2382 #else
2383 krb5_set_error_message(context, EINVAL,
2384 N_("no support for PKINIT compiled in", ""));
2385 return EINVAL;
2386 #endif
2389 krb5_error_code KRB5_LIB_FUNCTION
2390 _krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2391 krb5_get_init_creds_opt *opt,
2392 struct hx509_certs_data *certs)
2394 #ifdef PKINIT
2395 if (opt->opt_private == NULL) {
2396 krb5_set_error_message(context, EINVAL,
2397 N_("PKINIT: on non extendable opt", ""));
2398 return EINVAL;
2400 if (opt->opt_private->pk_init_ctx == NULL) {
2401 krb5_set_error_message(context, EINVAL,
2402 N_("PKINIT: on pkinit context", ""));
2403 return EINVAL;
2406 _krb5_pk_set_user_id(context, opt->opt_private->pk_init_ctx, certs);
2408 return 0;
2409 #else
2410 krb5_set_error_message(context, EINVAL,
2411 N_("no support for PKINIT compiled in", ""));
2412 return EINVAL;
2413 #endif
2416 #ifdef PKINIT
2418 static int
2419 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2421 hx509_octet_string_list list;
2422 int ret;
2424 *upn = NULL;
2426 ret = hx509_cert_find_subjectAltName_otherName(context,
2427 cert,
2428 &asn1_oid_id_pkinit_ms_san,
2429 &list);
2430 if (ret)
2431 return 0;
2433 if (list.len > 0 && list.val[0].length > 0)
2434 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2435 upn, NULL);
2436 else
2437 ret = 1;
2438 hx509_free_octet_string_list(&list);
2440 return ret;
2443 static int
2444 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2446 char *upn;
2447 int ret;
2449 ret = get_ms_san(context, cert, &upn);
2450 if (ret == 0)
2451 free(upn);
2452 return ret;
2457 #endif
2460 * Private since it need to be redesigned using krb5_get_init_creds()
2463 krb5_error_code KRB5_LIB_FUNCTION
2464 _krb5_pk_enterprise_cert(krb5_context context,
2465 const char *user_id,
2466 krb5_const_realm realm,
2467 krb5_principal *principal,
2468 struct hx509_certs_data **res)
2470 #ifdef PKINIT
2471 krb5_error_code ret;
2472 hx509_certs certs, result;
2473 hx509_cert cert;
2474 hx509_query *q;
2475 char *name;
2477 *principal = NULL;
2478 if (res)
2479 *res = NULL;
2481 if (user_id == NULL) {
2482 krb5_clear_error_message(context);
2483 return ENOENT;
2486 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2487 if (ret) {
2488 pk_copy_error(context, context->hx509ctx, ret,
2489 "Failed to init cert certs");
2490 return ret;
2493 ret = hx509_query_alloc(context->hx509ctx, &q);
2494 if (ret) {
2495 krb5_set_error_message(context, ret, "out of memory");
2496 hx509_certs_free(&certs);
2497 return ret;
2500 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2501 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2502 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2503 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2505 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2506 hx509_query_free(context->hx509ctx, q);
2507 hx509_certs_free(&certs);
2508 if (ret) {
2509 pk_copy_error(context, context->hx509ctx, ret,
2510 "Failed to find PKINIT certificate");
2511 return ret;
2514 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2515 hx509_certs_free(&result);
2516 if (ret) {
2517 pk_copy_error(context, context->hx509ctx, ret,
2518 "Failed to get one cert");
2519 goto out;
2522 ret = get_ms_san(context->hx509ctx, cert, &name);
2523 if (ret) {
2524 pk_copy_error(context, context->hx509ctx, ret,
2525 "Failed to get MS SAN");
2526 goto out;
2529 ret = krb5_make_principal(context, principal, realm, name, NULL);
2530 free(name);
2531 if (ret)
2532 goto out;
2534 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2536 if (res) {
2537 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2538 if (ret) {
2539 hx509_cert_free(cert);
2540 goto out;
2543 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2544 if (ret) {
2545 hx509_certs_free(res);
2546 goto out;
2550 out:
2551 hx509_cert_free(cert);
2553 return ret;
2554 #else
2555 krb5_set_error_message(context, EINVAL,
2556 N_("no support for PKINIT compiled in", ""));
2557 return EINVAL;
2558 #endif