1/*	$NetBSD: pkinit.c,v 1.6 2023/06/19 21:41:44 christos Exp $	*/
2
3/*
4 * Copyright (c) 2003 - 2016 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include "krb5_locl.h"
39
40struct krb5_dh_moduli {
41    char *name;
42    unsigned long bits;
43    heim_integer p;
44    heim_integer g;
45    heim_integer q;
46};
47
48#ifdef PKINIT
49
50#include <krb5/cms_asn1.h>
51#include <krb5/pkcs8_asn1.h>
52#include <krb5/pkcs9_asn1.h>
53#include <krb5/pkcs12_asn1.h>
54#include <krb5/pkinit_asn1.h>
55#include <krb5/asn1_err.h>
56
57#include <krb5/der.h>
58
59struct krb5_pk_cert {
60    hx509_cert cert;
61};
62
63static void
64pk_copy_error(krb5_context context,
65	      hx509_context hx509ctx,
66	      int hxret,
67	      const char *fmt,
68	      ...)
69    __attribute__ ((__format__ (__printf__, 4, 5)));
70
71/*
72 *
73 */
74
75KRB5_LIB_FUNCTION void KRB5_LIB_CALL
76_krb5_pk_cert_free(struct krb5_pk_cert *cert)
77{
78    if (cert->cert) {
79	hx509_cert_free(cert->cert);
80    }
81    free(cert);
82}
83
84static krb5_error_code
85BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer)
86{
87    integer->length = BN_num_bytes(bn);
88    integer->data = malloc(integer->length);
89    if (integer->data == NULL) {
90	krb5_clear_error_message(context);
91	return ENOMEM;
92    }
93    BN_bn2bin(bn, integer->data);
94    integer->negative = BN_is_negative(bn);
95    return 0;
96}
97
98static BIGNUM *
99integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
100{
101    BIGNUM *bn;
102
103    bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
104    if (bn == NULL) {
105	krb5_set_error_message(context, ENOMEM,
106			       N_("PKINIT: parsing BN failed %s", ""), field);
107	return NULL;
108    }
109    BN_set_negative(bn, f->negative);
110    return bn;
111}
112
113static krb5_error_code
114select_dh_group(krb5_context context, DH *dh, unsigned long bits,
115		struct krb5_dh_moduli **moduli)
116{
117    const struct krb5_dh_moduli *m;
118
119    if (bits == 0) {
120	m = moduli[1]; /* XXX */
121	if (m == NULL)
122	    m = moduli[0]; /* XXX */
123    } else {
124	int i;
125	for (i = 0; moduli[i] != NULL; i++) {
126	    if (bits < moduli[i]->bits)
127		break;
128	}
129	if (moduli[i] == NULL) {
130	    krb5_set_error_message(context, EINVAL,
131				   N_("Did not find a DH group parameter "
132				      "matching requirement of %lu bits", ""),
133				   bits);
134	    return EINVAL;
135	}
136	m = moduli[i];
137    }
138
139    BIGNUM *p = integer_to_BN(context, "p", &m->p);
140    if (p == NULL)
141	return ENOMEM;
142    BIGNUM *g = integer_to_BN(context, "g", &m->g);
143    if (g == NULL)
144	return ENOMEM;
145    BIGNUM *q = integer_to_BN(context, "q", &m->q);
146    if (q == NULL)
147	return ENOMEM;
148#if OPENSSL_VERSION_NUMBER < 0x10100000UL
149    dh->p = p;
150    dh->q = q;
151    dh->g = g;
152#else
153    DH_set0_pqg(dh, p, q, g);
154#endif
155
156    return 0;
157}
158
159struct certfind {
160    const char *type;
161    const heim_oid *oid;
162};
163
164/*
165 * Try searchin the key by to use by first looking for for PK-INIT
166 * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
167 */
168
169static krb5_error_code
170find_cert(krb5_context context, struct krb5_pk_identity *id,
171	  hx509_query *q, hx509_cert *cert)
172{
173    struct certfind cf[4] = {
174	{ "MobileMe EKU", NULL },
175	{ "PKINIT EKU", NULL },
176	{ "MS EKU", NULL },
177	{ "any (or no)", NULL }
178    };
179    int ret = HX509_CERT_NOT_FOUND;
180    size_t i, start = 1;
181    unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
182    const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
183
184
185    if (id->flags & PKINIT_BTMM)
186	start = 0;
187
188    cf[0].oid = &mobileMe;
189    cf[1].oid = &asn1_oid_id_pkekuoid;
190    cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
191    cf[3].oid = NULL;
192
193    for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
194	ret = hx509_query_match_eku(q, cf[i].oid);
195	if (ret) {
196	    pk_copy_error(context, context->hx509ctx, ret,
197			  "Failed setting %s OID", cf[i].type);
198	    return ret;
199	}
200
201	ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
202	if (ret == 0)
203	    break;
204	pk_copy_error(context, context->hx509ctx, ret,
205		      "Failed finding certificate with %s OID", cf[i].type);
206    }
207    return ret;
208}
209
210
211static krb5_error_code
212create_signature(krb5_context context,
213		 const heim_oid *eContentType,
214		 krb5_data *eContent,
215		 struct krb5_pk_identity *id,
216		 hx509_peer_info peer,
217		 krb5_data *sd_data)
218{
219    int ret, flags = 0;
220
221    if (id->cert == NULL)
222	flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
223
224    ret = hx509_cms_create_signed_1(context->hx509ctx,
225				    flags,
226				    eContentType,
227				    eContent->data,
228				    eContent->length,
229				    NULL,
230				    id->cert,
231				    peer,
232				    NULL,
233				    id->certs,
234				    sd_data);
235    if (ret) {
236	pk_copy_error(context, context->hx509ctx, ret,
237		      "Create CMS signedData");
238	return ret;
239    }
240
241    return 0;
242}
243
244static int
245cert2epi(hx509_context context, void *ctx, hx509_cert c)
246{
247    ExternalPrincipalIdentifiers *ids = ctx;
248    ExternalPrincipalIdentifier id;
249    hx509_name subject = NULL;
250    void *p;
251    int ret;
252
253    if (ids->len > 10)
254	return 0;
255
256    memset(&id, 0, sizeof(id));
257
258    ret = hx509_cert_get_subject(c, &subject);
259    if (ret)
260	return ret;
261
262    if (hx509_name_is_null_p(subject) != 0) {
263
264	id.subjectName = calloc(1, sizeof(*id.subjectName));
265	if (id.subjectName == NULL) {
266	    hx509_name_free(&subject);
267	    free_ExternalPrincipalIdentifier(&id);
268	    return ENOMEM;
269	}
270
271	ret = hx509_name_binary(subject, id.subjectName);
272	if (ret) {
273	    hx509_name_free(&subject);
274	    free_ExternalPrincipalIdentifier(&id);
275	    return ret;
276	}
277    }
278    hx509_name_free(&subject);
279
280
281    id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
282    if (id.issuerAndSerialNumber == NULL) {
283	free_ExternalPrincipalIdentifier(&id);
284	return ENOMEM;
285    }
286
287    {
288	IssuerAndSerialNumber iasn;
289	hx509_name issuer;
290	size_t size = 0;
291
292	memset(&iasn, 0, sizeof(iasn));
293
294	ret = hx509_cert_get_issuer(c, &issuer);
295	if (ret) {
296	    free_ExternalPrincipalIdentifier(&id);
297	    return ret;
298	}
299
300	ret = hx509_name_to_Name(issuer, &iasn.issuer);
301	hx509_name_free(&issuer);
302	if (ret) {
303	    free_ExternalPrincipalIdentifier(&id);
304	    return ret;
305	}
306
307	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
308	if (ret) {
309	    free_IssuerAndSerialNumber(&iasn);
310	    free_ExternalPrincipalIdentifier(&id);
311	    return ret;
312	}
313
314	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
315			   id.issuerAndSerialNumber->data,
316			   id.issuerAndSerialNumber->length,
317			   &iasn, &size, ret);
318	free_IssuerAndSerialNumber(&iasn);
319	if (ret) {
320            free_ExternalPrincipalIdentifier(&id);
321	    return ret;
322        }
323	if (id.issuerAndSerialNumber->length != size)
324	    abort();
325    }
326
327    id.subjectKeyIdentifier = NULL;
328
329    p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
330    if (p == NULL) {
331	free_ExternalPrincipalIdentifier(&id);
332	return ENOMEM;
333    }
334
335    ids->val = p;
336    ids->val[ids->len] = id;
337    ids->len++;
338
339    return 0;
340}
341
342static krb5_error_code
343build_edi(krb5_context context,
344	  hx509_context hx509ctx,
345	  hx509_certs certs,
346	  ExternalPrincipalIdentifiers *ids)
347{
348    return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
349}
350
351static krb5_error_code
352build_auth_pack(krb5_context context,
353		unsigned nonce,
354		krb5_pk_init_ctx ctx,
355		const KDC_REQ_BODY *body,
356		AuthPack *a)
357{
358    size_t buf_size, len = 0;
359    krb5_error_code ret;
360    void *buf;
361    krb5_timestamp sec;
362    int32_t usec;
363    Checksum checksum;
364
365    krb5_clear_error_message(context);
366
367    memset(&checksum, 0, sizeof(checksum));
368
369    krb5_us_timeofday(context, &sec, &usec);
370    a->pkAuthenticator.ctime = sec;
371    a->pkAuthenticator.nonce = nonce;
372
373    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
374    if (ret)
375	return ret;
376    if (buf_size != len)
377	krb5_abortx(context, "internal error in ASN.1 encoder");
378
379    ret = krb5_create_checksum(context,
380			       NULL,
381			       0,
382			       CKSUMTYPE_SHA1,
383			       buf,
384			       len,
385			       &checksum);
386    free(buf);
387    if (ret)
388	return ret;
389
390    ALLOC(a->pkAuthenticator.paChecksum, 1);
391    if (a->pkAuthenticator.paChecksum == NULL) {
392	return krb5_enomem(context);
393    }
394
395    ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
396			 checksum.checksum.data, checksum.checksum.length);
397    free_Checksum(&checksum);
398    if (ret)
399	return ret;
400
401    if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
402	const char *moduli_file;
403	unsigned long dh_min_bits;
404	krb5_data dhbuf;
405	size_t size = 0;
406
407	krb5_data_zero(&dhbuf);
408
409
410
411	moduli_file = krb5_config_get_string(context, NULL,
412					     "libdefaults",
413					     "moduli",
414					     NULL);
415
416	dh_min_bits =
417	    krb5_config_get_int_default(context, NULL, 0,
418					"libdefaults",
419					"pkinit_dh_min_bits",
420					NULL);
421
422	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
423	if (ret)
424	    return ret;
425
426	ctx->u.dh = DH_new();
427	if (ctx->u.dh == NULL)
428	    return krb5_enomem(context);
429
430	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
431	if (ret)
432	    return ret;
433
434	if (DH_generate_key(ctx->u.dh) != 1) {
435	    krb5_set_error_message(context, ENOMEM,
436				   N_("pkinit: failed to generate DH key", ""));
437	    return ENOMEM;
438	}
439
440
441	if (1 /* support_cached_dh */) {
442	    ALLOC(a->clientDHNonce, 1);
443	    if (a->clientDHNonce == NULL) {
444		krb5_clear_error_message(context);
445		return ENOMEM;
446	    }
447	    ret = krb5_data_alloc(a->clientDHNonce, 40);
448	    if (a->clientDHNonce == NULL) {
449		krb5_clear_error_message(context);
450		return ret;
451	    }
452	    RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
453	    ret = krb5_copy_data(context, a->clientDHNonce,
454				 &ctx->clientDHNonce);
455	    if (ret)
456		return ret;
457	}
458
459	ALLOC(a->clientPublicValue, 1);
460	if (a->clientPublicValue == NULL)
461	    return ENOMEM;
462
463	if (ctx->keyex == USE_DH) {
464	    DH *dh = ctx->u.dh;
465	    const BIGNUM *p, *q, *g;
466	    DomainParameters dp;
467	    heim_integer dh_pub_key;
468
469	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
470			       &a->clientPublicValue->algorithm.algorithm);
471	    if (ret)
472		return ret;
473
474	    memset(&dp, 0, sizeof(dp));
475
476#if OPENSSL_VERSION_NUMBER < 0x10100000UL
477	    p = dh->p;
478	    q = dh->q;
479	    g = dh->g;
480#else
481	    DH_get0_pqg(dh, &p, &q, &g);
482#endif
483
484	    ret = BN_to_integer(context, p, &dp.p);
485	    if (ret) {
486		free_DomainParameters(&dp);
487		return ret;
488	    }
489	    ret = BN_to_integer(context, g, &dp.g);
490	    if (ret) {
491		free_DomainParameters(&dp);
492		return ret;
493	    }
494	    dp.q = calloc(1, sizeof(*dp.q));
495	    if (dp.q == NULL) {
496		free_DomainParameters(&dp);
497		return ENOMEM;
498	    }
499	    ret = BN_to_integer(context, q, dp.q);
500	    if (ret) {
501		free_DomainParameters(&dp);
502		return ret;
503	    }
504	    dp.j = NULL;
505	    dp.validationParms = NULL;
506
507	    a->clientPublicValue->algorithm.parameters =
508		malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
509	    if (a->clientPublicValue->algorithm.parameters == NULL) {
510		free_DomainParameters(&dp);
511		return ret;
512	    }
513
514	    ASN1_MALLOC_ENCODE(DomainParameters,
515			       a->clientPublicValue->algorithm.parameters->data,
516			       a->clientPublicValue->algorithm.parameters->length,
517			       &dp, &size, ret);
518	    free_DomainParameters(&dp);
519	    if (ret)
520		return ret;
521	    if (size != a->clientPublicValue->algorithm.parameters->length)
522		krb5_abortx(context, "Internal ASN1 encoder error");
523
524	    const BIGNUM *pub_key;
525#if OPENSSL_VERSION_NUMBER < 0x10100000UL
526	    pub_key = dh->pub_key;
527#else
528	    DH_get0_key(dh, &pub_key, NULL);
529#endif
530	    ret = BN_to_integer(context, pub_key, &dh_pub_key);
531	    if (ret)
532		return ret;
533
534	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
535			       &dh_pub_key, &size, ret);
536	    der_free_heim_integer(&dh_pub_key);
537	    if (ret)
538		return ret;
539	    if (size != dhbuf.length)
540		krb5_abortx(context, "asn1 internal error");
541            a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
542            a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
543	} else if (ctx->keyex == USE_ECDH) {
544            ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
545            if (ret)
546                return ret;
547	} else
548	    krb5_abortx(context, "internal error");
549    }
550
551    {
552	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
553	if (a->supportedCMSTypes == NULL)
554	    return ENOMEM;
555
556	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
557				     ctx->id->cert,
558				     &a->supportedCMSTypes->val,
559				     &a->supportedCMSTypes->len);
560	if (ret)
561	    return ret;
562    }
563
564    return ret;
565}
566
567KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
568_krb5_pk_mk_ContentInfo(krb5_context context,
569			const krb5_data *buf,
570			const heim_oid *oid,
571			struct ContentInfo *content_info)
572{
573    krb5_error_code ret;
574
575    ret = der_copy_oid(oid, &content_info->contentType);
576    if (ret)
577	return ret;
578    ALLOC(content_info->content, 1);
579    if (content_info->content == NULL)
580	return ENOMEM;
581    content_info->content->data = malloc(buf->length);
582    if (content_info->content->data == NULL)
583	return ENOMEM;
584    memcpy(content_info->content->data, buf->data, buf->length);
585    content_info->content->length = buf->length;
586    return 0;
587}
588
589static krb5_error_code
590pk_mk_padata(krb5_context context,
591	     krb5_pk_init_ctx ctx,
592	     const KDC_REQ_BODY *req_body,
593	     unsigned nonce,
594	     METHOD_DATA *md)
595{
596    struct ContentInfo content_info;
597    krb5_error_code ret;
598    const heim_oid *oid = NULL;
599    size_t size = 0;
600    krb5_data buf, sd_buf;
601    int pa_type = -1;
602
603    krb5_data_zero(&buf);
604    krb5_data_zero(&sd_buf);
605    memset(&content_info, 0, sizeof(content_info));
606
607    if (ctx->type == PKINIT_WIN2K) {
608	AuthPack_Win2k ap;
609	krb5_timestamp sec;
610	int32_t usec;
611
612	memset(&ap, 0, sizeof(ap));
613
614	/* fill in PKAuthenticator */
615	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
616	if (ret) {
617	    free_AuthPack_Win2k(&ap);
618	    krb5_clear_error_message(context);
619	    goto out;
620	}
621	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
622	if (ret) {
623	    free_AuthPack_Win2k(&ap);
624	    krb5_clear_error_message(context);
625	    goto out;
626	}
627
628	krb5_us_timeofday(context, &sec, &usec);
629	ap.pkAuthenticator.ctime = sec;
630	ap.pkAuthenticator.cusec = usec;
631	ap.pkAuthenticator.nonce = nonce;
632
633	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
634			   &ap, &size, ret);
635	free_AuthPack_Win2k(&ap);
636	if (ret) {
637	    krb5_set_error_message(context, ret,
638				   N_("Failed encoding AuthPackWin: %d", ""),
639				   (int)ret);
640	    goto out;
641	}
642	if (buf.length != size)
643	    krb5_abortx(context, "internal ASN1 encoder error");
644
645	oid = &asn1_oid_id_pkcs7_data;
646    } else if (ctx->type == PKINIT_27) {
647	AuthPack ap;
648
649	memset(&ap, 0, sizeof(ap));
650
651	ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
652	if (ret) {
653	    free_AuthPack(&ap);
654	    goto out;
655	}
656
657	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
658	free_AuthPack(&ap);
659	if (ret) {
660	    krb5_set_error_message(context, ret,
661				   N_("Failed encoding AuthPack: %d", ""),
662				   (int)ret);
663	    goto out;
664	}
665	if (buf.length != size)
666	    krb5_abortx(context, "internal ASN1 encoder error");
667
668	oid = &asn1_oid_id_pkauthdata;
669    } else
670	krb5_abortx(context, "internal pkinit error");
671
672    ret = create_signature(context, oid, &buf, ctx->id,
673			   ctx->peer, &sd_buf);
674    krb5_data_free(&buf);
675    if (ret)
676	goto out;
677
678    ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
679    krb5_data_free(&sd_buf);
680    if (ret) {
681	krb5_set_error_message(context, ret,
682			       N_("ContentInfo wrapping of signedData failed",""));
683	goto out;
684    }
685
686    if (ctx->type == PKINIT_WIN2K) {
687	PA_PK_AS_REQ_Win2k winreq;
688
689	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
690
691	memset(&winreq, 0, sizeof(winreq));
692
693	winreq.signed_auth_pack = buf;
694
695	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
696			   &winreq, &size, ret);
697	free_PA_PK_AS_REQ_Win2k(&winreq);
698
699    } else if (ctx->type == PKINIT_27) {
700	PA_PK_AS_REQ req;
701
702	pa_type = KRB5_PADATA_PK_AS_REQ;
703
704	memset(&req, 0, sizeof(req));
705	req.signedAuthPack = buf;
706
707	if (ctx->trustedCertifiers) {
708
709	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
710	    if (req.trustedCertifiers == NULL) {
711		ret = krb5_enomem(context);
712		free_PA_PK_AS_REQ(&req);
713		goto out;
714	    }
715	    ret = build_edi(context, context->hx509ctx,
716			    ctx->id->anchors, req.trustedCertifiers);
717	    if (ret) {
718		krb5_set_error_message(context, ret,
719				       N_("pk-init: failed to build "
720					  "trustedCertifiers", ""));
721		free_PA_PK_AS_REQ(&req);
722		goto out;
723	    }
724	}
725	req.kdcPkId = NULL;
726
727	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
728			   &req, &size, ret);
729
730	free_PA_PK_AS_REQ(&req);
731
732    } else
733	krb5_abortx(context, "internal pkinit error");
734    if (ret) {
735	krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
736	goto out;
737    }
738    if (buf.length != size)
739	krb5_abortx(context, "Internal ASN1 encoder error");
740
741    ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
742    if (ret)
743	free(buf.data);
744
745    if (ret == 0)
746    	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
747
748 out:
749    free_ContentInfo(&content_info);
750
751    return ret;
752}
753
754
755KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
756_krb5_pk_mk_padata(krb5_context context,
757		   void *c,
758		   int ic_flags,
759		   int win2k,
760		   const KDC_REQ_BODY *req_body,
761		   unsigned nonce,
762		   METHOD_DATA *md)
763{
764    krb5_pk_init_ctx ctx = c;
765    int win2k_compat;
766
767    if (ctx->id->certs == NULL && ctx->anonymous == 0) {
768	krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
769			       N_("PKINIT: No user certificate given", ""));
770	return HEIM_PKINIT_NO_PRIVATE_KEY;
771    }
772
773    win2k_compat = krb5_config_get_bool_default(context, NULL,
774						win2k,
775						"realms",
776						req_body->realm,
777						"pkinit_win2k",
778						NULL);
779
780    if (win2k_compat) {
781	ctx->require_binding =
782	    krb5_config_get_bool_default(context, NULL,
783					 TRUE,
784					 "realms",
785					 req_body->realm,
786					 "pkinit_win2k_require_binding",
787					 NULL);
788	ctx->type = PKINIT_WIN2K;
789    } else
790	ctx->type = PKINIT_27;
791
792    ctx->require_eku =
793	krb5_config_get_bool_default(context, NULL,
794				     TRUE,
795				     "realms",
796				     req_body->realm,
797				     "pkinit_require_eku",
798				     NULL);
799    if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
800	ctx->require_eku = 0;
801    if (ctx->id->flags & PKINIT_BTMM)
802	ctx->require_eku = 0;
803
804    ctx->require_krbtgt_otherName =
805	krb5_config_get_bool_default(context, NULL,
806				     TRUE,
807				     "realms",
808				     req_body->realm,
809				     "pkinit_require_krbtgt_otherName",
810				     NULL);
811    if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
812	ctx->require_krbtgt_otherName = FALSE;
813
814    ctx->require_hostname_match =
815	krb5_config_get_bool_default(context, NULL,
816				     FALSE,
817				     "realms",
818				     req_body->realm,
819				     "pkinit_require_hostname_match",
820				     NULL);
821
822    ctx->trustedCertifiers =
823	krb5_config_get_bool_default(context, NULL,
824				     TRUE,
825				     "realms",
826				     req_body->realm,
827				     "pkinit_trustedCertifiers",
828				     NULL);
829
830    return pk_mk_padata(context, ctx, req_body, nonce, md);
831}
832
833static krb5_error_code
834pk_verify_sign(krb5_context context,
835	       const void *data,
836	       size_t length,
837	       struct krb5_pk_identity *id,
838	       heim_oid *contentType,
839	       krb5_data *content,
840	       struct krb5_pk_cert **signer)
841{
842    hx509_certs signer_certs;
843    int ret, flags = 0;
844
845    /* BTMM is broken in Leo and SnowLeo */
846    if (id->flags & PKINIT_BTMM) {
847	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
848	flags |= HX509_CMS_VS_NO_KU_CHECK;
849	flags |= HX509_CMS_VS_NO_VALIDATE;
850    }
851
852    *signer = NULL;
853
854    ret = hx509_cms_verify_signed(context->hx509ctx,
855				  id->verify_ctx,
856				  flags,
857				  data,
858				  length,
859				  NULL,
860				  id->certpool,
861				  contentType,
862				  content,
863				  &signer_certs);
864    if (ret) {
865	pk_copy_error(context, context->hx509ctx, ret,
866		      "CMS verify signed failed");
867	return ret;
868    }
869
870    *signer = calloc(1, sizeof(**signer));
871    if (*signer == NULL) {
872	krb5_clear_error_message(context);
873	ret = ENOMEM;
874	goto out;
875    }
876
877    ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
878    if (ret) {
879	pk_copy_error(context, context->hx509ctx, ret,
880		      "Failed to get on of the signer certs");
881	goto out;
882    }
883
884 out:
885    hx509_certs_free(&signer_certs);
886    if (ret) {
887	if (*signer) {
888	    hx509_cert_free((*signer)->cert);
889	    free(*signer);
890	    *signer = NULL;
891	}
892    }
893
894    return ret;
895}
896
897static krb5_error_code
898get_reply_key_win(krb5_context context,
899		  const krb5_data *content,
900		  unsigned nonce,
901		  krb5_keyblock **key)
902{
903    ReplyKeyPack_Win2k key_pack;
904    krb5_error_code ret;
905    size_t size;
906
907    ret = decode_ReplyKeyPack_Win2k(content->data,
908				    content->length,
909				    &key_pack,
910				    &size);
911    if (ret) {
912	krb5_set_error_message(context, ret,
913			       N_("PKINIT decoding reply key failed", ""));
914	free_ReplyKeyPack_Win2k(&key_pack);
915	return ret;
916    }
917
918    if ((unsigned)key_pack.nonce != nonce) {
919	krb5_set_error_message(context, ret,
920			       N_("PKINIT enckey nonce is wrong", ""));
921	free_ReplyKeyPack_Win2k(&key_pack);
922	return KRB5KRB_AP_ERR_MODIFIED;
923    }
924
925    *key = malloc (sizeof (**key));
926    if (*key == NULL) {
927	free_ReplyKeyPack_Win2k(&key_pack);
928	return krb5_enomem(context);
929    }
930
931    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
932    free_ReplyKeyPack_Win2k(&key_pack);
933    if (ret) {
934	krb5_set_error_message(context, ret,
935			       N_("PKINIT failed copying reply key", ""));
936	free(*key);
937	*key = NULL;
938    }
939
940    return ret;
941}
942
943static krb5_error_code
944get_reply_key(krb5_context context,
945	      const krb5_data *content,
946	      const krb5_data *req_buffer,
947	      krb5_keyblock **key)
948{
949    ReplyKeyPack key_pack;
950    krb5_error_code ret;
951    size_t size;
952
953    ret = decode_ReplyKeyPack(content->data,
954			      content->length,
955			      &key_pack,
956			      &size);
957    if (ret) {
958	krb5_set_error_message(context, ret,
959			       N_("PKINIT decoding reply key failed", ""));
960	free_ReplyKeyPack(&key_pack);
961	return ret;
962    }
963
964    {
965	krb5_crypto crypto;
966
967	/*
968	 * XXX Verify kp.replyKey is a allowed enctype in the
969	 * configuration file
970	 */
971
972	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
973	if (ret) {
974	    free_ReplyKeyPack(&key_pack);
975	    return ret;
976	}
977
978	ret = krb5_verify_checksum(context, crypto, 6,
979				   req_buffer->data, req_buffer->length,
980				   &key_pack.asChecksum);
981	krb5_crypto_destroy(context, crypto);
982	if (ret) {
983	    free_ReplyKeyPack(&key_pack);
984	    return ret;
985	}
986    }
987
988    *key = malloc (sizeof (**key));
989    if (*key == NULL) {
990	free_ReplyKeyPack(&key_pack);
991	return krb5_enomem(context);
992    }
993
994    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
995    free_ReplyKeyPack(&key_pack);
996    if (ret) {
997	krb5_set_error_message(context, ret,
998			       N_("PKINIT failed copying reply key", ""));
999	free(*key);
1000	*key = NULL;
1001    }
1002
1003    return ret;
1004}
1005
1006
1007static krb5_error_code
1008pk_verify_host(krb5_context context,
1009	       const char *realm,
1010	       const krb5_krbhst_info *hi,
1011	       struct krb5_pk_init_ctx_data *ctx,
1012	       struct krb5_pk_cert *host)
1013{
1014    krb5_error_code ret = 0;
1015
1016    if (ctx->require_eku) {
1017	ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1018				   &asn1_oid_id_pkkdcekuoid, 0);
1019	if (ret) {
1020	    krb5_set_error_message(context, ret,
1021				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
1022	    return ret;
1023	}
1024    }
1025    if (ctx->require_krbtgt_otherName) {
1026	hx509_octet_string_list list;
1027	size_t i;
1028	int matched = 0;
1029
1030	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1031						       host->cert,
1032						       &asn1_oid_id_pkinit_san,
1033						       &list);
1034	if (ret) {
1035	    krb5_set_error_message(context, ret,
1036				   N_("Failed to find the PK-INIT "
1037				      "subjectAltName in the KDC "
1038				      "certificate", ""));
1039
1040	    return ret;
1041	}
1042
1043	/*
1044	 * subjectAltNames are multi-valued, and a single KDC may serve
1045	 * multiple realms. The SAN validation here must accept
1046	 * the KDC's cert if *any* of the SANs match the expected KDC.
1047	 * It is OK for *some* of the SANs to not match, provided at least
1048	 * one does.
1049	 */
1050	for (i = 0; matched == 0 && i < list.len; i++) {
1051	    KRB5PrincipalName r;
1052
1053	    ret = decode_KRB5PrincipalName(list.val[i].data,
1054					   list.val[i].length,
1055					   &r,
1056					   NULL);
1057	    if (ret) {
1058		krb5_set_error_message(context, ret,
1059				       N_("Failed to decode the PK-INIT "
1060					  "subjectAltName in the "
1061					  "KDC certificate", ""));
1062
1063		break;
1064	    }
1065
1066	    if (r.principalName.name_string.len == 2 &&
1067		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1068		&& strcmp(r.principalName.name_string.val[1], realm) == 0
1069		&& strcmp(r.realm, realm) == 0)
1070		matched = 1;
1071
1072	    free_KRB5PrincipalName(&r);
1073	}
1074	hx509_free_octet_string_list(&list);
1075	if (matched == 0) {
1076	    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1077	    /* XXX: Lost in translation... */
1078	    krb5_set_error_message(context, ret,
1079				   N_("KDC have wrong realm name in "
1080				      "the certificate", ""));
1081	}
1082    }
1083    if (ret)
1084	return ret;
1085
1086    if (hi) {
1087	ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1088				    ctx->require_hostname_match,
1089				    HX509_HN_HOSTNAME,
1090				    hi->hostname,
1091				    hi->ai->ai_addr, hi->ai->ai_addrlen);
1092
1093	if (ret)
1094	    krb5_set_error_message(context, ret,
1095				   N_("Address mismatch in "
1096				      "the KDC certificate", ""));
1097    }
1098    return ret;
1099}
1100
1101static krb5_error_code
1102pk_rd_pa_reply_enckey(krb5_context context,
1103		      int type,
1104		      const heim_octet_string *indata,
1105		      const heim_oid *dataType,
1106		      const char *realm,
1107		      krb5_pk_init_ctx ctx,
1108		      krb5_enctype etype,
1109		      const krb5_krbhst_info *hi,
1110	       	      unsigned nonce,
1111		      const krb5_data *req_buffer,
1112	       	      PA_DATA *pa,
1113	       	      krb5_keyblock **key)
1114{
1115    krb5_error_code ret;
1116    struct krb5_pk_cert *host = NULL;
1117    krb5_data content;
1118    heim_oid contentType = { 0, NULL };
1119    int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1120
1121    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1122	krb5_set_error_message(context, EINVAL,
1123			       N_("PKINIT: Invalid content type", ""));
1124	return EINVAL;
1125    }
1126
1127    if (ctx->type == PKINIT_WIN2K)
1128	flags |= HX509_CMS_UE_ALLOW_WEAK;
1129
1130    ret = hx509_cms_unenvelope(context->hx509ctx,
1131			       ctx->id->certs,
1132			       flags,
1133			       indata->data,
1134			       indata->length,
1135			       NULL,
1136			       0,
1137			       &contentType,
1138			       &content);
1139    if (ret) {
1140	pk_copy_error(context, context->hx509ctx, ret,
1141		      "Failed to unenvelope CMS data in PK-INIT reply");
1142	return ret;
1143    }
1144    der_free_oid(&contentType);
1145
1146    /* win2k uses ContentInfo */
1147    if (type == PKINIT_WIN2K) {
1148	heim_oid type2;
1149	heim_octet_string out;
1150
1151	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1152	if (ret) {
1153	    /* windows LH with interesting CMS packets */
1154	    size_t ph = 1 + der_length_len(content.length);
1155	    unsigned char *ptr = malloc(content.length + ph);
1156	    size_t l;
1157
1158	    memcpy(ptr + ph, content.data, content.length);
1159
1160	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1161					  ASN1_C_UNIV, CONS, UT_Sequence, &l);
1162	    if (ret) {
1163                free(ptr);
1164		return ret;
1165            }
1166	    free(content.data);
1167	    content.data = ptr;
1168	    content.length += ph;
1169
1170	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1171	    if (ret)
1172		goto out;
1173	}
1174	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1175	    ret = EINVAL; /* XXX */
1176	    krb5_set_error_message(context, ret,
1177				   N_("PKINIT: Invalid content type", ""));
1178	    der_free_oid(&type2);
1179	    der_free_octet_string(&out);
1180	    goto out;
1181	}
1182	der_free_oid(&type2);
1183	krb5_data_free(&content);
1184	ret = krb5_data_copy(&content, out.data, out.length);
1185	der_free_octet_string(&out);
1186	if (ret) {
1187	    krb5_set_error_message(context, ret,
1188				   N_("malloc: out of memory", ""));
1189	    goto out;
1190	}
1191    }
1192
1193    ret = pk_verify_sign(context,
1194			 content.data,
1195			 content.length,
1196			 ctx->id,
1197			 &contentType,
1198			 &content,
1199			 &host);
1200    if (ret)
1201	goto out;
1202
1203    /* make sure that it is the kdc's certificate */
1204    ret = pk_verify_host(context, realm, hi, ctx, host);
1205    if (ret) {
1206	goto out;
1207    }
1208
1209#if 0
1210    if (type == PKINIT_WIN2K) {
1211	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1212	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1213	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1214	    goto out;
1215	}
1216    } else {
1217	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1218	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1219	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1220	    goto out;
1221	}
1222    }
1223#endif
1224
1225    switch(type) {
1226    case PKINIT_WIN2K:
1227	ret = get_reply_key(context, &content, req_buffer, key);
1228	if (ret != 0 && ctx->require_binding == 0)
1229	    ret = get_reply_key_win(context, &content, nonce, key);
1230	break;
1231    case PKINIT_27:
1232	ret = get_reply_key(context, &content, req_buffer, key);
1233	break;
1234    }
1235    if (ret)
1236	goto out;
1237
1238    /* XXX compare given etype with key->etype */
1239
1240 out:
1241    if (host)
1242	_krb5_pk_cert_free(host);
1243    der_free_oid(&contentType);
1244    krb5_data_free(&content);
1245
1246    return ret;
1247}
1248
1249/*
1250 * RFC 8062 section 7:
1251 *
1252 *  The client then decrypts the KDC contribution key and verifies that
1253 *  the ticket session key in the returned ticket is the combined key of
1254 *  the KDC contribution key and the reply key.
1255 */
1256KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1257_krb5_pk_kx_confirm(krb5_context context,
1258		    krb5_pk_init_ctx ctx,
1259		    krb5_keyblock *reply_key,
1260		    krb5_keyblock *session_key,
1261		    PA_DATA *pa_pkinit_kx)
1262{
1263    krb5_error_code ret;
1264    EncryptedData ed;
1265    krb5_keyblock ck, sk_verify;
1266    krb5_crypto ck_crypto = NULL;
1267    krb5_crypto rk_crypto = NULL;
1268    size_t len;
1269    krb5_data data;
1270    krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
1271    krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
1272
1273    heim_assert(ctx != NULL, "PKINIT context is non-NULL");
1274    heim_assert(reply_key != NULL, "reply key is non-NULL");
1275    heim_assert(session_key != NULL, "session key is non-NULL");
1276
1277    /* PA-PKINIT-KX is optional unless anonymous */
1278    if (pa_pkinit_kx == NULL)
1279	return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
1280
1281    memset(&ed, 0, sizeof(ed));
1282    krb5_keyblock_zero(&ck);
1283    krb5_keyblock_zero(&sk_verify);
1284    krb5_data_zero(&data);
1285
1286    ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
1287			       pa_pkinit_kx->padata_value.length,
1288			       &ed, &len);
1289    if (ret)
1290	goto out;
1291
1292    if (len != pa_pkinit_kx->padata_value.length) {
1293	ret = KRB5_KDCREP_MODIFIED;
1294	goto out;
1295    }
1296
1297    ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
1298    if (ret)
1299	goto out;
1300
1301    ret = krb5_decrypt_EncryptedData(context, rk_crypto,
1302				     KRB5_KU_PA_PKINIT_KX,
1303				     &ed, &data);
1304    if (ret)
1305	goto out;
1306
1307    ret = decode_EncryptionKey(data.data, data.length,
1308			       &ck, &len);
1309    if (ret)
1310	goto out;
1311
1312    ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
1313    if (ret)
1314	goto out;
1315
1316    ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
1317			     &p1, &p2, session_key->keytype,
1318			     &sk_verify);
1319    if (ret)
1320	goto out;
1321
1322    if (sk_verify.keytype != session_key->keytype ||
1323	krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
1324	ret = KRB5_KDCREP_MODIFIED;
1325	goto out;
1326    }
1327
1328out:
1329    free_EncryptedData(&ed);
1330    krb5_free_keyblock_contents(context, &ck);
1331    krb5_free_keyblock_contents(context, &sk_verify);
1332    if (ck_crypto)
1333	krb5_crypto_destroy(context, ck_crypto);
1334    if (rk_crypto)
1335	krb5_crypto_destroy(context, rk_crypto);
1336    krb5_data_free(&data);
1337
1338    return ret;
1339}
1340
1341static krb5_error_code
1342pk_rd_pa_reply_dh(krb5_context context,
1343		  const heim_octet_string *indata,
1344		  const heim_oid *dataType,
1345		  const char *realm,
1346		  krb5_pk_init_ctx ctx,
1347		  krb5_enctype etype,
1348		  const krb5_krbhst_info *hi,
1349		  const DHNonce *c_n,
1350		  const DHNonce *k_n,
1351                  unsigned nonce,
1352                  PA_DATA *pa,
1353                  krb5_keyblock **key)
1354{
1355    const unsigned char *p;
1356    unsigned char *dh_gen_key = NULL;
1357    struct krb5_pk_cert *host = NULL;
1358    BIGNUM *kdc_dh_pubkey = NULL;
1359    KDCDHKeyInfo kdc_dh_info;
1360    heim_oid contentType = { 0, NULL };
1361    krb5_data content;
1362    krb5_error_code ret;
1363    int dh_gen_keylen = 0;
1364    size_t size;
1365
1366    krb5_data_zero(&content);
1367    memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1368
1369    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1370	krb5_set_error_message(context, EINVAL,
1371			       N_("PKINIT: Invalid content type", ""));
1372	return EINVAL;
1373    }
1374
1375    ret = pk_verify_sign(context,
1376			 indata->data,
1377			 indata->length,
1378			 ctx->id,
1379			 &contentType,
1380			 &content,
1381			 &host);
1382    if (ret)
1383	goto out;
1384
1385    /* make sure that it is the kdc's certificate */
1386    ret = pk_verify_host(context, realm, hi, ctx, host);
1387    if (ret)
1388	goto out;
1389
1390    if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1391	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1392	krb5_set_error_message(context, ret,
1393			       N_("pkinit - dh reply contains wrong oid", ""));
1394	goto out;
1395    }
1396
1397    ret = decode_KDCDHKeyInfo(content.data,
1398			      content.length,
1399			      &kdc_dh_info,
1400			      &size);
1401
1402    if (ret) {
1403	krb5_set_error_message(context, ret,
1404			       N_("pkinit - failed to decode "
1405				  "KDC DH Key Info", ""));
1406	goto out;
1407    }
1408
1409    if (kdc_dh_info.nonce != nonce) {
1410	ret = KRB5KRB_AP_ERR_MODIFIED;
1411	krb5_set_error_message(context, ret,
1412			       N_("PKINIT: DH nonce is wrong", ""));
1413	goto out;
1414    }
1415
1416    if (kdc_dh_info.dhKeyExpiration) {
1417	if (k_n == NULL) {
1418	    ret = KRB5KRB_ERR_GENERIC;
1419	    krb5_set_error_message(context, ret,
1420				   N_("pkinit; got key expiration "
1421				      "without server nonce", ""));
1422	    goto out;
1423	}
1424	if (c_n == NULL) {
1425	    ret = KRB5KRB_ERR_GENERIC;
1426	    krb5_set_error_message(context, ret,
1427				   N_("pkinit; got DH reuse but no "
1428				      "client nonce", ""));
1429	    goto out;
1430	}
1431    } else {
1432	if (k_n) {
1433	    ret = KRB5KRB_ERR_GENERIC;
1434	    krb5_set_error_message(context, ret,
1435				   N_("pkinit: got server nonce "
1436				      "without key expiration", ""));
1437	    goto out;
1438	}
1439	c_n = NULL;
1440    }
1441
1442
1443    p = kdc_dh_info.subjectPublicKey.data;
1444    size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1445
1446    if (ctx->keyex == USE_DH) {
1447	DHPublicKey k;
1448	ret = decode_DHPublicKey(p, size, &k, NULL);
1449	if (ret) {
1450	    krb5_set_error_message(context, ret,
1451				   N_("pkinit: can't decode "
1452				      "without key expiration", ""));
1453	    goto out;
1454	}
1455
1456	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1457	free_DHPublicKey(&k);
1458	if (kdc_dh_pubkey == NULL) {
1459	    ret = ENOMEM;
1460	    goto out;
1461	}
1462
1463
1464	size = DH_size(ctx->u.dh);
1465
1466	dh_gen_key = malloc(size);
1467	if (dh_gen_key == NULL) {
1468	    ret = krb5_enomem(context);
1469	    goto out;
1470	}
1471
1472	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1473	if (dh_gen_keylen == -1) {
1474	    ret = KRB5KRB_ERR_GENERIC;
1475	    dh_gen_keylen = 0;
1476	    krb5_set_error_message(context, ret,
1477				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1478	    goto out;
1479	}
1480	if (dh_gen_keylen < (int)size) {
1481	    size -= dh_gen_keylen;
1482	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1483	    memset(dh_gen_key, 0, size);
1484	}
1485
1486    } else {
1487        ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1488                                                    size, &dh_gen_key,
1489                                                    &dh_gen_keylen);
1490        if (ret)
1491          goto out;
1492    }
1493
1494    if (dh_gen_keylen <= 0) {
1495	ret = EINVAL;
1496	krb5_set_error_message(context, ret,
1497			       N_("PKINIT: resulting DH key <= 0", ""));
1498	dh_gen_keylen = 0;
1499	goto out;
1500    }
1501
1502    *key = malloc (sizeof (**key));
1503    if (*key == NULL) {
1504	ret = krb5_enomem(context);
1505	goto out;
1506    }
1507
1508    ret = _krb5_pk_octetstring2key(context,
1509				   etype,
1510				   dh_gen_key, dh_gen_keylen,
1511				   c_n, k_n,
1512				   *key);
1513    if (ret) {
1514	krb5_set_error_message(context, ret,
1515			       N_("PKINIT: can't create key from DH key", ""));
1516	free(*key);
1517	*key = NULL;
1518	goto out;
1519    }
1520
1521 out:
1522    if (kdc_dh_pubkey)
1523	BN_free(kdc_dh_pubkey);
1524    if (dh_gen_key) {
1525	memset(dh_gen_key, 0, dh_gen_keylen);
1526	free(dh_gen_key);
1527    }
1528    if (host)
1529	_krb5_pk_cert_free(host);
1530    if (content.data)
1531	krb5_data_free(&content);
1532    der_free_oid(&contentType);
1533    free_KDCDHKeyInfo(&kdc_dh_info);
1534
1535    return ret;
1536}
1537
1538KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1539_krb5_pk_rd_pa_reply(krb5_context context,
1540		     const char *realm,
1541		     void *c,
1542		     krb5_enctype etype,
1543		     const krb5_krbhst_info *hi,
1544		     unsigned nonce,
1545		     const krb5_data *req_buffer,
1546		     PA_DATA *pa,
1547		     krb5_keyblock **key)
1548{
1549    krb5_pk_init_ctx ctx = c;
1550    krb5_error_code ret;
1551    size_t size;
1552
1553    /* Check for IETF PK-INIT first */
1554    if (ctx->type == PKINIT_27) {
1555	PA_PK_AS_REP rep;
1556	heim_octet_string os, data;
1557	heim_oid oid;
1558
1559	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1560	    krb5_set_error_message(context, EINVAL,
1561				   N_("PKINIT: wrong padata recv", ""));
1562	    return EINVAL;
1563	}
1564
1565	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1566				  pa->padata_value.length,
1567				  &rep,
1568				  &size);
1569	if (ret) {
1570	    krb5_set_error_message(context, ret,
1571				   N_("Failed to decode pkinit AS rep", ""));
1572	    return ret;
1573	}
1574
1575	switch (rep.element) {
1576	case choice_PA_PK_AS_REP_dhInfo:
1577	    _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1578	    os = rep.u.dhInfo.dhSignedData;
1579	    break;
1580	case choice_PA_PK_AS_REP_encKeyPack:
1581	    _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1582	    os = rep.u.encKeyPack;
1583	    break;
1584	default: {
1585	    PA_PK_AS_REP_BTMM btmm;
1586	    free_PA_PK_AS_REP(&rep);
1587	    memset(&rep, 0, sizeof(rep));
1588
1589	    _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1590
1591	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1592					   pa->padata_value.length,
1593					   &btmm,
1594					   &size);
1595	    if (ret) {
1596		krb5_set_error_message(context, EINVAL,
1597				       N_("PKINIT: -27 reply "
1598					  "invalid content type", ""));
1599		return EINVAL;
1600	    }
1601
1602	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1603		free_PA_PK_AS_REP_BTMM(&btmm);
1604		ret = EINVAL;
1605		krb5_set_error_message(context, ret,
1606				       N_("DH mode not supported for BTMM mode", ""));
1607		return ret;
1608	    }
1609
1610	    /*
1611	     * Transform to IETF style PK-INIT reply so that free works below
1612	     */
1613
1614	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1615	    rep.u.encKeyPack.data = btmm.encKeyPack->data;
1616	    rep.u.encKeyPack.length = btmm.encKeyPack->length;
1617	    btmm.encKeyPack->data = NULL;
1618	    btmm.encKeyPack->length = 0;
1619	    free_PA_PK_AS_REP_BTMM(&btmm);
1620	    os = rep.u.encKeyPack;
1621	}
1622	}
1623
1624	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1625	if (ret) {
1626	    free_PA_PK_AS_REP(&rep);
1627	    krb5_set_error_message(context, ret,
1628				   N_("PKINIT: failed to unwrap CI", ""));
1629	    return ret;
1630	}
1631
1632	switch (rep.element) {
1633	case choice_PA_PK_AS_REP_dhInfo:
1634	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1635				    ctx->clientDHNonce,
1636				    rep.u.dhInfo.serverDHNonce,
1637				    nonce, pa, key);
1638	    break;
1639	case choice_PA_PK_AS_REP_encKeyPack:
1640	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1641					ctx, etype, hi, nonce, req_buffer, pa, key);
1642	    break;
1643	default:
1644	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1645	}
1646	der_free_octet_string(&data);
1647	der_free_oid(&oid);
1648	free_PA_PK_AS_REP(&rep);
1649
1650    } else if (ctx->type == PKINIT_WIN2K) {
1651	PA_PK_AS_REP_Win2k w2krep;
1652
1653	/* Check for Windows encoding of the AS-REP pa data */
1654
1655#if 0 /* should this be ? */
1656	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1657	    krb5_set_error_message(context, EINVAL,
1658				   "PKINIT: wrong padata recv");
1659	    return EINVAL;
1660	}
1661#endif
1662
1663	memset(&w2krep, 0, sizeof(w2krep));
1664
1665	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1666					pa->padata_value.length,
1667					&w2krep,
1668					&size);
1669	if (ret) {
1670	    krb5_set_error_message(context, ret,
1671				   N_("PKINIT: Failed decoding windows "
1672				      "pkinit reply %d", ""), (int)ret);
1673	    return ret;
1674	}
1675
1676	krb5_clear_error_message(context);
1677
1678	switch (w2krep.element) {
1679	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1680	    heim_octet_string data;
1681	    heim_oid oid;
1682
1683	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1684					       &oid, &data, NULL);
1685	    free_PA_PK_AS_REP_Win2k(&w2krep);
1686	    if (ret) {
1687		krb5_set_error_message(context, ret,
1688				       N_("PKINIT: failed to unwrap CI", ""));
1689		return ret;
1690	    }
1691
1692	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1693					ctx, etype, hi, nonce, req_buffer, pa, key);
1694	    der_free_octet_string(&data);
1695	    der_free_oid(&oid);
1696
1697	    break;
1698	}
1699	default:
1700	    free_PA_PK_AS_REP_Win2k(&w2krep);
1701	    ret = EINVAL;
1702	    krb5_set_error_message(context, ret,
1703				   N_("PKINIT: win2k reply invalid "
1704				      "content type", ""));
1705	    break;
1706	}
1707
1708    } else {
1709	ret = EINVAL;
1710	krb5_set_error_message(context, ret,
1711			       N_("PKINIT: unknown reply type", ""));
1712    }
1713
1714    return ret;
1715}
1716
1717struct prompter {
1718    krb5_context context;
1719    krb5_prompter_fct prompter;
1720    void *prompter_data;
1721};
1722
1723static int
1724hx_pass_prompter(void *data, const hx509_prompt *prompter)
1725{
1726    krb5_error_code ret;
1727    krb5_prompt prompt;
1728    krb5_data password_data;
1729    struct prompter *p = data;
1730
1731    password_data.data   = prompter->reply.data;
1732    password_data.length = prompter->reply.length;
1733
1734    prompt.prompt = prompter->prompt;
1735    prompt.hidden = hx509_prompt_hidden(prompter->type);
1736    prompt.reply  = &password_data;
1737
1738    switch (prompter->type) {
1739    case HX509_PROMPT_TYPE_INFO:
1740	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1741	break;
1742    case HX509_PROMPT_TYPE_PASSWORD:
1743    case HX509_PROMPT_TYPE_QUESTION:
1744    default:
1745	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1746	break;
1747    }
1748
1749    ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1750    if (ret) {
1751	memset (prompter->reply.data, 0, prompter->reply.length);
1752	return 1;
1753    }
1754    return 0;
1755}
1756
1757static krb5_error_code
1758_krb5_pk_set_user_id(krb5_context context,
1759		     krb5_principal principal,
1760		     krb5_pk_init_ctx ctx,
1761		     struct hx509_certs_data *certs)
1762{
1763    hx509_certs c = hx509_certs_ref(certs);
1764    hx509_query *q = NULL;
1765    int ret;
1766
1767    if (ctx->id->certs)
1768	hx509_certs_free(&ctx->id->certs);
1769    if (ctx->id->cert) {
1770	hx509_cert_free(ctx->id->cert);
1771	ctx->id->cert = NULL;
1772    }
1773
1774    ctx->id->certs = c;
1775    ctx->anonymous = 0;
1776
1777    ret = hx509_query_alloc(context->hx509ctx, &q);
1778    if (ret) {
1779	pk_copy_error(context, context->hx509ctx, ret,
1780		      "Allocate query to find signing certificate");
1781	return ret;
1782    }
1783
1784    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1785    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1786
1787    if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1788	ctx->id->flags |= PKINIT_BTMM;
1789    }
1790
1791    ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1792    hx509_query_free(context->hx509ctx, q);
1793
1794    if (ret == 0 && _krb5_have_debug(context, 2)) {
1795	hx509_name name;
1796	char *str, *sn;
1797	heim_integer i;
1798
1799	ret = hx509_cert_get_subject(ctx->id->cert, &name);
1800	if (ret)
1801	    goto out;
1802
1803	ret = hx509_name_to_string(name, &str);
1804	hx509_name_free(&name);
1805	if (ret)
1806	    goto out;
1807
1808	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1809	if (ret) {
1810	    free(str);
1811	    goto out;
1812	}
1813
1814	ret = der_print_hex_heim_integer(&i, &sn);
1815	der_free_heim_integer(&i);
1816	if (ret) {
1817	    free(name);
1818	    goto out;
1819	}
1820
1821	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1822	free(str);
1823	free(sn);
1824    }
1825 out:
1826
1827    return ret;
1828}
1829
1830KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1831_krb5_pk_load_id(krb5_context context,
1832		 struct krb5_pk_identity **ret_id,
1833		 const char *user_id,
1834		 const char *anchor_id,
1835		 char * const *chain_list,
1836		 char * const *revoke_list,
1837		 krb5_prompter_fct prompter,
1838		 void *prompter_data,
1839		 char *password)
1840{
1841    struct krb5_pk_identity *id = NULL;
1842    struct prompter p;
1843    int ret;
1844
1845    *ret_id = NULL;
1846
1847    if (anchor_id == NULL) {
1848	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1849			       N_("PKINIT: No anchor given", ""));
1850	return HEIM_PKINIT_NO_VALID_CA;
1851    }
1852
1853    /* load cert */
1854
1855    id = calloc(1, sizeof(*id));
1856    if (id == NULL)
1857	return krb5_enomem(context);
1858
1859    if (user_id) {
1860	hx509_lock lock;
1861
1862	ret = hx509_lock_init(context->hx509ctx, &lock);
1863	if (ret) {
1864	    pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1865	    goto out;
1866	}
1867
1868	if (password && password[0])
1869	    hx509_lock_add_password(lock, password);
1870
1871	if (prompter) {
1872	    p.context = context;
1873	    p.prompter = prompter;
1874	    p.prompter_data = prompter_data;
1875
1876	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1877	    if (ret) {
1878		hx509_lock_free(lock);
1879		goto out;
1880	    }
1881	}
1882
1883	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1884        hx509_lock_free(lock);
1885	if (ret) {
1886	    pk_copy_error(context, context->hx509ctx, ret,
1887			  "Failed to init cert certs");
1888	    goto out;
1889	}
1890    } else {
1891	id->certs = NULL;
1892    }
1893
1894    ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1895    if (ret) {
1896	pk_copy_error(context, context->hx509ctx, ret,
1897		      "Failed to init anchors");
1898	goto out;
1899    }
1900
1901    ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1902			   0, NULL, &id->certpool);
1903    if (ret) {
1904	pk_copy_error(context, context->hx509ctx, ret,
1905		      "Failed to init chain");
1906	goto out;
1907    }
1908
1909    while (chain_list && *chain_list) {
1910	ret = hx509_certs_append(context->hx509ctx, id->certpool,
1911				 NULL, *chain_list);
1912	if (ret) {
1913	    pk_copy_error(context, context->hx509ctx, ret,
1914			  "Failed to laod chain %s",
1915			  *chain_list);
1916	    goto out;
1917	}
1918	chain_list++;
1919    }
1920
1921    if (revoke_list) {
1922	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1923	if (ret) {
1924	    pk_copy_error(context, context->hx509ctx, ret,
1925			  "Failed init revoke list");
1926	    goto out;
1927	}
1928
1929	while (*revoke_list) {
1930	    ret = hx509_revoke_add_crl(context->hx509ctx,
1931				       id->revokectx,
1932				       *revoke_list);
1933	    if (ret) {
1934		pk_copy_error(context, context->hx509ctx, ret,
1935			      "Failed load revoke list");
1936		goto out;
1937	    }
1938	    revoke_list++;
1939	}
1940    } else
1941	hx509_context_set_missing_revoke(context->hx509ctx, 1);
1942
1943    ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1944    if (ret) {
1945	pk_copy_error(context, context->hx509ctx, ret,
1946		      "Failed init verify context");
1947	goto out;
1948    }
1949
1950    hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1951    hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1952
1953 out:
1954    if (ret) {
1955	hx509_verify_destroy_ctx(id->verify_ctx);
1956	hx509_certs_free(&id->certs);
1957	hx509_certs_free(&id->anchors);
1958	hx509_certs_free(&id->certpool);
1959	hx509_revoke_free(&id->revokectx);
1960	free(id);
1961    } else
1962	*ret_id = id;
1963
1964    return ret;
1965}
1966
1967/*
1968 *
1969 */
1970
1971static void
1972pk_copy_error(krb5_context context,
1973	      hx509_context hx509ctx,
1974	      int hxret,
1975	      const char *fmt,
1976	      ...)
1977{
1978    va_list va;
1979    char *s, *f;
1980    int ret;
1981
1982    va_start(va, fmt);
1983    ret = vasprintf(&f, fmt, va);
1984    va_end(va);
1985    if (ret == -1 || f == NULL) {
1986	krb5_clear_error_message(context);
1987	return;
1988    }
1989
1990    s = hx509_get_error_string(hx509ctx, hxret);
1991    if (s == NULL) {
1992	krb5_clear_error_message(context);
1993	free(f);
1994	return;
1995    }
1996    krb5_set_error_message(context, hxret, "%s: %s", f, s);
1997    free(s);
1998    free(f);
1999}
2000
2001static int
2002parse_integer(krb5_context context, char **p, const char *file, int lineno,
2003	      const char *name, heim_integer *integer)
2004{
2005    int ret;
2006    char *p1;
2007    p1 = strsep(p, " \t");
2008    if (p1 == NULL) {
2009	krb5_set_error_message(context, EINVAL,
2010			       N_("moduli file %s missing %s on line %d", ""),
2011			       file, name, lineno);
2012	return EINVAL;
2013    }
2014    ret = der_parse_hex_heim_integer(p1, integer);
2015    if (ret) {
2016	krb5_set_error_message(context, ret,
2017			       N_("moduli file %s failed parsing %s "
2018				  "on line %d", ""),
2019			       file, name, lineno);
2020	return ret;
2021    }
2022
2023    return 0;
2024}
2025
2026KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2027_krb5_parse_moduli_line(krb5_context context,
2028			const char *file,
2029			int lineno,
2030			char *p,
2031			struct krb5_dh_moduli **m)
2032{
2033    struct krb5_dh_moduli *m1;
2034    char *p1;
2035    int ret;
2036
2037    *m = NULL;
2038
2039    m1 = calloc(1, sizeof(*m1));
2040    if (m1 == NULL)
2041	return krb5_enomem(context);
2042
2043    while (isspace((unsigned char)*p))
2044	p++;
2045    if (*p  == '#') {
2046        free(m1);
2047	return 0;
2048    }
2049    ret = EINVAL;
2050
2051    p1 = strsep(&p, " \t");
2052    if (p1 == NULL) {
2053	krb5_set_error_message(context, ret,
2054			       N_("moduli file %s missing name on line %d", ""),
2055			       file, lineno);
2056	goto out;
2057    }
2058    m1->name = strdup(p1);
2059    if (m1->name == NULL) {
2060	ret = krb5_enomem(context);
2061	goto out;
2062    }
2063
2064    p1 = strsep(&p, " \t");
2065    if (p1 == NULL) {
2066	krb5_set_error_message(context, ret,
2067			       N_("moduli file %s missing bits on line %d", ""),
2068			       file, lineno);
2069	goto out;
2070    }
2071
2072    m1->bits = atoi(p1);
2073    if (m1->bits == 0) {
2074	krb5_set_error_message(context, ret,
2075			       N_("moduli file %s have un-parsable "
2076				  "bits on line %d", ""), file, lineno);
2077	goto out;
2078    }
2079
2080    ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2081    if (ret)
2082	goto out;
2083    ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2084    if (ret)
2085	goto out;
2086    ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2087    if (ret)
2088	goto out;
2089
2090    *m = m1;
2091
2092    return 0;
2093 out:
2094    free(m1->name);
2095    der_free_heim_integer(&m1->p);
2096    der_free_heim_integer(&m1->g);
2097    der_free_heim_integer(&m1->q);
2098    free(m1);
2099    return ret;
2100}
2101
2102KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2103_krb5_free_moduli(struct krb5_dh_moduli **moduli)
2104{
2105    int i;
2106    for (i = 0; moduli[i] != NULL; i++) {
2107	free(moduli[i]->name);
2108	der_free_heim_integer(&moduli[i]->p);
2109	der_free_heim_integer(&moduli[i]->g);
2110	der_free_heim_integer(&moduli[i]->q);
2111	free(moduli[i]);
2112    }
2113    free(moduli);
2114}
2115
2116static const char *default_moduli_RFC2412_MODP_group2 =
2117    /* name */
2118    "RFC2412-MODP-group2 "
2119    /* bits */
2120    "1024 "
2121    /* p */
2122    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2123    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2124    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2125    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2126    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2127    "FFFFFFFF" "FFFFFFFF "
2128    /* g */
2129    "02 "
2130    /* q */
2131    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2132    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2133    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2134    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2135    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2136    "FFFFFFFF" "FFFFFFFF";
2137
2138static const char *default_moduli_rfc3526_MODP_group14 =
2139    /* name */
2140    "rfc3526-MODP-group14 "
2141    /* bits */
2142    "1760 "
2143    /* p */
2144    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2145    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2146    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2147    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2148    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2149    "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2150    "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2151    "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2152    "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2153    "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2154    "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2155    /* g */
2156    "02 "
2157    /* q */
2158    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2159    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2160    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2161    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2162    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2163    "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2164    "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2165    "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2166    "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2167    "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2168    "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2169
2170KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2171_krb5_parse_moduli(krb5_context context, const char *file,
2172		   struct krb5_dh_moduli ***moduli)
2173{
2174    /* name bits P G Q */
2175    krb5_error_code ret;
2176    struct krb5_dh_moduli **m = NULL, **m2;
2177    char buf[4096];
2178    FILE *f;
2179    int lineno = 0, n = 0;
2180
2181    *moduli = NULL;
2182
2183    m = calloc(1, sizeof(m[0]) * 3);
2184    if (m == NULL)
2185	return krb5_enomem(context);
2186
2187    strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2188    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2189    if (ret) {
2190	_krb5_free_moduli(m);
2191	return ret;
2192    }
2193    n++;
2194
2195    strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2196    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2197    if (ret) {
2198	_krb5_free_moduli(m);
2199	return ret;
2200    }
2201    n++;
2202
2203
2204    if (file == NULL)
2205	file = MODULI_FILE;
2206
2207#ifdef KRB5_USE_PATH_TOKENS
2208    {
2209        char * exp_file;
2210
2211	if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2212            f = fopen(exp_file, "r");
2213            krb5_xfree(exp_file);
2214        } else {
2215            f = NULL;
2216        }
2217    }
2218#else
2219    f = fopen(file, "r");
2220#endif
2221
2222    if (f == NULL) {
2223	*moduli = m;
2224	return 0;
2225    }
2226    rk_cloexec_file(f);
2227
2228    while(fgets(buf, sizeof(buf), f) != NULL) {
2229	struct krb5_dh_moduli *element;
2230
2231	buf[strcspn(buf, "\n")] = '\0';
2232	lineno++;
2233
2234	m2 = realloc(m, (n + 2) * sizeof(m[0]));
2235	if (m2 == NULL) {
2236	    _krb5_free_moduli(m);
2237	    return krb5_enomem(context);
2238	}
2239	m = m2;
2240
2241	m[n] = NULL;
2242
2243	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2244	if (ret) {
2245	    _krb5_free_moduli(m);
2246	    return ret;
2247	}
2248	if (element == NULL)
2249	    continue;
2250
2251	m[n] = element;
2252	m[n + 1] = NULL;
2253	n++;
2254    }
2255    *moduli = m;
2256    return 0;
2257}
2258
2259KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2260_krb5_dh_group_ok(krb5_context context, unsigned long bits,
2261		  heim_integer *p, heim_integer *g, heim_integer *q,
2262		  struct krb5_dh_moduli **moduli,
2263		  char **name)
2264{
2265    int i;
2266
2267    if (name)
2268	*name = NULL;
2269
2270    for (i = 0; moduli[i] != NULL; i++) {
2271	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2272	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2273	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2274	    {
2275		if (bits && bits > moduli[i]->bits) {
2276		    krb5_set_error_message(context,
2277					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2278					   N_("PKINIT: DH group parameter %s "
2279					      "no accepted, not enough bits "
2280					      "generated", ""),
2281					   moduli[i]->name);
2282		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2283		}
2284		if (name)
2285		    *name = strdup(moduli[i]->name);
2286		return 0;
2287	    }
2288    }
2289    krb5_set_error_message(context,
2290			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2291			   N_("PKINIT: DH group parameter no ok", ""));
2292    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2293}
2294#endif /* PKINIT */
2295
2296KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2297_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2298{
2299#ifdef PKINIT
2300    krb5_pk_init_ctx ctx;
2301
2302    if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2303	return;
2304    ctx = opt->opt_private->pk_init_ctx;
2305    switch (ctx->keyex) {
2306    case USE_DH:
2307	if (ctx->u.dh)
2308	    DH_free(ctx->u.dh);
2309	break;
2310    case USE_RSA:
2311	break;
2312    case USE_ECDH:
2313	if (ctx->u.eckey)
2314            _krb5_pk_eckey_free(ctx->u.eckey);
2315	break;
2316    }
2317    if (ctx->id) {
2318	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2319	hx509_certs_free(&ctx->id->certs);
2320	hx509_cert_free(ctx->id->cert);
2321	hx509_certs_free(&ctx->id->anchors);
2322	hx509_certs_free(&ctx->id->certpool);
2323
2324	if (ctx->clientDHNonce) {
2325	    krb5_free_data(NULL, ctx->clientDHNonce);
2326	    ctx->clientDHNonce = NULL;
2327	}
2328	if (ctx->m)
2329	    _krb5_free_moduli(ctx->m);
2330	free(ctx->id);
2331	ctx->id = NULL;
2332    }
2333    free(opt->opt_private->pk_init_ctx);
2334    opt->opt_private->pk_init_ctx = NULL;
2335#endif
2336}
2337
2338KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2339krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2340				   krb5_get_init_creds_opt *opt,
2341				   krb5_principal principal,
2342				   const char *user_id,
2343				   const char *x509_anchors,
2344				   char * const * pool,
2345				   char * const * pki_revoke,
2346				   int flags,
2347				   krb5_prompter_fct prompter,
2348				   void *prompter_data,
2349				   char *password)
2350{
2351#ifdef PKINIT
2352    krb5_error_code ret;
2353    char *anchors = NULL;
2354
2355    if (opt->opt_private == NULL) {
2356	krb5_set_error_message(context, EINVAL,
2357			       N_("PKINIT: on non extendable opt", ""));
2358	return EINVAL;
2359    }
2360
2361    opt->opt_private->pk_init_ctx =
2362	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2363    if (opt->opt_private->pk_init_ctx == NULL)
2364	return krb5_enomem(context);
2365    opt->opt_private->pk_init_ctx->require_binding = 0;
2366    opt->opt_private->pk_init_ctx->require_eku = 1;
2367    opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2368    opt->opt_private->pk_init_ctx->peer = NULL;
2369
2370    /* XXX implement krb5_appdefault_strings  */
2371    if (pool == NULL)
2372	pool = krb5_config_get_strings(context, NULL,
2373				       "appdefaults",
2374				       "pkinit_pool",
2375				       NULL);
2376
2377    if (pki_revoke == NULL)
2378	pki_revoke = krb5_config_get_strings(context, NULL,
2379					     "appdefaults",
2380					     "pkinit_revoke",
2381					     NULL);
2382
2383    if (x509_anchors == NULL) {
2384	krb5_appdefault_string(context, "kinit",
2385			       krb5_principal_get_realm(context, principal),
2386			       "pkinit_anchors", NULL, &anchors);
2387	x509_anchors = anchors;
2388    }
2389
2390    if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
2391	opt->opt_private->pk_init_ctx->anonymous = 1;
2392
2393    ret = _krb5_pk_load_id(context,
2394			   &opt->opt_private->pk_init_ctx->id,
2395			   user_id,
2396			   x509_anchors,
2397			   pool,
2398			   pki_revoke,
2399			   prompter,
2400			   prompter_data,
2401			   password);
2402    if (ret) {
2403	free(opt->opt_private->pk_init_ctx);
2404	opt->opt_private->pk_init_ctx = NULL;
2405	return ret;
2406    }
2407    if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
2408	opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2409
2410    if (principal && krb5_principal_is_lkdc(context, principal))
2411	opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
2412
2413    if (opt->opt_private->pk_init_ctx->id->certs) {
2414	_krb5_pk_set_user_id(context,
2415			     principal,
2416			     opt->opt_private->pk_init_ctx,
2417			     opt->opt_private->pk_init_ctx->id->certs);
2418    } else
2419	opt->opt_private->pk_init_ctx->id->cert = NULL;
2420
2421    if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
2422	hx509_context hx509ctx = context->hx509ctx;
2423	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2424
2425	opt->opt_private->pk_init_ctx->keyex = USE_DH;
2426
2427	/*
2428	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2429	 */
2430	if (cert) {
2431	    AlgorithmIdentifier alg;
2432
2433	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2434	    if (ret == 0) {
2435		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2436		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2437		free_AlgorithmIdentifier(&alg);
2438	    }
2439	}
2440
2441    } else {
2442	opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2443
2444	if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2445	    krb5_set_error_message(context, EINVAL,
2446				   N_("No anonymous pkinit support in RSA mode", ""));
2447	    return EINVAL;
2448	}
2449    }
2450
2451    return 0;
2452#else
2453    krb5_set_error_message(context, EINVAL,
2454			   N_("no support for PKINIT compiled in", ""));
2455    return EINVAL;
2456#endif
2457}
2458
2459krb5_error_code KRB5_LIB_FUNCTION
2460krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2461					      krb5_get_init_creds_opt *opt,
2462					      struct hx509_certs_data *certs)
2463{
2464#ifdef PKINIT
2465    if (opt->opt_private == NULL) {
2466	krb5_set_error_message(context, EINVAL,
2467			       N_("PKINIT: on non extendable opt", ""));
2468	return EINVAL;
2469    }
2470    if (opt->opt_private->pk_init_ctx == NULL) {
2471	krb5_set_error_message(context, EINVAL,
2472			       N_("PKINIT: on pkinit context", ""));
2473	return EINVAL;
2474    }
2475
2476    _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2477
2478    return 0;
2479#else
2480    krb5_set_error_message(context, EINVAL,
2481			   N_("no support for PKINIT compiled in", ""));
2482    return EINVAL;
2483#endif
2484}
2485
2486#ifdef PKINIT
2487
2488static int
2489get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2490{
2491    hx509_octet_string_list list;
2492    int ret;
2493
2494    *upn = NULL;
2495
2496    ret = hx509_cert_find_subjectAltName_otherName(context,
2497						   cert,
2498						   &asn1_oid_id_pkinit_ms_san,
2499						   &list);
2500    if (ret)
2501	return 0;
2502
2503    if (list.len > 0 && list.val[0].length > 0)
2504	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2505				upn, NULL);
2506    else
2507	ret = 1;
2508    hx509_free_octet_string_list(&list);
2509
2510    return ret;
2511}
2512
2513static int
2514find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2515{
2516    char *upn;
2517    int ret;
2518
2519    ret = get_ms_san(context, cert, &upn);
2520    if (ret == 0)
2521	free(upn);
2522    return ret;
2523}
2524
2525
2526
2527#endif
2528
2529/*
2530 * Private since it need to be redesigned using krb5_get_init_creds()
2531 */
2532
2533KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2534krb5_pk_enterprise_cert(krb5_context context,
2535			const char *user_id,
2536			krb5_const_realm realm,
2537			krb5_principal *principal,
2538			struct hx509_certs_data **res)
2539{
2540#ifdef PKINIT
2541    krb5_error_code ret;
2542    hx509_certs certs, result;
2543    hx509_cert cert = NULL;
2544    hx509_query *q;
2545    char *name;
2546
2547    *principal = NULL;
2548    if (res)
2549	*res = NULL;
2550
2551    if (user_id == NULL) {
2552	krb5_set_error_message(context, ENOENT, "no user id");
2553	return ENOENT;
2554    }
2555
2556    ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2557    if (ret) {
2558	pk_copy_error(context, context->hx509ctx, ret,
2559		      "Failed to init cert certs");
2560	goto out;
2561    }
2562
2563    ret = hx509_query_alloc(context->hx509ctx, &q);
2564    if (ret) {
2565	krb5_set_error_message(context, ret, "out of memory");
2566	hx509_certs_free(&certs);
2567	goto out;
2568    }
2569
2570    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2571    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2572    hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2573    hx509_query_match_cmp_func(q, find_ms_san, NULL);
2574
2575    ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2576    hx509_query_free(context->hx509ctx, q);
2577    hx509_certs_free(&certs);
2578    if (ret) {
2579	pk_copy_error(context, context->hx509ctx, ret,
2580		      "Failed to find PKINIT certificate");
2581	return ret;
2582    }
2583
2584    ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2585    hx509_certs_free(&result);
2586    if (ret) {
2587	pk_copy_error(context, context->hx509ctx, ret,
2588		      "Failed to get one cert");
2589	goto out;
2590    }
2591
2592    ret = get_ms_san(context->hx509ctx, cert, &name);
2593    if (ret) {
2594	pk_copy_error(context, context->hx509ctx, ret,
2595		      "Failed to get MS SAN");
2596	goto out;
2597    }
2598
2599    ret = krb5_make_principal(context, principal, realm, name, NULL);
2600    free(name);
2601    if (ret)
2602	goto out;
2603
2604    krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2605
2606    if (res) {
2607	ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2608	if (ret)
2609	    goto out;
2610
2611	ret = hx509_certs_add(context->hx509ctx, *res, cert);
2612	if (ret) {
2613	    hx509_certs_free(res);
2614	    goto out;
2615	}
2616    }
2617
2618 out:
2619    hx509_cert_free(cert);
2620
2621    return ret;
2622#else
2623    krb5_set_error_message(context, EINVAL,
2624			   N_("no support for PKINIT compiled in", ""));
2625    return EINVAL;
2626#endif
2627}
2628