s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / third_party / heimdal / lib / krb5 / pkinit.c
blob6dcdcfa732982f0522225bab86caa65c107a2aac
1 /*
2 * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "krb5_locl.h"
38 struct krb5_dh_moduli {
39 char *name;
40 unsigned long bits;
41 heim_integer p;
42 heim_integer g;
43 heim_integer q;
46 #ifdef PKINIT
48 #include <cms_asn1.h>
49 #include <pkcs8_asn1.h>
50 #include <pkcs9_asn1.h>
51 #include <pkcs12_asn1.h>
52 #include <pkinit_asn1.h>
53 #include <asn1_err.h>
55 #include <der.h>
57 struct krb5_pk_cert {
58 hx509_cert cert;
61 static void
62 pk_copy_error(krb5_context context,
63 hx509_context hx509ctx,
64 int hxret,
65 const char *fmt,
66 ...)
67 __attribute__ ((__format__ (__printf__, 4, 5)));
73 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
74 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
76 if (cert->cert) {
77 hx509_cert_free(cert->cert);
79 free(cert);
82 static krb5_error_code
83 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
85 integer->length = BN_num_bytes(bn);
86 integer->data = malloc(integer->length);
87 if (integer->data == NULL) {
88 krb5_clear_error_message(context);
89 return ENOMEM;
91 BN_bn2bin(bn, integer->data);
92 integer->negative = BN_is_negative(bn);
93 return 0;
96 static BIGNUM *
97 integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
99 BIGNUM *bn;
101 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
102 if (bn == NULL) {
103 krb5_set_error_message(context, ENOMEM,
104 N_("PKINIT: parsing BN failed %s", ""), field);
105 return NULL;
107 BN_set_negative(bn, f->negative);
108 return bn;
111 static krb5_error_code
112 select_dh_group(krb5_context context, DH *dh, unsigned long min_bits,
113 struct krb5_dh_moduli **moduli)
115 const struct krb5_dh_moduli *m;
117 if (moduli[0] == NULL) {
118 krb5_set_error_message(context, EINVAL,
119 N_("Did not find a DH group parameter "
120 "matching requirement of %lu bits", ""),
121 min_bits);
122 return EINVAL;
125 if (min_bits == 0) {
126 m = moduli[1]; /* XXX */
127 if (m == NULL)
128 m = moduli[0]; /* XXX */
129 } else {
130 int i;
131 for (i = 0; moduli[i] != NULL; i++) {
132 if (moduli[i]->bits >= min_bits)
133 break;
135 if (moduli[i] == NULL) {
136 krb5_set_error_message(context, EINVAL,
137 N_("Did not find a DH group parameter "
138 "matching requirement of %lu bits", ""),
139 min_bits);
140 return EINVAL;
142 m = moduli[i];
145 dh->p = integer_to_BN(context, "p", &m->p);
146 if (dh->p == NULL)
147 return ENOMEM;
148 dh->g = integer_to_BN(context, "g", &m->g);
149 if (dh->g == NULL)
150 return ENOMEM;
151 dh->q = integer_to_BN(context, "q", &m->q);
152 if (dh->q == NULL)
153 return ENOMEM;
155 return 0;
158 struct certfind {
159 const char *type;
160 const heim_oid *oid;
164 * Try searchin the key by to use by first looking for for PK-INIT
165 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
168 static krb5_error_code
169 find_cert(krb5_context context, struct krb5_pk_identity *id,
170 hx509_query *q, hx509_cert *cert)
172 struct certfind cf[4] = {
173 { "MobileMe EKU", NULL },
174 { "PKINIT EKU", NULL },
175 { "MS EKU", NULL },
176 { "any (or no)", NULL }
178 int ret = HX509_CERT_NOT_FOUND;
179 size_t i, start = 1;
180 unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
181 const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
184 if (id->flags & PKINIT_BTMM)
185 start = 0;
187 cf[0].oid = &mobileMe;
188 cf[1].oid = &asn1_oid_id_pkekuoid;
189 cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
190 cf[3].oid = NULL;
192 for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
193 ret = hx509_query_match_eku(q, cf[i].oid);
194 if (ret) {
195 pk_copy_error(context, context->hx509ctx, ret,
196 "Failed setting %s OID", cf[i].type);
197 return ret;
200 ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
201 if (ret == 0)
202 break;
203 pk_copy_error(context, context->hx509ctx, ret,
204 "Failed finding certificate with %s OID", cf[i].type);
206 return ret;
210 static krb5_error_code
211 create_signature(krb5_context context,
212 const heim_oid *eContentType,
213 krb5_data *eContent,
214 struct krb5_pk_identity *id,
215 hx509_peer_info peer,
216 krb5_data *sd_data)
218 int ret, flags = 0;
220 if (id->cert == NULL)
221 flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
223 ret = hx509_cms_create_signed_1(context->hx509ctx,
224 flags,
225 eContentType,
226 eContent->data,
227 eContent->length,
228 NULL,
229 id->cert,
230 peer,
231 NULL,
232 id->certs,
233 sd_data);
234 if (ret) {
235 pk_copy_error(context, context->hx509ctx, ret,
236 "Create CMS signedData");
237 return ret;
240 return 0;
243 static int KRB5_LIB_CALL
244 cert2epi(hx509_context context, void *ctx, hx509_cert c)
246 ExternalPrincipalIdentifiers *ids = ctx;
247 ExternalPrincipalIdentifier id;
248 hx509_name subject = NULL;
249 void *p;
250 int ret;
252 if (ids->len > 10)
253 return 0;
255 memset(&id, 0, sizeof(id));
257 ret = hx509_cert_get_subject(c, &subject);
258 if (ret)
259 return ret;
261 if (hx509_name_is_null_p(subject) != 0) {
263 id.subjectName = calloc(1, sizeof(*id.subjectName));
264 if (id.subjectName == NULL) {
265 hx509_name_free(&subject);
266 free_ExternalPrincipalIdentifier(&id);
267 return ENOMEM;
270 ret = hx509_name_binary(subject, id.subjectName);
271 if (ret) {
272 hx509_name_free(&subject);
273 free_ExternalPrincipalIdentifier(&id);
274 return ret;
277 hx509_name_free(&subject);
280 id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
281 if (id.issuerAndSerialNumber == NULL) {
282 free_ExternalPrincipalIdentifier(&id);
283 return ENOMEM;
287 IssuerAndSerialNumber iasn;
288 hx509_name issuer;
289 size_t size = 0;
291 memset(&iasn, 0, sizeof(iasn));
293 ret = hx509_cert_get_issuer(c, &issuer);
294 if (ret) {
295 free_ExternalPrincipalIdentifier(&id);
296 return ret;
299 ret = hx509_name_to_Name(issuer, &iasn.issuer);
300 hx509_name_free(&issuer);
301 if (ret) {
302 free_ExternalPrincipalIdentifier(&id);
303 return ret;
306 ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
307 if (ret) {
308 free_IssuerAndSerialNumber(&iasn);
309 free_ExternalPrincipalIdentifier(&id);
310 return ret;
313 ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
314 id.issuerAndSerialNumber->data,
315 id.issuerAndSerialNumber->length,
316 &iasn, &size, ret);
317 free_IssuerAndSerialNumber(&iasn);
318 if (ret) {
319 free_ExternalPrincipalIdentifier(&id);
320 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_f(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 = 0;
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 return krb5_enomem(context);
394 ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
395 checksum.checksum.data, checksum.checksum.length);
396 free_Checksum(&checksum);
397 if (ret)
398 return ret;
400 if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
401 const char *moduli_file;
402 unsigned long dh_min_bits;
403 krb5_data dhbuf;
404 size_t size = 0;
406 krb5_data_zero(&dhbuf);
410 moduli_file = krb5_config_get_string(context, NULL,
411 "libdefaults",
412 "moduli",
413 NULL);
415 dh_min_bits =
416 krb5_config_get_int_default(context, NULL, 0,
417 "libdefaults",
418 "pkinit_dh_min_bits",
419 NULL);
421 ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
422 if (ret)
423 return ret;
425 ctx->u.dh = DH_new();
426 if (ctx->u.dh == NULL)
427 return krb5_enomem(context);
429 ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
430 if (ret)
431 return ret;
433 if (DH_generate_key(ctx->u.dh) != 1) {
434 krb5_set_error_message(context, ENOMEM,
435 N_("pkinit: failed to generate DH key", ""));
436 return ENOMEM;
440 if (1 /* support_cached_dh */) {
441 ALLOC(a->clientDHNonce, 1);
442 if (a->clientDHNonce == NULL) {
443 krb5_clear_error_message(context);
444 return ENOMEM;
446 ret = krb5_data_alloc(a->clientDHNonce, 40);
447 if (a->clientDHNonce == NULL) {
448 krb5_clear_error_message(context);
449 return ret;
451 ret = RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
452 if (ret != 1)
453 return KRB5_CRYPTO_INTERNAL;
454 ret = krb5_copy_data(context, a->clientDHNonce,
455 &ctx->clientDHNonce);
456 if (ret)
457 return ret;
460 ALLOC(a->clientPublicValue, 1);
461 if (a->clientPublicValue == NULL)
462 return ENOMEM;
464 if (ctx->keyex == USE_DH) {
465 DH *dh = ctx->u.dh;
466 DomainParameters dp;
467 heim_integer dh_pub_key;
469 ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
470 &a->clientPublicValue->algorithm.algorithm);
471 if (ret)
472 return ret;
474 memset(&dp, 0, sizeof(dp));
476 ret = BN_to_integer(context, dh->p, &dp.p);
477 if (ret) {
478 free_DomainParameters(&dp);
479 return ret;
481 ret = BN_to_integer(context, dh->g, &dp.g);
482 if (ret) {
483 free_DomainParameters(&dp);
484 return ret;
486 if (dh->q && BN_num_bits(dh->q)) {
488 * The q parameter is required, but MSFT made it optional.
489 * It's only required in order to verify the domain parameters
490 * -- the security of the DH group --, but we validate groups
491 * against known groups rather than accepting arbitrary groups
492 * chosen by the peer, so we really don't need to have put it
493 * on the wire. Because these are Oakley groups, and the
494 * primes are Sophie Germain primes, q is p>>1 and we can
495 * compute it on the fly like MIT Kerberos does, but we'd have
496 * to implement BN_rshift1().
498 dp.q = calloc(1, sizeof(*dp.q));
499 if (dp.q == NULL) {
500 free_DomainParameters(&dp);
501 return ENOMEM;
503 ret = BN_to_integer(context, dh->q, dp.q);
504 if (ret) {
505 free_DomainParameters(&dp);
506 return ret;
509 dp.j = NULL;
510 dp.validationParms = NULL;
512 a->clientPublicValue->algorithm.parameters =
513 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
514 if (a->clientPublicValue->algorithm.parameters == NULL) {
515 free_DomainParameters(&dp);
516 return ret;
519 ASN1_MALLOC_ENCODE(DomainParameters,
520 a->clientPublicValue->algorithm.parameters->data,
521 a->clientPublicValue->algorithm.parameters->length,
522 &dp, &size, ret);
523 free_DomainParameters(&dp);
524 if (ret)
525 return ret;
526 if (size != a->clientPublicValue->algorithm.parameters->length)
527 krb5_abortx(context, "Internal ASN1 encoder error");
529 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
530 if (ret)
531 return ret;
533 ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
534 &dh_pub_key, &size, ret);
535 der_free_heim_integer(&dh_pub_key);
536 if (ret)
537 return ret;
538 if (size != dhbuf.length)
539 krb5_abortx(context, "asn1 internal error");
540 a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
541 a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
542 } else if (ctx->keyex == USE_ECDH) {
543 ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
544 if (ret)
545 return ret;
546 } else
547 krb5_abortx(context, "internal error");
551 a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
552 if (a->supportedCMSTypes == NULL)
553 return ENOMEM;
555 ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
556 ctx->id->cert,
557 &a->supportedCMSTypes->val,
558 &a->supportedCMSTypes->len);
559 if (ret)
560 return ret;
563 return ret;
566 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
567 _krb5_pk_mk_ContentInfo(krb5_context context,
568 const krb5_data *buf,
569 const heim_oid *oid,
570 struct ContentInfo *content_info)
572 krb5_error_code ret;
574 ret = der_copy_oid(oid, &content_info->contentType);
575 if (ret)
576 return ret;
577 ALLOC(content_info->content, 1);
578 if (content_info->content == NULL)
579 return ENOMEM;
580 content_info->content->data = malloc(buf->length);
581 if (content_info->content->data == NULL)
582 return ENOMEM;
583 memcpy(content_info->content->data, buf->data, buf->length);
584 content_info->content->length = buf->length;
585 return 0;
588 static krb5_error_code
589 pk_mk_padata(krb5_context context,
590 krb5_pk_init_ctx ctx,
591 const KDC_REQ_BODY *req_body,
592 unsigned nonce,
593 METHOD_DATA *md)
595 struct ContentInfo content_info;
596 krb5_error_code ret;
597 const heim_oid *oid = NULL;
598 size_t size = 0;
599 krb5_data buf, sd_buf;
600 int pa_type = -1;
602 krb5_data_zero(&buf);
603 krb5_data_zero(&sd_buf);
604 memset(&content_info, 0, sizeof(content_info));
606 if (ctx->type == PKINIT_WIN2K) {
607 AuthPack_Win2k ap;
608 krb5_timestamp sec;
609 int32_t usec;
611 memset(&ap, 0, sizeof(ap));
613 /* fill in PKAuthenticator */
614 ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
615 if (ret) {
616 free_AuthPack_Win2k(&ap);
617 krb5_clear_error_message(context);
618 goto out;
620 ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
621 if (ret) {
622 free_AuthPack_Win2k(&ap);
623 krb5_clear_error_message(context);
624 goto out;
627 krb5_us_timeofday(context, &sec, &usec);
628 ap.pkAuthenticator.ctime = sec;
629 ap.pkAuthenticator.cusec = usec;
630 ap.pkAuthenticator.nonce = nonce;
632 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
633 &ap, &size, ret);
634 free_AuthPack_Win2k(&ap);
635 if (ret) {
636 krb5_set_error_message(context, ret,
637 N_("Failed encoding AuthPackWin: %d", ""),
638 (int)ret);
639 goto out;
641 if (buf.length != size)
642 krb5_abortx(context, "internal ASN1 encoder error");
644 oid = &asn1_oid_id_pkcs7_data;
645 } else if (ctx->type == PKINIT_27) {
646 AuthPack ap;
648 memset(&ap, 0, sizeof(ap));
650 ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
651 if (ret) {
652 free_AuthPack(&ap);
653 goto out;
656 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
657 free_AuthPack(&ap);
658 if (ret) {
659 krb5_set_error_message(context, ret,
660 N_("Failed encoding AuthPack: %d", ""),
661 (int)ret);
662 goto out;
664 if (buf.length != size)
665 krb5_abortx(context, "internal ASN1 encoder error");
667 oid = &asn1_oid_id_pkauthdata;
668 } else
669 krb5_abortx(context, "internal pkinit error");
671 ret = create_signature(context, oid, &buf, ctx->id,
672 ctx->peer, &sd_buf);
673 krb5_data_free(&buf);
674 if (ret)
675 goto out;
677 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
678 krb5_data_free(&sd_buf);
679 if (ret) {
680 krb5_set_error_message(context, ret,
681 N_("ContentInfo wrapping of signedData failed",""));
682 goto out;
685 if (ctx->type == PKINIT_WIN2K) {
686 PA_PK_AS_REQ_Win2k winreq;
688 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
690 memset(&winreq, 0, sizeof(winreq));
692 winreq.signed_auth_pack = buf;
694 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
695 &winreq, &size, ret);
696 free_PA_PK_AS_REQ_Win2k(&winreq);
698 } else if (ctx->type == PKINIT_27) {
699 PA_PK_AS_REQ req;
701 pa_type = KRB5_PADATA_PK_AS_REQ;
703 memset(&req, 0, sizeof(req));
704 req.signedAuthPack = buf;
706 if (ctx->trustedCertifiers) {
708 req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
709 if (req.trustedCertifiers == NULL) {
710 ret = krb5_enomem(context);
711 free_PA_PK_AS_REQ(&req);
712 goto out;
714 ret = build_edi(context, context->hx509ctx,
715 ctx->id->anchors, req.trustedCertifiers);
716 if (ret) {
717 krb5_set_error_message(context, ret,
718 N_("pk-init: failed to build "
719 "trustedCertifiers", ""));
720 free_PA_PK_AS_REQ(&req);
721 goto out;
724 req.kdcPkId = NULL;
726 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
727 &req, &size, ret);
729 free_PA_PK_AS_REQ(&req);
731 } else
732 krb5_abortx(context, "internal pkinit error");
733 if (ret) {
734 krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
735 goto out;
737 if (buf.length != size)
738 krb5_abortx(context, "Internal ASN1 encoder error");
740 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
741 if (ret)
742 free(buf.data);
744 if (ret == 0)
745 ret = krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
747 out:
748 free_ContentInfo(&content_info);
750 return ret;
754 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
755 _krb5_pk_mk_padata(krb5_context context,
756 void *c,
757 int ic_flags,
758 int win2k,
759 const KDC_REQ_BODY *req_body,
760 unsigned nonce,
761 METHOD_DATA *md)
763 krb5_pk_init_ctx ctx = c;
764 int win2k_compat;
766 if (ctx->id->certs == NULL && ctx->anonymous == 0) {
767 krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
768 N_("PKINIT: No user certificate given", ""));
769 return HEIM_PKINIT_NO_PRIVATE_KEY;
772 win2k_compat = krb5_config_get_bool_default(context, NULL,
773 win2k,
774 "realms",
775 req_body->realm,
776 "pkinit_win2k",
777 NULL);
779 if (win2k_compat) {
780 ctx->require_binding =
781 krb5_config_get_bool_default(context, NULL,
782 TRUE,
783 "realms",
784 req_body->realm,
785 "pkinit_win2k_require_binding",
786 NULL);
787 ctx->type = PKINIT_WIN2K;
788 } else
789 ctx->type = PKINIT_27;
791 ctx->require_eku =
792 krb5_config_get_bool_default(context, NULL,
793 TRUE,
794 "realms",
795 req_body->realm,
796 "pkinit_require_eku",
797 NULL);
798 if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
799 ctx->require_eku = 0;
800 if (ctx->id->flags & (PKINIT_BTMM | PKINIT_NO_KDC_ANCHOR))
801 ctx->require_eku = 0;
803 ctx->require_krbtgt_otherName =
804 krb5_config_get_bool_default(context, NULL,
805 TRUE,
806 "realms",
807 req_body->realm,
808 "pkinit_require_krbtgt_otherName",
809 NULL);
810 if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
811 ctx->require_krbtgt_otherName = FALSE;
813 ctx->require_hostname_match =
814 krb5_config_get_bool_default(context, NULL,
815 FALSE,
816 "realms",
817 req_body->realm,
818 "pkinit_require_hostname_match",
819 NULL);
821 ctx->trustedCertifiers =
822 krb5_config_get_bool_default(context, NULL,
823 TRUE,
824 "realms",
825 req_body->realm,
826 "pkinit_trustedCertifiers",
827 NULL);
829 return pk_mk_padata(context, ctx, req_body, nonce, md);
832 static krb5_error_code
833 pk_verify_sign(krb5_context context,
834 const void *data,
835 size_t length,
836 struct krb5_pk_identity *id,
837 heim_oid *contentType,
838 krb5_data *content,
839 struct krb5_pk_cert **signer)
841 hx509_certs signer_certs;
842 int ret;
843 unsigned flags = 0, verify_flags = 0;
845 *signer = NULL;
847 if (id->flags & PKINIT_BTMM) {
848 flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
849 flags |= HX509_CMS_VS_NO_KU_CHECK;
850 flags |= HX509_CMS_VS_NO_VALIDATE;
852 if (id->flags & PKINIT_NO_KDC_ANCHOR)
853 flags |= HX509_CMS_VS_NO_VALIDATE;
855 ret = hx509_cms_verify_signed_ext(context->hx509ctx,
856 id->verify_ctx,
857 flags,
858 data,
859 length,
860 NULL,
861 id->certpool,
862 contentType,
863 content,
864 &signer_certs,
865 &verify_flags);
866 if (ret) {
867 pk_copy_error(context, context->hx509ctx, ret,
868 "CMS verify signed failed");
869 return ret;
872 heim_assert((verify_flags & HX509_CMS_VSE_VALIDATED) ||
873 (id->flags & PKINIT_NO_KDC_ANCHOR),
874 "Either PKINIT signer must be validated, or NO_KDC_ANCHOR must be set");
876 if ((verify_flags & HX509_CMS_VSE_VALIDATED) == 0)
877 goto out;
879 *signer = calloc(1, sizeof(**signer));
880 if (*signer == NULL) {
881 krb5_clear_error_message(context);
882 ret = ENOMEM;
883 goto out;
886 ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
887 if (ret) {
888 pk_copy_error(context, context->hx509ctx, ret,
889 "Failed to get one of the signer certs");
890 goto out;
893 out:
894 hx509_certs_free(&signer_certs);
895 if (ret) {
896 if (*signer) {
897 hx509_cert_free((*signer)->cert);
898 free(*signer);
899 *signer = NULL;
903 return ret;
906 static krb5_error_code
907 get_reply_key_win(krb5_context context,
908 const krb5_data *content,
909 unsigned nonce,
910 krb5_keyblock **key)
912 ReplyKeyPack_Win2k key_pack;
913 krb5_error_code ret;
914 size_t size;
916 ret = decode_ReplyKeyPack_Win2k(content->data,
917 content->length,
918 &key_pack,
919 &size);
920 if (ret) {
921 krb5_set_error_message(context, ret,
922 N_("PKINIT decoding reply key failed", ""));
923 free_ReplyKeyPack_Win2k(&key_pack);
924 return ret;
927 if ((unsigned)key_pack.nonce != nonce) {
928 krb5_set_error_message(context, ret,
929 N_("PKINIT enckey nonce is wrong", ""));
930 free_ReplyKeyPack_Win2k(&key_pack);
931 return KRB5KRB_AP_ERR_MODIFIED;
934 *key = malloc (sizeof (**key));
935 if (*key == NULL) {
936 free_ReplyKeyPack_Win2k(&key_pack);
937 return krb5_enomem(context);
940 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
941 free_ReplyKeyPack_Win2k(&key_pack);
942 if (ret) {
943 krb5_set_error_message(context, ret,
944 N_("PKINIT failed copying reply key", ""));
945 free(*key);
946 *key = NULL;
949 return ret;
952 static krb5_error_code
953 get_reply_key(krb5_context context,
954 const krb5_data *content,
955 const krb5_data *req_buffer,
956 krb5_keyblock **key)
958 ReplyKeyPack key_pack;
959 krb5_error_code ret;
960 size_t size;
962 ret = decode_ReplyKeyPack(content->data,
963 content->length,
964 &key_pack,
965 &size);
966 if (ret) {
967 krb5_set_error_message(context, ret,
968 N_("PKINIT decoding reply key failed", ""));
969 free_ReplyKeyPack(&key_pack);
970 return ret;
974 krb5_crypto crypto;
977 * XXX Verify kp.replyKey is a allowed enctype in the
978 * configuration file
981 ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
982 if (ret) {
983 free_ReplyKeyPack(&key_pack);
984 return ret;
987 ret = krb5_verify_checksum(context, crypto, 6,
988 req_buffer->data, req_buffer->length,
989 &key_pack.asChecksum);
990 krb5_crypto_destroy(context, crypto);
991 if (ret) {
992 free_ReplyKeyPack(&key_pack);
993 return ret;
997 *key = malloc (sizeof (**key));
998 if (*key == NULL) {
999 free_ReplyKeyPack(&key_pack);
1000 return krb5_enomem(context);
1003 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1004 free_ReplyKeyPack(&key_pack);
1005 if (ret) {
1006 krb5_set_error_message(context, ret,
1007 N_("PKINIT failed copying reply key", ""));
1008 free(*key);
1009 *key = NULL;
1012 return ret;
1016 static krb5_error_code
1017 pk_verify_host(krb5_context context,
1018 const char *realm,
1019 struct krb5_pk_init_ctx_data *ctx,
1020 struct krb5_pk_cert *host)
1022 krb5_error_code ret = 0;
1024 if (ctx->require_eku) {
1025 ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1026 &asn1_oid_id_pkkdcekuoid, 0);
1027 if (ret) {
1028 krb5_set_error_message(context, ret,
1029 N_("No PK-INIT KDC EKU in kdc certificate", ""));
1030 return ret;
1033 if (ctx->require_krbtgt_otherName) {
1034 hx509_octet_string_list list;
1035 size_t i;
1036 int matched = 0;
1038 ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1039 host->cert,
1040 &asn1_oid_id_pkinit_san,
1041 &list);
1042 if (ret) {
1043 krb5_set_error_message(context, ret,
1044 N_("Failed to find the PK-INIT "
1045 "subjectAltName in the KDC "
1046 "certificate", ""));
1048 return ret;
1052 * subjectAltNames are multi-valued, and a single KDC may serve
1053 * multiple realms. The SAN validation here must accept
1054 * the KDC's cert if *any* of the SANs match the expected KDC.
1055 * It is OK for *some* of the SANs to not match, provided at least
1056 * one does.
1058 for (i = 0; matched == 0 && i < list.len; i++) {
1059 KRB5PrincipalName r;
1061 ret = decode_KRB5PrincipalName(list.val[i].data,
1062 list.val[i].length,
1064 NULL);
1065 if (ret) {
1066 krb5_set_error_message(context, ret,
1067 N_("Failed to decode the PK-INIT "
1068 "subjectAltName in the "
1069 "KDC certificate", ""));
1071 break;
1074 if (r.principalName.name_string.len == 2 &&
1075 strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1076 && strcmp(r.principalName.name_string.val[1], realm) == 0
1077 && strcmp(r.realm, realm) == 0)
1078 matched = 1;
1080 free_KRB5PrincipalName(&r);
1082 hx509_free_octet_string_list(&list);
1084 if (matched == 0 &&
1085 (ctx->id->flags & PKINIT_NO_KDC_ANCHOR) == 0) {
1086 ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1087 /* XXX: Lost in translation... */
1088 krb5_set_error_message(context, ret,
1089 N_("KDC has wrong realm name in "
1090 "the certificate", ""));
1093 if (ret)
1094 return ret;
1096 return ret;
1099 static krb5_error_code
1100 pk_rd_pa_reply_enckey(krb5_context context,
1101 int type,
1102 const heim_octet_string *indata,
1103 const heim_oid *dataType,
1104 const char *realm,
1105 krb5_pk_init_ctx ctx,
1106 krb5_enctype etype,
1107 unsigned nonce,
1108 const krb5_data *req_buffer,
1109 PA_DATA *pa,
1110 krb5_keyblock **key)
1112 krb5_error_code ret;
1113 struct krb5_pk_cert *host = NULL;
1114 krb5_data content;
1115 heim_octet_string unwrapped;
1116 heim_oid contentType = { 0, NULL };
1117 int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1119 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1120 krb5_set_error_message(context, EINVAL,
1121 N_("PKINIT: Invalid content type", ""));
1122 return EINVAL;
1125 if (ctx->type == PKINIT_WIN2K)
1126 flags |= HX509_CMS_UE_ALLOW_WEAK;
1128 ret = hx509_cms_unenvelope(context->hx509ctx,
1129 ctx->id->certs,
1130 flags,
1131 indata->data,
1132 indata->length,
1133 NULL,
1135 &contentType,
1136 &content);
1137 if (ret) {
1138 pk_copy_error(context, context->hx509ctx, ret,
1139 "Failed to unenvelope CMS data in PK-INIT reply");
1140 return ret;
1142 der_free_oid(&contentType);
1144 /* win2k uses ContentInfo */
1145 if (type == PKINIT_WIN2K) {
1146 heim_oid type2;
1148 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1149 if (ret) {
1150 /* windows LH with interesting CMS packets */
1151 size_t ph = 1 + der_length_len(content.length);
1152 unsigned char *ptr = malloc(content.length + ph);
1153 size_t l;
1155 memcpy(ptr + ph, content.data, content.length);
1157 ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1158 ASN1_C_UNIV, CONS, UT_Sequence, &l);
1159 if (ret) {
1160 free(ptr);
1161 return ret;
1163 free(content.data);
1164 content.data = ptr;
1165 content.length += ph;
1167 ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
1168 if (ret)
1169 goto out;
1171 if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1172 ret = EINVAL; /* XXX */
1173 krb5_set_error_message(context, ret,
1174 N_("PKINIT: Invalid content type", ""));
1175 der_free_oid(&type2);
1176 der_free_octet_string(&unwrapped);
1177 goto out;
1179 der_free_oid(&type2);
1180 krb5_data_free(&content);
1181 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1182 der_free_octet_string(&unwrapped);
1183 if (ret) {
1184 krb5_set_error_message(context, ret,
1185 N_("malloc: out of memory", ""));
1186 goto out;
1190 ret = pk_verify_sign(context,
1191 content.data,
1192 content.length,
1193 ctx->id,
1194 &contentType,
1195 &unwrapped,
1196 &host);
1197 if (ret == 0) {
1198 krb5_data_free(&content);
1199 ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
1200 der_free_octet_string(&unwrapped);
1202 if (ret)
1203 goto out;
1205 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1206 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1208 if (host) {
1209 /* make sure that it is the kdc's certificate */
1210 ret = pk_verify_host(context, realm, ctx, host);
1211 if (ret)
1212 goto out;
1214 ctx->kdc_verified = 1;
1217 #if 0
1218 if (type == PKINIT_WIN2K) {
1219 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1220 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1221 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1222 goto out;
1224 } else {
1225 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1226 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1227 krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1228 goto out;
1231 #endif
1233 switch(type) {
1234 case PKINIT_WIN2K:
1235 ret = get_reply_key(context, &content, req_buffer, key);
1236 if (ret != 0 && ctx->require_binding == 0)
1237 ret = get_reply_key_win(context, &content, nonce, key);
1238 break;
1239 case PKINIT_27:
1240 ret = get_reply_key(context, &content, req_buffer, key);
1241 break;
1243 if (ret)
1244 goto out;
1246 /* XXX compare given etype with key->etype */
1248 out:
1249 if (host)
1250 _krb5_pk_cert_free(host);
1251 der_free_oid(&contentType);
1252 krb5_data_free(&content);
1254 return ret;
1258 * RFC 8062 section 7:
1260 * The client then decrypts the KDC contribution key and verifies that
1261 * the ticket session key in the returned ticket is the combined key of
1262 * the KDC contribution key and the reply key.
1264 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1265 _krb5_pk_kx_confirm(krb5_context context,
1266 krb5_pk_init_ctx ctx,
1267 krb5_keyblock *reply_key,
1268 krb5_keyblock *session_key,
1269 PA_DATA *pa_pkinit_kx)
1271 krb5_error_code ret;
1272 EncryptedData ed;
1273 krb5_keyblock ck, sk_verify;
1274 krb5_crypto ck_crypto = NULL;
1275 krb5_crypto rk_crypto = NULL;
1276 size_t len;
1277 krb5_data data;
1278 krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1279 krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1281 heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1282 heim_assert(reply_key != NULL, "reply key is non-NULL");
1283 heim_assert(session_key != NULL, "session key is non-NULL");
1285 /* PA-PKINIT-KX is optional unless anonymous */
1286 if (pa_pkinit_kx == NULL)
1287 return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1289 memset(&ed, 0, sizeof(ed));
1290 krb5_keyblock_zero(&ck);
1291 krb5_keyblock_zero(&sk_verify);
1292 krb5_data_zero(&data);
1294 ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1295 pa_pkinit_kx->padata_value.length,
1296 &ed, &len);
1297 if (ret)
1298 goto out;
1300 if (len != pa_pkinit_kx->padata_value.length) {
1301 ret = KRB5_KDCREP_MODIFIED;
1302 goto out;
1305 ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1306 if (ret)
1307 goto out;
1309 ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1310 KRB5_KU_PA_PKINIT_KX,
1311 &ed, &data);
1312 if (ret)
1313 goto out;
1315 ret = decode_EncryptionKey(data.data, data.length,
1316 &ck, &len);
1317 if (ret)
1318 goto out;
1320 ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1321 if (ret)
1322 goto out;
1324 ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1325 &p1, &p2, session_key->keytype,
1326 &sk_verify);
1327 if (ret)
1328 goto out;
1330 if (sk_verify.keytype != session_key->keytype ||
1331 krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1332 ret = KRB5_KDCREP_MODIFIED;
1333 goto out;
1336 out:
1337 free_EncryptedData(&ed);
1338 krb5_free_keyblock_contents(context, &ck);
1339 krb5_free_keyblock_contents(context, &sk_verify);
1340 if (ck_crypto)
1341 krb5_crypto_destroy(context, ck_crypto);
1342 if (rk_crypto)
1343 krb5_crypto_destroy(context, rk_crypto);
1344 krb5_data_free(&data);
1346 return ret;
1349 static krb5_error_code
1350 pk_rd_pa_reply_dh(krb5_context context,
1351 const heim_octet_string *indata,
1352 const heim_oid *dataType,
1353 const char *realm,
1354 krb5_pk_init_ctx ctx,
1355 krb5_enctype etype,
1356 const DHNonce *c_n,
1357 const DHNonce *k_n,
1358 unsigned nonce,
1359 PA_DATA *pa,
1360 krb5_keyblock **key)
1362 const unsigned char *p;
1363 unsigned char *dh_gen_key = NULL;
1364 struct krb5_pk_cert *host = NULL;
1365 BIGNUM *kdc_dh_pubkey = NULL;
1366 KDCDHKeyInfo kdc_dh_info;
1367 heim_oid contentType = { 0, NULL };
1368 krb5_data content;
1369 krb5_error_code ret;
1370 int dh_gen_keylen = 0;
1371 size_t size;
1373 krb5_data_zero(&content);
1374 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1376 if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1377 krb5_set_error_message(context, EINVAL,
1378 N_("PKINIT: Invalid content type", ""));
1379 return EINVAL;
1382 ret = pk_verify_sign(context,
1383 indata->data,
1384 indata->length,
1385 ctx->id,
1386 &contentType,
1387 &content,
1388 &host);
1389 if (ret)
1390 goto out;
1392 heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
1393 "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
1395 if (host) {
1396 /* make sure that it is the kdc's certificate */
1397 ret = pk_verify_host(context, realm, ctx, host);
1398 if (ret)
1399 goto out;
1401 ctx->kdc_verified = 1;
1404 if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1405 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1406 krb5_set_error_message(context, ret,
1407 N_("pkinit - dh reply contains wrong oid", ""));
1408 goto out;
1411 ret = decode_KDCDHKeyInfo(content.data,
1412 content.length,
1413 &kdc_dh_info,
1414 &size);
1416 if (ret) {
1417 krb5_set_error_message(context, ret,
1418 N_("pkinit - failed to decode "
1419 "KDC DH Key Info", ""));
1420 goto out;
1423 if (kdc_dh_info.nonce != nonce) {
1424 ret = KRB5KRB_AP_ERR_MODIFIED;
1425 krb5_set_error_message(context, ret,
1426 N_("PKINIT: DH nonce is wrong", ""));
1427 goto out;
1430 if (kdc_dh_info.dhKeyExpiration) {
1431 if (k_n == NULL) {
1432 ret = KRB5KRB_ERR_GENERIC;
1433 krb5_set_error_message(context, ret,
1434 N_("pkinit; got key expiration "
1435 "without server nonce", ""));
1436 goto out;
1438 if (c_n == NULL) {
1439 ret = KRB5KRB_ERR_GENERIC;
1440 krb5_set_error_message(context, ret,
1441 N_("pkinit; got DH reuse but no "
1442 "client nonce", ""));
1443 goto out;
1445 } else {
1446 if (k_n) {
1447 ret = KRB5KRB_ERR_GENERIC;
1448 krb5_set_error_message(context, ret,
1449 N_("pkinit: got server nonce "
1450 "without key expiration", ""));
1451 goto out;
1453 c_n = NULL;
1457 p = kdc_dh_info.subjectPublicKey.data;
1458 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1460 if (ctx->keyex == USE_DH) {
1461 DHPublicKey k;
1462 ret = decode_DHPublicKey(p, size, &k, NULL);
1463 if (ret) {
1464 krb5_set_error_message(context, ret,
1465 N_("pkinit: can't decode "
1466 "without key expiration", ""));
1467 goto out;
1470 kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1471 free_DHPublicKey(&k);
1472 if (kdc_dh_pubkey == NULL) {
1473 ret = ENOMEM;
1474 goto out;
1478 size = DH_size(ctx->u.dh);
1480 dh_gen_key = malloc(size);
1481 if (dh_gen_key == NULL) {
1482 ret = krb5_enomem(context);
1483 goto out;
1486 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1487 if (dh_gen_keylen == -1) {
1488 ret = KRB5KRB_ERR_GENERIC;
1489 dh_gen_keylen = 0;
1490 krb5_set_error_message(context, ret,
1491 N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1492 goto out;
1494 if (dh_gen_keylen < (int)size) {
1495 size -= dh_gen_keylen;
1496 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1497 memset(dh_gen_key, 0, size);
1498 dh_gen_keylen += size;
1501 } else {
1502 ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1503 size, &dh_gen_key,
1504 &dh_gen_keylen);
1505 if (ret)
1506 goto out;
1509 if (dh_gen_keylen <= 0) {
1510 ret = EINVAL;
1511 krb5_set_error_message(context, ret,
1512 N_("PKINIT: resulting DH key <= 0", ""));
1513 dh_gen_keylen = 0;
1514 goto out;
1517 *key = malloc (sizeof (**key));
1518 if (*key == NULL) {
1519 ret = krb5_enomem(context);
1520 goto out;
1523 ret = _krb5_pk_octetstring2key(context,
1524 etype,
1525 dh_gen_key, dh_gen_keylen,
1526 c_n, k_n,
1527 *key);
1528 if (ret) {
1529 krb5_set_error_message(context, ret,
1530 N_("PKINIT: can't create key from DH key", ""));
1531 free(*key);
1532 *key = NULL;
1533 goto out;
1536 out:
1537 if (kdc_dh_pubkey)
1538 BN_free(kdc_dh_pubkey);
1539 if (dh_gen_key) {
1540 memset(dh_gen_key, 0, dh_gen_keylen);
1541 free(dh_gen_key);
1543 if (host)
1544 _krb5_pk_cert_free(host);
1545 if (content.data)
1546 krb5_data_free(&content);
1547 der_free_oid(&contentType);
1548 free_KDCDHKeyInfo(&kdc_dh_info);
1550 return ret;
1553 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1554 _krb5_pk_rd_pa_reply(krb5_context context,
1555 const char *realm,
1556 void *c,
1557 krb5_enctype etype,
1558 unsigned nonce,
1559 const krb5_data *req_buffer,
1560 PA_DATA *pa,
1561 krb5_keyblock **key)
1563 krb5_pk_init_ctx ctx = c;
1564 krb5_error_code ret;
1565 size_t size;
1567 /* Check for IETF PK-INIT first */
1568 if (ctx->type == PKINIT_27) {
1569 PA_PK_AS_REP rep;
1570 heim_octet_string os, data;
1571 heim_oid oid;
1573 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1574 krb5_set_error_message(context, EINVAL,
1575 N_("PKINIT: wrong padata recv", ""));
1576 return EINVAL;
1579 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1580 pa->padata_value.length,
1581 &rep,
1582 &size);
1583 if (ret) {
1584 krb5_set_error_message(context, ret,
1585 N_("Failed to decode pkinit AS rep", ""));
1586 return ret;
1589 switch (rep.element) {
1590 case choice_PA_PK_AS_REP_dhInfo:
1591 _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1592 os = rep.u.dhInfo.dhSignedData;
1593 break;
1594 case choice_PA_PK_AS_REP_encKeyPack:
1595 _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1596 os = rep.u.encKeyPack;
1597 break;
1598 default: {
1599 PA_PK_AS_REP_BTMM btmm;
1600 free_PA_PK_AS_REP(&rep);
1601 memset(&rep, 0, sizeof(rep));
1603 _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1605 ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1606 pa->padata_value.length,
1607 &btmm,
1608 &size);
1609 if (ret) {
1610 krb5_set_error_message(context, EINVAL,
1611 N_("PKINIT: -27 reply "
1612 "invalid content type", ""));
1613 return EINVAL;
1616 if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1617 free_PA_PK_AS_REP_BTMM(&btmm);
1618 ret = EINVAL;
1619 krb5_set_error_message(context, ret,
1620 N_("DH mode not supported for BTMM mode", ""));
1621 return ret;
1625 * Transform to IETF style PK-INIT reply so that free works below
1628 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1629 rep.u.encKeyPack.data = btmm.encKeyPack->data;
1630 rep.u.encKeyPack.length = btmm.encKeyPack->length;
1631 btmm.encKeyPack->data = NULL;
1632 btmm.encKeyPack->length = 0;
1633 free_PA_PK_AS_REP_BTMM(&btmm);
1634 os = rep.u.encKeyPack;
1638 ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1639 if (ret) {
1640 free_PA_PK_AS_REP(&rep);
1641 krb5_set_error_message(context, ret,
1642 N_("PKINIT: failed to unwrap CI", ""));
1643 return ret;
1646 switch (rep.element) {
1647 case choice_PA_PK_AS_REP_dhInfo:
1648 ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype,
1649 ctx->clientDHNonce,
1650 rep.u.dhInfo.serverDHNonce,
1651 nonce, pa, key);
1652 break;
1653 case choice_PA_PK_AS_REP_encKeyPack:
1654 ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1655 ctx, etype, nonce, req_buffer, pa, key);
1656 break;
1657 default:
1658 krb5_abortx(context, "pk-init as-rep case not possible to happen");
1660 der_free_octet_string(&data);
1661 der_free_oid(&oid);
1662 free_PA_PK_AS_REP(&rep);
1664 } else if (ctx->type == PKINIT_WIN2K) {
1665 PA_PK_AS_REP_Win2k w2krep;
1667 /* Check for Windows encoding of the AS-REP pa data */
1669 #if 0 /* should this be ? */
1670 if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1671 krb5_set_error_message(context, EINVAL,
1672 "PKINIT: wrong padata recv");
1673 return EINVAL;
1675 #endif
1677 memset(&w2krep, 0, sizeof(w2krep));
1679 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1680 pa->padata_value.length,
1681 &w2krep,
1682 &size);
1683 if (ret) {
1684 krb5_set_error_message(context, ret,
1685 N_("PKINIT: Failed decoding windows "
1686 "pkinit reply %d", ""), (int)ret);
1687 return ret;
1690 krb5_clear_error_message(context);
1692 switch (w2krep.element) {
1693 case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1694 heim_octet_string data;
1695 heim_oid oid;
1697 ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1698 &oid, &data, NULL);
1699 free_PA_PK_AS_REP_Win2k(&w2krep);
1700 if (ret) {
1701 krb5_set_error_message(context, ret,
1702 N_("PKINIT: failed to unwrap CI", ""));
1703 return ret;
1706 ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1707 ctx, etype, nonce, req_buffer, pa, key);
1708 der_free_octet_string(&data);
1709 der_free_oid(&oid);
1711 break;
1713 default:
1714 free_PA_PK_AS_REP_Win2k(&w2krep);
1715 ret = EINVAL;
1716 krb5_set_error_message(context, ret,
1717 N_("PKINIT: win2k reply invalid "
1718 "content type", ""));
1719 break;
1722 } else {
1723 ret = EINVAL;
1724 krb5_set_error_message(context, ret,
1725 N_("PKINIT: unknown reply type", ""));
1728 return ret;
1731 struct prompter {
1732 krb5_context context;
1733 krb5_prompter_fct prompter;
1734 void *prompter_data;
1737 static int
1738 hx_pass_prompter(void *data, const hx509_prompt *prompter)
1740 krb5_error_code ret;
1741 krb5_prompt prompt;
1742 krb5_data password_data;
1743 struct prompter *p = data;
1745 password_data.data = prompter->reply.data;
1746 password_data.length = prompter->reply.length;
1748 prompt.prompt = prompter->prompt;
1749 prompt.hidden = hx509_prompt_hidden(prompter->type);
1750 prompt.reply = &password_data;
1752 switch (prompter->type) {
1753 case HX509_PROMPT_TYPE_INFO:
1754 prompt.type = KRB5_PROMPT_TYPE_INFO;
1755 break;
1756 case HX509_PROMPT_TYPE_PASSWORD:
1757 case HX509_PROMPT_TYPE_QUESTION:
1758 default:
1759 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1760 break;
1763 ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1764 if (ret) {
1765 memset (prompter->reply.data, 0, prompter->reply.length);
1766 return 1;
1768 return 0;
1771 static krb5_error_code
1772 _krb5_pk_set_user_id(krb5_context context,
1773 krb5_principal principal,
1774 krb5_pk_init_ctx ctx,
1775 struct hx509_certs_data *certs)
1777 hx509_certs c = hx509_certs_ref(certs);
1778 hx509_query *q = NULL;
1779 int ret;
1781 if (ctx->id->certs)
1782 hx509_certs_free(&ctx->id->certs);
1783 if (ctx->id->cert) {
1784 hx509_cert_free(ctx->id->cert);
1785 ctx->id->cert = NULL;
1788 ctx->id->certs = c;
1789 ctx->anonymous = 0;
1791 ret = hx509_query_alloc(context->hx509ctx, &q);
1792 if (ret) {
1793 pk_copy_error(context, context->hx509ctx, ret,
1794 "Allocate query to find signing certificate");
1795 return ret;
1798 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1799 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1801 if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1802 ctx->id->flags |= PKINIT_BTMM;
1805 ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1806 hx509_query_free(context->hx509ctx, q);
1808 if (ret == 0 && _krb5_have_debug(context, 2)) {
1809 hx509_name name;
1810 char *str, *sn;
1811 heim_integer i;
1813 ret = hx509_cert_get_subject(ctx->id->cert, &name);
1814 if (ret)
1815 goto out;
1817 ret = hx509_name_to_string(name, &str);
1818 hx509_name_free(&name);
1819 if (ret)
1820 goto out;
1822 ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1823 if (ret) {
1824 free(str);
1825 goto out;
1828 ret = der_print_hex_heim_integer(&i, &sn);
1829 der_free_heim_integer(&i);
1830 if (ret) {
1831 free(str);
1832 goto out;
1835 _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1836 free(str);
1837 free(sn);
1839 out:
1841 return ret;
1844 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1845 _krb5_pk_load_id(krb5_context context,
1846 struct krb5_pk_identity **ret_id,
1847 const char *user_id,
1848 const char *anchor_id,
1849 char * const *chain_list,
1850 char * const *revoke_list,
1851 krb5_prompter_fct prompter,
1852 void *prompter_data,
1853 char *password)
1855 struct krb5_pk_identity *id = NULL;
1856 struct prompter p;
1857 krb5_error_code ret;
1859 *ret_id = NULL;
1861 /* load cert */
1863 id = calloc(1, sizeof(*id));
1864 if (id == NULL)
1865 return krb5_enomem(context);
1867 if (user_id) {
1868 hx509_lock lock;
1870 ret = hx509_lock_init(context->hx509ctx, &lock);
1871 if (ret) {
1872 pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1873 goto out;
1876 if (password && password[0])
1877 hx509_lock_add_password(lock, password);
1879 if (prompter) {
1880 p.context = context;
1881 p.prompter = prompter;
1882 p.prompter_data = prompter_data;
1884 ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1885 if (ret) {
1886 hx509_lock_free(lock);
1887 goto out;
1891 ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1892 hx509_lock_free(lock);
1893 if (ret) {
1894 pk_copy_error(context, context->hx509ctx, ret,
1895 "Failed to init cert certs");
1896 goto out;
1898 } else {
1899 id->certs = NULL;
1902 ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1903 if (ret) {
1904 pk_copy_error(context, context->hx509ctx, ret,
1905 "Failed to init anchors");
1906 goto out;
1909 ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1910 0, NULL, &id->certpool);
1911 if (ret) {
1912 pk_copy_error(context, context->hx509ctx, ret,
1913 "Failed to init chain");
1914 goto out;
1917 while (chain_list && *chain_list) {
1918 ret = hx509_certs_append(context->hx509ctx, id->certpool,
1919 NULL, *chain_list);
1920 if (ret) {
1921 pk_copy_error(context, context->hx509ctx, ret,
1922 "Failed to load chain %s",
1923 *chain_list);
1924 goto out;
1926 chain_list++;
1929 if (revoke_list) {
1930 ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1931 if (ret) {
1932 pk_copy_error(context, context->hx509ctx, ret,
1933 "Failed to init revoke list");
1934 goto out;
1937 while (*revoke_list) {
1938 ret = hx509_revoke_add_crl(context->hx509ctx,
1939 id->revokectx,
1940 *revoke_list);
1941 if (ret) {
1942 pk_copy_error(context, context->hx509ctx, ret,
1943 "Failed to load revoke list");
1944 goto out;
1946 revoke_list++;
1948 } else
1949 hx509_context_set_missing_revoke(context->hx509ctx, 1);
1951 ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1952 if (ret) {
1953 pk_copy_error(context, context->hx509ctx, ret,
1954 "Failed to init verify context");
1955 goto out;
1958 hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1959 hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1961 out:
1962 if (ret) {
1963 hx509_verify_destroy_ctx(id->verify_ctx);
1964 hx509_certs_free(&id->certs);
1965 hx509_certs_free(&id->anchors);
1966 hx509_certs_free(&id->certpool);
1967 hx509_revoke_free(&id->revokectx);
1968 free(id);
1969 } else
1970 *ret_id = id;
1972 return ret;
1979 static void
1980 pk_copy_error(krb5_context context,
1981 hx509_context hx509ctx,
1982 int hxret,
1983 const char *fmt,
1984 ...)
1986 va_list va;
1987 char *s, *f;
1988 int ret;
1990 va_start(va, fmt);
1991 ret = vasprintf(&f, fmt, va);
1992 va_end(va);
1993 if (ret == -1 || f == NULL) {
1994 krb5_clear_error_message(context);
1995 return;
1998 s = hx509_get_error_string(hx509ctx, hxret);
1999 if (s == NULL) {
2000 krb5_clear_error_message(context);
2001 free(f);
2002 return;
2004 krb5_set_error_message(context, hxret, "%s: %s", f, s);
2005 free(s);
2006 free(f);
2009 static int
2010 parse_integer(krb5_context context, char **p, const char *file, int lineno,
2011 const char *name, heim_integer *integer)
2013 int ret;
2014 char *p1;
2015 p1 = strsep(p, " \t");
2016 if (p1 == NULL) {
2017 krb5_set_error_message(context, EINVAL,
2018 N_("moduli file %s missing %s on line %d", ""),
2019 file, name, lineno);
2020 return EINVAL;
2022 ret = der_parse_hex_heim_integer(p1, integer);
2023 if (ret) {
2024 krb5_set_error_message(context, ret,
2025 N_("moduli file %s failed parsing %s "
2026 "on line %d", ""),
2027 file, name, lineno);
2028 return ret;
2031 return 0;
2034 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2035 _krb5_parse_moduli_line(krb5_context context,
2036 const char *file,
2037 int lineno,
2038 char *p,
2039 struct krb5_dh_moduli **m)
2041 struct krb5_dh_moduli *m1;
2042 char *p1;
2043 int ret;
2045 *m = NULL;
2047 m1 = calloc(1, sizeof(*m1));
2048 if (m1 == NULL)
2049 return krb5_enomem(context);
2051 while (isspace((unsigned char)*p))
2052 p++;
2053 if (*p == '#') {
2054 free(m1);
2055 return 0;
2057 ret = EINVAL;
2059 p1 = strsep(&p, " \t");
2060 if (p1 == NULL) {
2061 krb5_set_error_message(context, ret,
2062 N_("moduli file %s missing name on line %d", ""),
2063 file, lineno);
2064 goto out;
2066 m1->name = strdup(p1);
2067 if (m1->name == NULL) {
2068 ret = krb5_enomem(context);
2069 goto out;
2072 p1 = strsep(&p, " \t");
2073 if (p1 == NULL) {
2074 krb5_set_error_message(context, ret,
2075 N_("moduli file %s missing bits on line %d", ""),
2076 file, lineno);
2077 goto out;
2080 m1->bits = atoi(p1);
2081 if (m1->bits == 0) {
2082 krb5_set_error_message(context, ret,
2083 N_("moduli file %s has un-parsable "
2084 "bits on line %d", ""), file, lineno);
2085 goto out;
2088 ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2089 if (ret)
2090 goto out;
2091 ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2092 if (ret)
2093 goto out;
2094 ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2095 if (ret) {
2096 m1->q.negative = 0;
2097 m1->q.length = 0;
2098 m1->q.data = 0;
2099 krb5_clear_error_message(context);
2102 *m = m1;
2104 return 0;
2105 out:
2106 free(m1->name);
2107 der_free_heim_integer(&m1->p);
2108 der_free_heim_integer(&m1->g);
2109 der_free_heim_integer(&m1->q);
2110 free(m1);
2111 return ret;
2114 static void
2115 free_moduli_element(struct krb5_dh_moduli *element)
2117 free(element->name);
2118 der_free_heim_integer(&element->p);
2119 der_free_heim_integer(&element->g);
2120 der_free_heim_integer(&element->q);
2121 free(element);
2124 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2125 _krb5_free_moduli(struct krb5_dh_moduli **moduli)
2127 int i;
2128 for (i = 0; moduli[i] != NULL; i++)
2129 free_moduli_element(moduli[i]);
2130 free(moduli);
2133 static const char *default_moduli_RFC2412_MODP_group2 =
2134 /* name */
2135 "RFC2412-MODP-group2 "
2136 /* bits */
2137 "1024 "
2138 /* p */
2139 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2140 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2141 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2142 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2143 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2144 "FFFFFFFF" "FFFFFFFF "
2145 /* g */
2146 "02 "
2147 /* q */
2148 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2149 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2150 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2151 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2152 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2153 "FFFFFFFF" "FFFFFFFF";
2155 static const char *default_moduli_rfc3526_MODP_group14 =
2156 /* name */
2157 "rfc3526-MODP-group14 "
2158 /* bits */
2159 "2048 "
2160 /* p */
2161 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2162 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2163 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2164 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2165 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2166 "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2167 "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2168 "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2169 "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2170 "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2171 "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2172 /* g */
2173 "02 "
2174 /* q */
2175 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2176 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2177 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2178 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2179 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2180 "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2181 "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2182 "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2183 "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2184 "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2185 "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2187 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2188 _krb5_parse_moduli(krb5_context context, const char *file,
2189 struct krb5_dh_moduli ***moduli)
2191 /* name bits P G Q */
2192 krb5_error_code ret;
2193 struct krb5_dh_moduli **m = NULL, **m2;
2194 char buf[4096];
2195 FILE *f;
2196 int lineno = 0, n = 0;
2198 *moduli = NULL;
2200 m = calloc(1, sizeof(m[0]) * 3);
2201 if (m == NULL)
2202 return krb5_enomem(context);
2204 strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2205 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
2206 if (ret) {
2207 _krb5_free_moduli(m);
2208 return ret;
2210 n++;
2212 strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2213 ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
2214 if (ret) {
2215 _krb5_free_moduli(m);
2216 return ret;
2218 n++;
2221 if (file == NULL)
2222 file = MODULI_FILE;
2225 char *exp_file;
2227 if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2228 f = fopen(exp_file, "r");
2229 krb5_xfree(exp_file);
2230 } else {
2231 f = NULL;
2235 if (f == NULL) {
2236 *moduli = m;
2237 return 0;
2239 rk_cloexec_file(f);
2241 while(fgets(buf, sizeof(buf), f) != NULL) {
2242 struct krb5_dh_moduli *element;
2244 buf[strcspn(buf, "\n")] = '\0';
2245 lineno++;
2247 ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
2248 if (ret)
2249 break;
2250 if (element == NULL)
2251 continue;
2253 m2 = realloc(m, (n + 2) * sizeof(m[0]));
2254 if (m2 == NULL) {
2255 free_moduli_element(element);
2256 ret = krb5_enomem(context);
2257 break;
2259 m = m2;
2261 m[n] = element;
2262 m[n + 1] = NULL;
2263 n++;
2265 if (ret) {
2266 _krb5_free_moduli(m);
2267 m = NULL;
2270 *moduli = m;
2272 (void) fclose(f);
2273 return ret;
2276 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2277 _krb5_dh_group_ok(krb5_context context, unsigned long bits,
2278 heim_integer *p, heim_integer *g, heim_integer *q,
2279 struct krb5_dh_moduli **moduli,
2280 char **name)
2282 int i;
2284 if (name)
2285 *name = NULL;
2287 for (i = 0; moduli[i] != NULL; i++) {
2288 if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2289 der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2290 (q == NULL || moduli[i]->q.length == 0 ||
2291 der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2293 if (bits && bits > moduli[i]->bits) {
2294 krb5_set_error_message(context,
2295 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2296 N_("PKINIT: DH group parameter %s "
2297 "not accepted, not enough bits "
2298 "generated", ""),
2299 moduli[i]->name);
2300 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2302 if (name)
2303 *name = strdup(moduli[i]->name);
2304 return 0;
2307 krb5_set_error_message(context,
2308 KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2309 N_("PKINIT: DH group parameter not ok", ""));
2310 return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2312 #endif /* PKINIT */
2314 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2315 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2317 #ifdef PKINIT
2318 krb5_pk_init_ctx ctx;
2320 if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2321 return;
2322 ctx = opt->opt_private->pk_init_ctx;
2323 switch (ctx->keyex) {
2324 case USE_DH:
2325 if (ctx->u.dh)
2326 DH_free(ctx->u.dh);
2327 break;
2328 case USE_RSA:
2329 break;
2330 case USE_ECDH:
2331 if (ctx->u.eckey)
2332 _krb5_pk_eckey_free(ctx->u.eckey);
2333 break;
2335 if (ctx->id) {
2336 hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2337 hx509_certs_free(&ctx->id->certs);
2338 hx509_cert_free(ctx->id->cert);
2339 hx509_certs_free(&ctx->id->anchors);
2340 hx509_certs_free(&ctx->id->certpool);
2342 if (ctx->clientDHNonce) {
2343 krb5_free_data(NULL, ctx->clientDHNonce);
2344 ctx->clientDHNonce = NULL;
2346 if (ctx->m)
2347 _krb5_free_moduli(ctx->m);
2348 free(ctx->id);
2349 ctx->id = NULL;
2351 free(opt->opt_private->pk_init_ctx);
2352 opt->opt_private->pk_init_ctx = NULL;
2353 #endif
2356 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2357 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2358 krb5_get_init_creds_opt *opt,
2359 krb5_principal principal,
2360 const char *user_id,
2361 const char *x509_anchors,
2362 char * const * pool,
2363 char * const * pki_revoke,
2364 int flags,
2365 krb5_prompter_fct prompter,
2366 void *prompter_data,
2367 char *password)
2369 #ifdef PKINIT
2370 krb5_error_code ret;
2371 char **freeme1 = NULL;
2372 char **freeme2 = NULL;
2373 char *anchors = NULL;
2375 if (opt->opt_private == NULL) {
2376 krb5_set_error_message(context, EINVAL,
2377 N_("PKINIT: on non extendable opt", ""));
2378 return EINVAL;
2381 opt->opt_private->pk_init_ctx =
2382 calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2383 if (opt->opt_private->pk_init_ctx == NULL)
2384 return krb5_enomem(context);
2385 opt->opt_private->pk_init_ctx->require_binding = 0;
2386 opt->opt_private->pk_init_ctx->require_eku = 1;
2387 opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2388 opt->opt_private->pk_init_ctx->peer = NULL;
2390 /* XXX implement krb5_appdefault_strings */
2391 if (pool == NULL)
2392 pool = freeme1 = krb5_config_get_strings(context, NULL, "appdefaults",
2393 "pkinit_pool", NULL);
2395 if (pki_revoke == NULL)
2396 pki_revoke = freeme2 = krb5_config_get_strings(context, NULL,
2397 "appdefaults",
2398 "pkinit_revoke", NULL);
2400 if (x509_anchors == NULL) {
2401 krb5_appdefault_string(context, "kinit",
2402 krb5_principal_get_realm(context, principal),
2403 "pkinit_anchors", NULL, &anchors);
2404 x509_anchors = anchors;
2407 if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
2408 opt->opt_private->pk_init_ctx->anonymous = 1;
2410 if ((flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR) == 0 &&
2411 x509_anchors == NULL) {
2412 krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
2413 N_("PKINIT: No anchor given", ""));
2414 return HEIM_PKINIT_NO_VALID_CA;
2417 ret = _krb5_pk_load_id(context,
2418 &opt->opt_private->pk_init_ctx->id,
2419 user_id,
2420 x509_anchors,
2421 pool,
2422 pki_revoke,
2423 prompter,
2424 prompter_data,
2425 password);
2426 krb5_config_free_strings(freeme2);
2427 krb5_config_free_strings(freeme1);
2428 free(anchors);
2429 if (ret) {
2430 free(opt->opt_private->pk_init_ctx);
2431 opt->opt_private->pk_init_ctx = NULL;
2432 return ret;
2434 if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
2435 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2436 if (principal && krb5_principal_is_lkdc(context, principal))
2437 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2438 if (flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR)
2439 opt->opt_private->pk_init_ctx->id->flags |= PKINIT_NO_KDC_ANCHOR;
2441 if (opt->opt_private->pk_init_ctx->id->certs) {
2442 ret = _krb5_pk_set_user_id(context,
2443 principal,
2444 opt->opt_private->pk_init_ctx,
2445 opt->opt_private->pk_init_ctx->id->certs);
2446 if (ret) {
2447 free(opt->opt_private->pk_init_ctx);
2448 opt->opt_private->pk_init_ctx = NULL;
2449 return ret;
2451 } else
2452 opt->opt_private->pk_init_ctx->id->cert = NULL;
2454 if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
2455 hx509_context hx509ctx = context->hx509ctx;
2456 hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2458 opt->opt_private->pk_init_ctx->keyex = USE_DH;
2461 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2463 if (cert) {
2464 AlgorithmIdentifier alg;
2466 ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2467 if (ret == 0) {
2468 if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2469 opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2470 free_AlgorithmIdentifier(&alg);
2474 } else {
2475 opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2477 if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2478 krb5_set_error_message(context, EINVAL,
2479 N_("No anonymous pkinit support in RSA mode", ""));
2480 return EINVAL;
2484 return 0;
2485 #else
2486 krb5_set_error_message(context, EINVAL,
2487 N_("no support for PKINIT compiled in", ""));
2488 return EINVAL;
2489 #endif
2492 krb5_error_code KRB5_LIB_FUNCTION
2493 krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2494 krb5_get_init_creds_opt *opt,
2495 struct hx509_certs_data *certs)
2497 #ifdef PKINIT
2498 if (opt->opt_private == NULL) {
2499 krb5_set_error_message(context, EINVAL,
2500 N_("PKINIT: on non extendable opt", ""));
2501 return EINVAL;
2503 if (opt->opt_private->pk_init_ctx == NULL) {
2504 krb5_set_error_message(context, EINVAL,
2505 N_("PKINIT: on pkinit context", ""));
2506 return EINVAL;
2509 return _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2510 #else
2511 krb5_set_error_message(context, EINVAL,
2512 N_("no support for PKINIT compiled in", ""));
2513 return EINVAL;
2514 #endif
2517 #ifdef PKINIT
2519 static int
2520 get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2522 hx509_octet_string_list list;
2523 int ret;
2525 *upn = NULL;
2527 ret = hx509_cert_find_subjectAltName_otherName(context,
2528 cert,
2529 &asn1_oid_id_pkinit_ms_san,
2530 &list);
2531 if (ret)
2532 return 0;
2534 if (list.len > 0 && list.val[0].length > 0)
2535 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2536 upn, NULL);
2537 else
2538 ret = 1;
2539 hx509_free_octet_string_list(&list);
2541 return ret;
2544 static int
2545 find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2547 char *upn;
2548 int ret;
2550 ret = get_ms_san(context, cert, &upn);
2551 if (ret == 0)
2552 free(upn);
2553 return ret;
2558 #endif
2561 * Private since it need to be redesigned using krb5_get_init_creds()
2564 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2565 krb5_pk_enterprise_cert(krb5_context context,
2566 const char *user_id,
2567 krb5_const_realm realm,
2568 krb5_principal *principal,
2569 struct hx509_certs_data **res)
2571 #ifdef PKINIT
2572 krb5_error_code ret;
2573 hx509_certs certs, result;
2574 hx509_cert cert = NULL;
2575 hx509_query *q;
2576 char *name;
2578 *principal = NULL;
2579 if (res)
2580 *res = NULL;
2582 if (user_id == NULL) {
2583 krb5_set_error_message(context, ENOENT, "no user id");
2584 return ENOENT;
2587 ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2588 if (ret) {
2589 pk_copy_error(context, context->hx509ctx, ret,
2590 "Failed to init cert certs");
2591 goto out;
2594 ret = hx509_query_alloc(context->hx509ctx, &q);
2595 if (ret) {
2596 krb5_set_error_message(context, ret, "out of memory");
2597 hx509_certs_free(&certs);
2598 goto out;
2601 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2602 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2603 hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2604 hx509_query_match_cmp_func(q, find_ms_san, NULL);
2606 ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2607 hx509_query_free(context->hx509ctx, q);
2608 hx509_certs_free(&certs);
2609 if (ret) {
2610 pk_copy_error(context, context->hx509ctx, ret,
2611 "Failed to find PKINIT certificate");
2612 return ret;
2615 ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2616 hx509_certs_free(&result);
2617 if (ret) {
2618 pk_copy_error(context, context->hx509ctx, ret,
2619 "Failed to get one cert");
2620 goto out;
2623 ret = get_ms_san(context->hx509ctx, cert, &name);
2624 if (ret) {
2625 pk_copy_error(context, context->hx509ctx, ret,
2626 "Failed to get MS SAN");
2627 goto out;
2630 ret = krb5_make_principal(context, principal, realm, name, NULL);
2631 free(name);
2632 if (ret)
2633 goto out;
2635 krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2637 if (res) {
2638 ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2639 if (ret)
2640 goto out;
2642 ret = hx509_certs_add(context->hx509ctx, *res, cert);
2643 if (ret) {
2644 hx509_certs_free(res);
2645 goto out;
2649 out:
2650 hx509_cert_free(cert);
2652 return ret;
2653 #else
2654 krb5_set_error_message(context, EINVAL,
2655 N_("no support for PKINIT compiled in", ""));
2656 return EINVAL;
2657 #endif
2660 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
2661 _krb5_pk_is_kdc_verified(krb5_context context,
2662 krb5_get_init_creds_opt *opt)
2664 if (opt == NULL ||
2665 opt->opt_private == NULL ||
2666 opt->opt_private->pk_init_ctx == NULL)
2667 return FALSE;
2669 return opt->opt_private->pk_init_ctx->kdc_verified;