pkinit.c revision 1.3
1/*	$NetBSD: pkinit.c,v 1.3 2018/02/05 16:00:53 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, 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, __UNCONST(p), &dp.p);
485	    if (ret) {
486		free_DomainParameters(&dp);
487		return ret;
488	    }
489	    ret = BN_to_integer(context, __UNCONST(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, __UNCONST(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, __UNCONST(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
812    ctx->require_hostname_match =
813	krb5_config_get_bool_default(context, NULL,
814				     FALSE,
815				     "realms",
816				     req_body->realm,
817				     "pkinit_require_hostname_match",
818				     NULL);
819
820    ctx->trustedCertifiers =
821	krb5_config_get_bool_default(context, NULL,
822				     TRUE,
823				     "realms",
824				     req_body->realm,
825				     "pkinit_trustedCertifiers",
826				     NULL);
827
828    return pk_mk_padata(context, ctx, req_body, nonce, md);
829}
830
831static krb5_error_code
832pk_verify_sign(krb5_context context,
833	       const void *data,
834	       size_t length,
835	       struct krb5_pk_identity *id,
836	       heim_oid *contentType,
837	       krb5_data *content,
838	       struct krb5_pk_cert **signer)
839{
840    hx509_certs signer_certs;
841    int ret, flags = 0;
842
843    /* BTMM is broken in Leo and SnowLeo */
844    if (id->flags & PKINIT_BTMM) {
845	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
846	flags |= HX509_CMS_VS_NO_KU_CHECK;
847	flags |= HX509_CMS_VS_NO_VALIDATE;
848    }
849
850    *signer = NULL;
851
852    ret = hx509_cms_verify_signed(context->hx509ctx,
853				  id->verify_ctx,
854				  flags,
855				  data,
856				  length,
857				  NULL,
858				  id->certpool,
859				  contentType,
860				  content,
861				  &signer_certs);
862    if (ret) {
863	pk_copy_error(context, context->hx509ctx, ret,
864		      "CMS verify signed failed");
865	return ret;
866    }
867
868    *signer = calloc(1, sizeof(**signer));
869    if (*signer == NULL) {
870	krb5_clear_error_message(context);
871	ret = ENOMEM;
872	goto out;
873    }
874
875    ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
876    if (ret) {
877	pk_copy_error(context, context->hx509ctx, ret,
878		      "Failed to get on of the signer certs");
879	goto out;
880    }
881
882 out:
883    hx509_certs_free(&signer_certs);
884    if (ret) {
885	if (*signer) {
886	    hx509_cert_free((*signer)->cert);
887	    free(*signer);
888	    *signer = NULL;
889	}
890    }
891
892    return ret;
893}
894
895static krb5_error_code
896get_reply_key_win(krb5_context context,
897		  const krb5_data *content,
898		  unsigned nonce,
899		  krb5_keyblock **key)
900{
901    ReplyKeyPack_Win2k key_pack;
902    krb5_error_code ret;
903    size_t size;
904
905    ret = decode_ReplyKeyPack_Win2k(content->data,
906				    content->length,
907				    &key_pack,
908				    &size);
909    if (ret) {
910	krb5_set_error_message(context, ret,
911			       N_("PKINIT decoding reply key failed", ""));
912	free_ReplyKeyPack_Win2k(&key_pack);
913	return ret;
914    }
915
916    if ((unsigned)key_pack.nonce != nonce) {
917	krb5_set_error_message(context, ret,
918			       N_("PKINIT enckey nonce is wrong", ""));
919	free_ReplyKeyPack_Win2k(&key_pack);
920	return KRB5KRB_AP_ERR_MODIFIED;
921    }
922
923    *key = malloc (sizeof (**key));
924    if (*key == NULL) {
925	free_ReplyKeyPack_Win2k(&key_pack);
926	return krb5_enomem(context);
927    }
928
929    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
930    free_ReplyKeyPack_Win2k(&key_pack);
931    if (ret) {
932	krb5_set_error_message(context, ret,
933			       N_("PKINIT failed copying reply key", ""));
934	free(*key);
935	*key = NULL;
936    }
937
938    return ret;
939}
940
941static krb5_error_code
942get_reply_key(krb5_context context,
943	      const krb5_data *content,
944	      const krb5_data *req_buffer,
945	      krb5_keyblock **key)
946{
947    ReplyKeyPack key_pack;
948    krb5_error_code ret;
949    size_t size;
950
951    ret = decode_ReplyKeyPack(content->data,
952			      content->length,
953			      &key_pack,
954			      &size);
955    if (ret) {
956	krb5_set_error_message(context, ret,
957			       N_("PKINIT decoding reply key failed", ""));
958	free_ReplyKeyPack(&key_pack);
959	return ret;
960    }
961
962    {
963	krb5_crypto crypto;
964
965	/*
966	 * XXX Verify kp.replyKey is a allowed enctype in the
967	 * configuration file
968	 */
969
970	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
971	if (ret) {
972	    free_ReplyKeyPack(&key_pack);
973	    return ret;
974	}
975
976	ret = krb5_verify_checksum(context, crypto, 6,
977				   req_buffer->data, req_buffer->length,
978				   &key_pack.asChecksum);
979	krb5_crypto_destroy(context, crypto);
980	if (ret) {
981	    free_ReplyKeyPack(&key_pack);
982	    return ret;
983	}
984    }
985
986    *key = malloc (sizeof (**key));
987    if (*key == NULL) {
988	free_ReplyKeyPack(&key_pack);
989	return krb5_enomem(context);
990    }
991
992    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
993    free_ReplyKeyPack(&key_pack);
994    if (ret) {
995	krb5_set_error_message(context, ret,
996			       N_("PKINIT failed copying reply key", ""));
997	free(*key);
998	*key = NULL;
999    }
1000
1001    return ret;
1002}
1003
1004
1005static krb5_error_code
1006pk_verify_host(krb5_context context,
1007	       const char *realm,
1008	       const krb5_krbhst_info *hi,
1009	       struct krb5_pk_init_ctx_data *ctx,
1010	       struct krb5_pk_cert *host)
1011{
1012    krb5_error_code ret = 0;
1013
1014    if (ctx->require_eku) {
1015	ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1016				   &asn1_oid_id_pkkdcekuoid, 0);
1017	if (ret) {
1018	    krb5_set_error_message(context, ret,
1019				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
1020	    return ret;
1021	}
1022    }
1023    if (ctx->require_krbtgt_otherName) {
1024	hx509_octet_string_list list;
1025	size_t i;
1026	int matched = 0;
1027
1028	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1029						       host->cert,
1030						       &asn1_oid_id_pkinit_san,
1031						       &list);
1032	if (ret) {
1033	    krb5_set_error_message(context, ret,
1034				   N_("Failed to find the PK-INIT "
1035				      "subjectAltName in the KDC "
1036				      "certificate", ""));
1037
1038	    return ret;
1039	}
1040
1041	/*
1042	 * subjectAltNames are multi-valued, and a single KDC may serve
1043	 * multiple realms. The SAN validation here must accept
1044	 * the KDC's cert if *any* of the SANs match the expected KDC.
1045	 * It is OK for *some* of the SANs to not match, provided at least
1046	 * one does.
1047	 */
1048	for (i = 0; matched == 0 && i < list.len; i++) {
1049	    KRB5PrincipalName r;
1050
1051	    ret = decode_KRB5PrincipalName(list.val[i].data,
1052					   list.val[i].length,
1053					   &r,
1054					   NULL);
1055	    if (ret) {
1056		krb5_set_error_message(context, ret,
1057				       N_("Failed to decode the PK-INIT "
1058					  "subjectAltName in the "
1059					  "KDC certificate", ""));
1060
1061		break;
1062	    }
1063
1064	    if (r.principalName.name_string.len == 2 &&
1065		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
1066		&& strcmp(r.principalName.name_string.val[1], realm) == 0
1067		&& strcmp(r.realm, realm) == 0)
1068		matched = 1;
1069
1070	    free_KRB5PrincipalName(&r);
1071	}
1072	hx509_free_octet_string_list(&list);
1073	if (matched == 0) {
1074	    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1075	    /* XXX: Lost in translation... */
1076	    krb5_set_error_message(context, ret,
1077				   N_("KDC have wrong realm name in "
1078				      "the certificate", ""));
1079	}
1080    }
1081    if (ret)
1082	return ret;
1083
1084    if (hi) {
1085	ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1086				    ctx->require_hostname_match,
1087				    HX509_HN_HOSTNAME,
1088				    hi->hostname,
1089				    hi->ai->ai_addr, hi->ai->ai_addrlen);
1090
1091	if (ret)
1092	    krb5_set_error_message(context, ret,
1093				   N_("Address mismatch in "
1094				      "the KDC certificate", ""));
1095    }
1096    return ret;
1097}
1098
1099static krb5_error_code
1100pk_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		      const krb5_krbhst_info *hi,
1108	       	      unsigned nonce,
1109		      const krb5_data *req_buffer,
1110	       	      PA_DATA *pa,
1111	       	      krb5_keyblock **key)
1112{
1113    krb5_error_code ret;
1114    struct krb5_pk_cert *host = NULL;
1115    krb5_data content;
1116    heim_oid contentType = { 0, NULL };
1117    int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1118
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;
1123    }
1124
1125    if (ctx->type == PKINIT_WIN2K)
1126	flags |= HX509_CMS_UE_ALLOW_WEAK;
1127
1128    ret = hx509_cms_unenvelope(context->hx509ctx,
1129			       ctx->id->certs,
1130			       flags,
1131			       indata->data,
1132			       indata->length,
1133			       NULL,
1134			       0,
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;
1141    }
1142    der_free_oid(&contentType);
1143
1144    /* win2k uses ContentInfo */
1145    if (type == PKINIT_WIN2K) {
1146	heim_oid type2;
1147	heim_octet_string out;
1148
1149	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1150	if (ret) {
1151	    /* windows LH with interesting CMS packets */
1152	    size_t ph = 1 + der_length_len(content.length);
1153	    unsigned char *ptr = malloc(content.length + ph);
1154	    size_t l;
1155
1156	    memcpy(ptr + ph, content.data, content.length);
1157
1158	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1159					  ASN1_C_UNIV, CONS, UT_Sequence, &l);
1160	    if (ret) {
1161                free(ptr);
1162		return ret;
1163            }
1164	    free(content.data);
1165	    content.data = ptr;
1166	    content.length += ph;
1167
1168	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1169	    if (ret)
1170		goto out;
1171	}
1172	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1173	    ret = EINVAL; /* XXX */
1174	    krb5_set_error_message(context, ret,
1175				   N_("PKINIT: Invalid content type", ""));
1176	    der_free_oid(&type2);
1177	    der_free_octet_string(&out);
1178	    goto out;
1179	}
1180	der_free_oid(&type2);
1181	krb5_data_free(&content);
1182	ret = krb5_data_copy(&content, out.data, out.length);
1183	der_free_octet_string(&out);
1184	if (ret) {
1185	    krb5_set_error_message(context, ret,
1186				   N_("malloc: out of memory", ""));
1187	    goto out;
1188	}
1189    }
1190
1191    ret = pk_verify_sign(context,
1192			 content.data,
1193			 content.length,
1194			 ctx->id,
1195			 &contentType,
1196			 &content,
1197			 &host);
1198    if (ret)
1199	goto out;
1200
1201    /* make sure that it is the kdc's certificate */
1202    ret = pk_verify_host(context, realm, hi, ctx, host);
1203    if (ret) {
1204	goto out;
1205    }
1206
1207#if 0
1208    if (type == PKINIT_WIN2K) {
1209	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1210	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1211	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1212	    goto out;
1213	}
1214    } else {
1215	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1216	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1217	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1218	    goto out;
1219	}
1220    }
1221#endif
1222
1223    switch(type) {
1224    case PKINIT_WIN2K:
1225	ret = get_reply_key(context, &content, req_buffer, key);
1226	if (ret != 0 && ctx->require_binding == 0)
1227	    ret = get_reply_key_win(context, &content, nonce, key);
1228	break;
1229    case PKINIT_27:
1230	ret = get_reply_key(context, &content, req_buffer, key);
1231	break;
1232    }
1233    if (ret)
1234	goto out;
1235
1236    /* XXX compare given etype with key->etype */
1237
1238 out:
1239    if (host)
1240	_krb5_pk_cert_free(host);
1241    der_free_oid(&contentType);
1242    krb5_data_free(&content);
1243
1244    return ret;
1245}
1246
1247static krb5_error_code
1248pk_rd_pa_reply_dh(krb5_context context,
1249		  const heim_octet_string *indata,
1250		  const heim_oid *dataType,
1251		  const char *realm,
1252		  krb5_pk_init_ctx ctx,
1253		  krb5_enctype etype,
1254		  const krb5_krbhst_info *hi,
1255		  const DHNonce *c_n,
1256		  const DHNonce *k_n,
1257                  unsigned nonce,
1258                  PA_DATA *pa,
1259                  krb5_keyblock **key)
1260{
1261    const unsigned char *p;
1262    unsigned char *dh_gen_key = NULL;
1263    struct krb5_pk_cert *host = NULL;
1264    BIGNUM *kdc_dh_pubkey = NULL;
1265    KDCDHKeyInfo kdc_dh_info;
1266    heim_oid contentType = { 0, NULL };
1267    krb5_data content;
1268    krb5_error_code ret;
1269    int dh_gen_keylen = 0;
1270    size_t size;
1271
1272    krb5_data_zero(&content);
1273    memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1274
1275    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1276	krb5_set_error_message(context, EINVAL,
1277			       N_("PKINIT: Invalid content type", ""));
1278	return EINVAL;
1279    }
1280
1281    ret = pk_verify_sign(context,
1282			 indata->data,
1283			 indata->length,
1284			 ctx->id,
1285			 &contentType,
1286			 &content,
1287			 &host);
1288    if (ret)
1289	goto out;
1290
1291    /* make sure that it is the kdc's certificate */
1292    ret = pk_verify_host(context, realm, hi, ctx, host);
1293    if (ret)
1294	goto out;
1295
1296    if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1297	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1298	krb5_set_error_message(context, ret,
1299			       N_("pkinit - dh reply contains wrong oid", ""));
1300	goto out;
1301    }
1302
1303    ret = decode_KDCDHKeyInfo(content.data,
1304			      content.length,
1305			      &kdc_dh_info,
1306			      &size);
1307
1308    if (ret) {
1309	krb5_set_error_message(context, ret,
1310			       N_("pkinit - failed to decode "
1311				  "KDC DH Key Info", ""));
1312	goto out;
1313    }
1314
1315    if (kdc_dh_info.nonce != nonce) {
1316	ret = KRB5KRB_AP_ERR_MODIFIED;
1317	krb5_set_error_message(context, ret,
1318			       N_("PKINIT: DH nonce is wrong", ""));
1319	goto out;
1320    }
1321
1322    if (kdc_dh_info.dhKeyExpiration) {
1323	if (k_n == NULL) {
1324	    ret = KRB5KRB_ERR_GENERIC;
1325	    krb5_set_error_message(context, ret,
1326				   N_("pkinit; got key expiration "
1327				      "without server nonce", ""));
1328	    goto out;
1329	}
1330	if (c_n == NULL) {
1331	    ret = KRB5KRB_ERR_GENERIC;
1332	    krb5_set_error_message(context, ret,
1333				   N_("pkinit; got DH reuse but no "
1334				      "client nonce", ""));
1335	    goto out;
1336	}
1337    } else {
1338	if (k_n) {
1339	    ret = KRB5KRB_ERR_GENERIC;
1340	    krb5_set_error_message(context, ret,
1341				   N_("pkinit: got server nonce "
1342				      "without key expiration", ""));
1343	    goto out;
1344	}
1345	c_n = NULL;
1346    }
1347
1348
1349    p = kdc_dh_info.subjectPublicKey.data;
1350    size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1351
1352    if (ctx->keyex == USE_DH) {
1353	DHPublicKey k;
1354	ret = decode_DHPublicKey(p, size, &k, NULL);
1355	if (ret) {
1356	    krb5_set_error_message(context, ret,
1357				   N_("pkinit: can't decode "
1358				      "without key expiration", ""));
1359	    goto out;
1360	}
1361
1362	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1363	free_DHPublicKey(&k);
1364	if (kdc_dh_pubkey == NULL) {
1365	    ret = ENOMEM;
1366	    goto out;
1367	}
1368
1369
1370	size = DH_size(ctx->u.dh);
1371
1372	dh_gen_key = malloc(size);
1373	if (dh_gen_key == NULL) {
1374	    ret = krb5_enomem(context);
1375	    goto out;
1376	}
1377
1378	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1379	if (dh_gen_keylen == -1) {
1380	    ret = KRB5KRB_ERR_GENERIC;
1381	    dh_gen_keylen = 0;
1382	    krb5_set_error_message(context, ret,
1383				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1384	    goto out;
1385	}
1386	if (dh_gen_keylen < (int)size) {
1387	    size -= dh_gen_keylen;
1388	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1389	    memset(dh_gen_key, 0, size);
1390	}
1391
1392    } else {
1393        ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
1394                                                    size, &dh_gen_key,
1395                                                    &dh_gen_keylen);
1396        if (ret)
1397          goto out;
1398    }
1399
1400    if (dh_gen_keylen <= 0) {
1401	ret = EINVAL;
1402	krb5_set_error_message(context, ret,
1403			       N_("PKINIT: resulting DH key <= 0", ""));
1404	dh_gen_keylen = 0;
1405	goto out;
1406    }
1407
1408    *key = malloc (sizeof (**key));
1409    if (*key == NULL) {
1410	ret = krb5_enomem(context);
1411	goto out;
1412    }
1413
1414    ret = _krb5_pk_octetstring2key(context,
1415				   etype,
1416				   dh_gen_key, dh_gen_keylen,
1417				   c_n, k_n,
1418				   *key);
1419    if (ret) {
1420	krb5_set_error_message(context, ret,
1421			       N_("PKINIT: can't create key from DH key", ""));
1422	free(*key);
1423	*key = NULL;
1424	goto out;
1425    }
1426
1427 out:
1428    if (kdc_dh_pubkey)
1429	BN_free(kdc_dh_pubkey);
1430    if (dh_gen_key) {
1431	memset(dh_gen_key, 0, dh_gen_keylen);
1432	free(dh_gen_key);
1433    }
1434    if (host)
1435	_krb5_pk_cert_free(host);
1436    if (content.data)
1437	krb5_data_free(&content);
1438    der_free_oid(&contentType);
1439    free_KDCDHKeyInfo(&kdc_dh_info);
1440
1441    return ret;
1442}
1443
1444KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1445_krb5_pk_rd_pa_reply(krb5_context context,
1446		     const char *realm,
1447		     void *c,
1448		     krb5_enctype etype,
1449		     const krb5_krbhst_info *hi,
1450		     unsigned nonce,
1451		     const krb5_data *req_buffer,
1452		     PA_DATA *pa,
1453		     krb5_keyblock **key)
1454{
1455    krb5_pk_init_ctx ctx = c;
1456    krb5_error_code ret;
1457    size_t size;
1458
1459    /* Check for IETF PK-INIT first */
1460    if (ctx->type == PKINIT_27) {
1461	PA_PK_AS_REP rep;
1462	heim_octet_string os, data;
1463	heim_oid oid;
1464
1465	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1466	    krb5_set_error_message(context, EINVAL,
1467				   N_("PKINIT: wrong padata recv", ""));
1468	    return EINVAL;
1469	}
1470
1471	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1472				  pa->padata_value.length,
1473				  &rep,
1474				  &size);
1475	if (ret) {
1476	    krb5_set_error_message(context, ret,
1477				   N_("Failed to decode pkinit AS rep", ""));
1478	    return ret;
1479	}
1480
1481	switch (rep.element) {
1482	case choice_PA_PK_AS_REP_dhInfo:
1483	    _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1484	    os = rep.u.dhInfo.dhSignedData;
1485	    break;
1486	case choice_PA_PK_AS_REP_encKeyPack:
1487	    _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1488	    os = rep.u.encKeyPack;
1489	    break;
1490	default: {
1491	    PA_PK_AS_REP_BTMM btmm;
1492	    free_PA_PK_AS_REP(&rep);
1493	    memset(&rep, 0, sizeof(rep));
1494
1495	    _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1496
1497	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1498					   pa->padata_value.length,
1499					   &btmm,
1500					   &size);
1501	    if (ret) {
1502		krb5_set_error_message(context, EINVAL,
1503				       N_("PKINIT: -27 reply "
1504					  "invalid content type", ""));
1505		return EINVAL;
1506	    }
1507
1508	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1509		free_PA_PK_AS_REP_BTMM(&btmm);
1510		ret = EINVAL;
1511		krb5_set_error_message(context, ret,
1512				       N_("DH mode not supported for BTMM mode", ""));
1513		return ret;
1514	    }
1515
1516	    /*
1517	     * Transform to IETF style PK-INIT reply so that free works below
1518	     */
1519
1520	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1521	    rep.u.encKeyPack.data = btmm.encKeyPack->data;
1522	    rep.u.encKeyPack.length = btmm.encKeyPack->length;
1523	    btmm.encKeyPack->data = NULL;
1524	    btmm.encKeyPack->length = 0;
1525	    free_PA_PK_AS_REP_BTMM(&btmm);
1526	    os = rep.u.encKeyPack;
1527	}
1528	}
1529
1530	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1531	if (ret) {
1532	    free_PA_PK_AS_REP(&rep);
1533	    krb5_set_error_message(context, ret,
1534				   N_("PKINIT: failed to unwrap CI", ""));
1535	    return ret;
1536	}
1537
1538	switch (rep.element) {
1539	case choice_PA_PK_AS_REP_dhInfo:
1540	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1541				    ctx->clientDHNonce,
1542				    rep.u.dhInfo.serverDHNonce,
1543				    nonce, pa, key);
1544	    break;
1545	case choice_PA_PK_AS_REP_encKeyPack:
1546	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1547					ctx, etype, hi, nonce, req_buffer, pa, key);
1548	    break;
1549	default:
1550	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1551	}
1552	der_free_octet_string(&data);
1553	der_free_oid(&oid);
1554	free_PA_PK_AS_REP(&rep);
1555
1556    } else if (ctx->type == PKINIT_WIN2K) {
1557	PA_PK_AS_REP_Win2k w2krep;
1558
1559	/* Check for Windows encoding of the AS-REP pa data */
1560
1561#if 0 /* should this be ? */
1562	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1563	    krb5_set_error_message(context, EINVAL,
1564				   "PKINIT: wrong padata recv");
1565	    return EINVAL;
1566	}
1567#endif
1568
1569	memset(&w2krep, 0, sizeof(w2krep));
1570
1571	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1572					pa->padata_value.length,
1573					&w2krep,
1574					&size);
1575	if (ret) {
1576	    krb5_set_error_message(context, ret,
1577				   N_("PKINIT: Failed decoding windows "
1578				      "pkinit reply %d", ""), (int)ret);
1579	    return ret;
1580	}
1581
1582	krb5_clear_error_message(context);
1583
1584	switch (w2krep.element) {
1585	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1586	    heim_octet_string data;
1587	    heim_oid oid;
1588
1589	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1590					       &oid, &data, NULL);
1591	    free_PA_PK_AS_REP_Win2k(&w2krep);
1592	    if (ret) {
1593		krb5_set_error_message(context, ret,
1594				       N_("PKINIT: failed to unwrap CI", ""));
1595		return ret;
1596	    }
1597
1598	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1599					ctx, etype, hi, nonce, req_buffer, pa, key);
1600	    der_free_octet_string(&data);
1601	    der_free_oid(&oid);
1602
1603	    break;
1604	}
1605	default:
1606	    free_PA_PK_AS_REP_Win2k(&w2krep);
1607	    ret = EINVAL;
1608	    krb5_set_error_message(context, ret,
1609				   N_("PKINIT: win2k reply invalid "
1610				      "content type", ""));
1611	    break;
1612	}
1613
1614    } else {
1615	ret = EINVAL;
1616	krb5_set_error_message(context, ret,
1617			       N_("PKINIT: unknown reply type", ""));
1618    }
1619
1620    return ret;
1621}
1622
1623struct prompter {
1624    krb5_context context;
1625    krb5_prompter_fct prompter;
1626    void *prompter_data;
1627};
1628
1629static int
1630hx_pass_prompter(void *data, const hx509_prompt *prompter)
1631{
1632    krb5_error_code ret;
1633    krb5_prompt prompt;
1634    krb5_data password_data;
1635    struct prompter *p = data;
1636
1637    password_data.data   = prompter->reply.data;
1638    password_data.length = prompter->reply.length;
1639
1640    prompt.prompt = prompter->prompt;
1641    prompt.hidden = hx509_prompt_hidden(prompter->type);
1642    prompt.reply  = &password_data;
1643
1644    switch (prompter->type) {
1645    case HX509_PROMPT_TYPE_INFO:
1646	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1647	break;
1648    case HX509_PROMPT_TYPE_PASSWORD:
1649    case HX509_PROMPT_TYPE_QUESTION:
1650    default:
1651	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1652	break;
1653    }
1654
1655    ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1656    if (ret) {
1657	memset (prompter->reply.data, 0, prompter->reply.length);
1658	return 1;
1659    }
1660    return 0;
1661}
1662
1663static krb5_error_code
1664_krb5_pk_set_user_id(krb5_context context,
1665		     krb5_principal principal,
1666		     krb5_pk_init_ctx ctx,
1667		     struct hx509_certs_data *certs)
1668{
1669    hx509_certs c = hx509_certs_ref(certs);
1670    hx509_query *q = NULL;
1671    int ret;
1672
1673    if (ctx->id->certs)
1674	hx509_certs_free(&ctx->id->certs);
1675    if (ctx->id->cert) {
1676	hx509_cert_free(ctx->id->cert);
1677	ctx->id->cert = NULL;
1678    }
1679
1680    ctx->id->certs = c;
1681    ctx->anonymous = 0;
1682
1683    ret = hx509_query_alloc(context->hx509ctx, &q);
1684    if (ret) {
1685	pk_copy_error(context, context->hx509ctx, ret,
1686		      "Allocate query to find signing certificate");
1687	return ret;
1688    }
1689
1690    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1691    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1692
1693    if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1694	ctx->id->flags |= PKINIT_BTMM;
1695    }
1696
1697    ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1698    hx509_query_free(context->hx509ctx, q);
1699
1700    if (ret == 0 && _krb5_have_debug(context, 2)) {
1701	hx509_name name;
1702	char *str, *sn;
1703	heim_integer i;
1704
1705	ret = hx509_cert_get_subject(ctx->id->cert, &name);
1706	if (ret)
1707	    goto out;
1708
1709	ret = hx509_name_to_string(name, &str);
1710	hx509_name_free(&name);
1711	if (ret)
1712	    goto out;
1713
1714	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1715	if (ret) {
1716	    free(str);
1717	    goto out;
1718	}
1719
1720	ret = der_print_hex_heim_integer(&i, &sn);
1721	der_free_heim_integer(&i);
1722	if (ret) {
1723	    free(name);
1724	    goto out;
1725	}
1726
1727	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1728	free(str);
1729	free(sn);
1730    }
1731 out:
1732
1733    return ret;
1734}
1735
1736KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1737_krb5_pk_load_id(krb5_context context,
1738		 struct krb5_pk_identity **ret_id,
1739		 const char *user_id,
1740		 const char *anchor_id,
1741		 char * const *chain_list,
1742		 char * const *revoke_list,
1743		 krb5_prompter_fct prompter,
1744		 void *prompter_data,
1745		 char *password)
1746{
1747    struct krb5_pk_identity *id = NULL;
1748    struct prompter p;
1749    int ret;
1750
1751    *ret_id = NULL;
1752
1753    if (anchor_id == NULL) {
1754	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1755			       N_("PKINIT: No anchor given", ""));
1756	return HEIM_PKINIT_NO_VALID_CA;
1757    }
1758
1759    /* load cert */
1760
1761    id = calloc(1, sizeof(*id));
1762    if (id == NULL)
1763	return krb5_enomem(context);
1764
1765    if (user_id) {
1766	hx509_lock lock;
1767
1768	ret = hx509_lock_init(context->hx509ctx, &lock);
1769	if (ret) {
1770	    pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1771	    goto out;
1772	}
1773
1774	if (password && password[0])
1775	    hx509_lock_add_password(lock, password);
1776
1777	if (prompter) {
1778	    p.context = context;
1779	    p.prompter = prompter;
1780	    p.prompter_data = prompter_data;
1781
1782	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1783	    if (ret) {
1784		hx509_lock_free(lock);
1785		goto out;
1786	    }
1787	}
1788
1789	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1790        hx509_lock_free(lock);
1791	if (ret) {
1792	    pk_copy_error(context, context->hx509ctx, ret,
1793			  "Failed to init cert certs");
1794	    goto out;
1795	}
1796    } else {
1797	id->certs = NULL;
1798    }
1799
1800    ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1801    if (ret) {
1802	pk_copy_error(context, context->hx509ctx, ret,
1803		      "Failed to init anchors");
1804	goto out;
1805    }
1806
1807    ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1808			   0, NULL, &id->certpool);
1809    if (ret) {
1810	pk_copy_error(context, context->hx509ctx, ret,
1811		      "Failed to init chain");
1812	goto out;
1813    }
1814
1815    while (chain_list && *chain_list) {
1816	ret = hx509_certs_append(context->hx509ctx, id->certpool,
1817				 NULL, *chain_list);
1818	if (ret) {
1819	    pk_copy_error(context, context->hx509ctx, ret,
1820			  "Failed to laod chain %s",
1821			  *chain_list);
1822	    goto out;
1823	}
1824	chain_list++;
1825    }
1826
1827    if (revoke_list) {
1828	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1829	if (ret) {
1830	    pk_copy_error(context, context->hx509ctx, ret,
1831			  "Failed init revoke list");
1832	    goto out;
1833	}
1834
1835	while (*revoke_list) {
1836	    ret = hx509_revoke_add_crl(context->hx509ctx,
1837				       id->revokectx,
1838				       *revoke_list);
1839	    if (ret) {
1840		pk_copy_error(context, context->hx509ctx, ret,
1841			      "Failed load revoke list");
1842		goto out;
1843	    }
1844	    revoke_list++;
1845	}
1846    } else
1847	hx509_context_set_missing_revoke(context->hx509ctx, 1);
1848
1849    ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1850    if (ret) {
1851	pk_copy_error(context, context->hx509ctx, ret,
1852		      "Failed init verify context");
1853	goto out;
1854    }
1855
1856    hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1857    hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1858
1859 out:
1860    if (ret) {
1861	hx509_verify_destroy_ctx(id->verify_ctx);
1862	hx509_certs_free(&id->certs);
1863	hx509_certs_free(&id->anchors);
1864	hx509_certs_free(&id->certpool);
1865	hx509_revoke_free(&id->revokectx);
1866	free(id);
1867    } else
1868	*ret_id = id;
1869
1870    return ret;
1871}
1872
1873/*
1874 *
1875 */
1876
1877static void
1878pk_copy_error(krb5_context context,
1879	      hx509_context hx509ctx,
1880	      int hxret,
1881	      const char *fmt,
1882	      ...)
1883{
1884    va_list va;
1885    char *s, *f;
1886    int ret;
1887
1888    va_start(va, fmt);
1889    ret = vasprintf(&f, fmt, va);
1890    va_end(va);
1891    if (ret == -1 || f == NULL) {
1892	krb5_clear_error_message(context);
1893	return;
1894    }
1895
1896    s = hx509_get_error_string(hx509ctx, hxret);
1897    if (s == NULL) {
1898	krb5_clear_error_message(context);
1899	free(f);
1900	return;
1901    }
1902    krb5_set_error_message(context, hxret, "%s: %s", f, s);
1903    free(s);
1904    free(f);
1905}
1906
1907static int
1908parse_integer(krb5_context context, char **p, const char *file, int lineno,
1909	      const char *name, heim_integer *integer)
1910{
1911    int ret;
1912    char *p1;
1913    p1 = strsep(p, " \t");
1914    if (p1 == NULL) {
1915	krb5_set_error_message(context, EINVAL,
1916			       N_("moduli file %s missing %s on line %d", ""),
1917			       file, name, lineno);
1918	return EINVAL;
1919    }
1920    ret = der_parse_hex_heim_integer(p1, integer);
1921    if (ret) {
1922	krb5_set_error_message(context, ret,
1923			       N_("moduli file %s failed parsing %s "
1924				  "on line %d", ""),
1925			       file, name, lineno);
1926	return ret;
1927    }
1928
1929    return 0;
1930}
1931
1932KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1933_krb5_parse_moduli_line(krb5_context context,
1934			const char *file,
1935			int lineno,
1936			char *p,
1937			struct krb5_dh_moduli **m)
1938{
1939    struct krb5_dh_moduli *m1;
1940    char *p1;
1941    int ret;
1942
1943    *m = NULL;
1944
1945    m1 = calloc(1, sizeof(*m1));
1946    if (m1 == NULL)
1947	return krb5_enomem(context);
1948
1949    while (isspace((unsigned char)*p))
1950	p++;
1951    if (*p  == '#') {
1952        free(m1);
1953	return 0;
1954    }
1955    ret = EINVAL;
1956
1957    p1 = strsep(&p, " \t");
1958    if (p1 == NULL) {
1959	krb5_set_error_message(context, ret,
1960			       N_("moduli file %s missing name on line %d", ""),
1961			       file, lineno);
1962	goto out;
1963    }
1964    m1->name = strdup(p1);
1965    if (m1->name == NULL) {
1966	ret = krb5_enomem(context);
1967	goto out;
1968    }
1969
1970    p1 = strsep(&p, " \t");
1971    if (p1 == NULL) {
1972	krb5_set_error_message(context, ret,
1973			       N_("moduli file %s missing bits on line %d", ""),
1974			       file, lineno);
1975	goto out;
1976    }
1977
1978    m1->bits = atoi(p1);
1979    if (m1->bits == 0) {
1980	krb5_set_error_message(context, ret,
1981			       N_("moduli file %s have un-parsable "
1982				  "bits on line %d", ""), file, lineno);
1983	goto out;
1984    }
1985
1986    ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1987    if (ret)
1988	goto out;
1989    ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1990    if (ret)
1991	goto out;
1992    ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1993    if (ret)
1994	goto out;
1995
1996    *m = m1;
1997
1998    return 0;
1999 out:
2000    free(m1->name);
2001    der_free_heim_integer(&m1->p);
2002    der_free_heim_integer(&m1->g);
2003    der_free_heim_integer(&m1->q);
2004    free(m1);
2005    return ret;
2006}
2007
2008KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2009_krb5_free_moduli(struct krb5_dh_moduli **moduli)
2010{
2011    int i;
2012    for (i = 0; moduli[i] != NULL; i++) {
2013	free(moduli[i]->name);
2014	der_free_heim_integer(&moduli[i]->p);
2015	der_free_heim_integer(&moduli[i]->g);
2016	der_free_heim_integer(&moduli[i]->q);
2017	free(moduli[i]);
2018    }
2019    free(moduli);
2020}
2021
2022static const char *default_moduli_RFC2412_MODP_group2 =
2023    /* name */
2024    "RFC2412-MODP-group2 "
2025    /* bits */
2026    "1024 "
2027    /* p */
2028    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2029    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2030    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2031    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2032    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2033    "FFFFFFFF" "FFFFFFFF "
2034    /* g */
2035    "02 "
2036    /* q */
2037    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2038    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2039    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2040    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2041    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2042    "FFFFFFFF" "FFFFFFFF";
2043
2044static const char *default_moduli_rfc3526_MODP_group14 =
2045    /* name */
2046    "rfc3526-MODP-group14 "
2047    /* bits */
2048    "1760 "
2049    /* p */
2050    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2051    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2052    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2053    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2054    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2055    "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2056    "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2057    "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2058    "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2059    "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2060    "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2061    /* g */
2062    "02 "
2063    /* q */
2064    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2065    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2066    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2067    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2068    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2069    "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2070    "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2071    "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2072    "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2073    "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2074    "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2075
2076KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2077_krb5_parse_moduli(krb5_context context, const char *file,
2078		   struct krb5_dh_moduli ***moduli)
2079{
2080    /* name bits P G Q */
2081    krb5_error_code ret;
2082    struct krb5_dh_moduli **m = NULL, **m2;
2083    char buf[4096];
2084    FILE *f;
2085    int lineno = 0, n = 0;
2086
2087    *moduli = NULL;
2088
2089    m = calloc(1, sizeof(m[0]) * 3);
2090    if (m == NULL)
2091	return krb5_enomem(context);
2092
2093    strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2094    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2095    if (ret) {
2096	_krb5_free_moduli(m);
2097	return ret;
2098    }
2099    n++;
2100
2101    strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2102    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2103    if (ret) {
2104	_krb5_free_moduli(m);
2105	return ret;
2106    }
2107    n++;
2108
2109
2110    if (file == NULL)
2111	file = MODULI_FILE;
2112
2113#ifdef KRB5_USE_PATH_TOKENS
2114    {
2115        char * exp_file;
2116
2117	if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
2118            f = fopen(exp_file, "r");
2119            krb5_xfree(exp_file);
2120        } else {
2121            f = NULL;
2122        }
2123    }
2124#else
2125    f = fopen(file, "r");
2126#endif
2127
2128    if (f == NULL) {
2129	*moduli = m;
2130	return 0;
2131    }
2132    rk_cloexec_file(f);
2133
2134    while(fgets(buf, sizeof(buf), f) != NULL) {
2135	struct krb5_dh_moduli *element;
2136
2137	buf[strcspn(buf, "\n")] = '\0';
2138	lineno++;
2139
2140	m2 = realloc(m, (n + 2) * sizeof(m[0]));
2141	if (m2 == NULL) {
2142	    _krb5_free_moduli(m);
2143	    return krb5_enomem(context);
2144	}
2145	m = m2;
2146
2147	m[n] = NULL;
2148
2149	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2150	if (ret) {
2151	    _krb5_free_moduli(m);
2152	    return ret;
2153	}
2154	if (element == NULL)
2155	    continue;
2156
2157	m[n] = element;
2158	m[n + 1] = NULL;
2159	n++;
2160    }
2161    *moduli = m;
2162    return 0;
2163}
2164
2165KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2166_krb5_dh_group_ok(krb5_context context, unsigned long bits,
2167		  heim_integer *p, heim_integer *g, heim_integer *q,
2168		  struct krb5_dh_moduli **moduli,
2169		  char **name)
2170{
2171    int i;
2172
2173    if (name)
2174	*name = NULL;
2175
2176    for (i = 0; moduli[i] != NULL; i++) {
2177	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2178	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2179	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2180	    {
2181		if (bits && bits > moduli[i]->bits) {
2182		    krb5_set_error_message(context,
2183					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2184					   N_("PKINIT: DH group parameter %s "
2185					      "no accepted, not enough bits "
2186					      "generated", ""),
2187					   moduli[i]->name);
2188		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2189		}
2190		if (name)
2191		    *name = strdup(moduli[i]->name);
2192		return 0;
2193	    }
2194    }
2195    krb5_set_error_message(context,
2196			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2197			   N_("PKINIT: DH group parameter no ok", ""));
2198    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2199}
2200#endif /* PKINIT */
2201
2202KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2203_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2204{
2205#ifdef PKINIT
2206    krb5_pk_init_ctx ctx;
2207
2208    if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2209	return;
2210    ctx = opt->opt_private->pk_init_ctx;
2211    switch (ctx->keyex) {
2212    case USE_DH:
2213	if (ctx->u.dh)
2214	    DH_free(ctx->u.dh);
2215	break;
2216    case USE_RSA:
2217	break;
2218    case USE_ECDH:
2219	if (ctx->u.eckey)
2220            _krb5_pk_eckey_free(ctx->u.eckey);
2221	break;
2222    }
2223    if (ctx->id) {
2224	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2225	hx509_certs_free(&ctx->id->certs);
2226	hx509_cert_free(ctx->id->cert);
2227	hx509_certs_free(&ctx->id->anchors);
2228	hx509_certs_free(&ctx->id->certpool);
2229
2230	if (ctx->clientDHNonce) {
2231	    krb5_free_data(NULL, ctx->clientDHNonce);
2232	    ctx->clientDHNonce = NULL;
2233	}
2234	if (ctx->m)
2235	    _krb5_free_moduli(ctx->m);
2236	free(ctx->id);
2237	ctx->id = NULL;
2238    }
2239    free(opt->opt_private->pk_init_ctx);
2240    opt->opt_private->pk_init_ctx = NULL;
2241#endif
2242}
2243
2244KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2245krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2246				   krb5_get_init_creds_opt *opt,
2247				   krb5_principal principal,
2248				   const char *user_id,
2249				   const char *x509_anchors,
2250				   char * const * pool,
2251				   char * const * pki_revoke,
2252				   int flags,
2253				   krb5_prompter_fct prompter,
2254				   void *prompter_data,
2255				   char *password)
2256{
2257#ifdef PKINIT
2258    krb5_error_code ret;
2259    char *anchors = NULL;
2260
2261    if (opt->opt_private == NULL) {
2262	krb5_set_error_message(context, EINVAL,
2263			       N_("PKINIT: on non extendable opt", ""));
2264	return EINVAL;
2265    }
2266
2267    opt->opt_private->pk_init_ctx =
2268	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2269    if (opt->opt_private->pk_init_ctx == NULL)
2270	return krb5_enomem(context);
2271    opt->opt_private->pk_init_ctx->require_binding = 0;
2272    opt->opt_private->pk_init_ctx->require_eku = 1;
2273    opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2274    opt->opt_private->pk_init_ctx->peer = NULL;
2275
2276    /* XXX implement krb5_appdefault_strings  */
2277    if (pool == NULL)
2278	pool = krb5_config_get_strings(context, NULL,
2279				       "appdefaults",
2280				       "pkinit_pool",
2281				       NULL);
2282
2283    if (pki_revoke == NULL)
2284	pki_revoke = krb5_config_get_strings(context, NULL,
2285					     "appdefaults",
2286					     "pkinit_revoke",
2287					     NULL);
2288
2289    if (x509_anchors == NULL) {
2290	krb5_appdefault_string(context, "kinit",
2291			       krb5_principal_get_realm(context, principal),
2292			       "pkinit_anchors", NULL, &anchors);
2293	x509_anchors = anchors;
2294    }
2295
2296    if (flags & 4)
2297	opt->opt_private->pk_init_ctx->anonymous = 1;
2298
2299    ret = _krb5_pk_load_id(context,
2300			   &opt->opt_private->pk_init_ctx->id,
2301			   user_id,
2302			   x509_anchors,
2303			   pool,
2304			   pki_revoke,
2305			   prompter,
2306			   prompter_data,
2307			   password);
2308    if (ret) {
2309	free(opt->opt_private->pk_init_ctx);
2310	opt->opt_private->pk_init_ctx = NULL;
2311	return ret;
2312    }
2313
2314    if (opt->opt_private->pk_init_ctx->id->certs) {
2315	_krb5_pk_set_user_id(context,
2316			     principal,
2317			     opt->opt_private->pk_init_ctx,
2318			     opt->opt_private->pk_init_ctx->id->certs);
2319    } else
2320	opt->opt_private->pk_init_ctx->id->cert = NULL;
2321
2322    if ((flags & 2) == 0) {
2323	hx509_context hx509ctx = context->hx509ctx;
2324	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2325
2326	opt->opt_private->pk_init_ctx->keyex = USE_DH;
2327
2328	/*
2329	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2330	 */
2331	if (cert) {
2332	    AlgorithmIdentifier alg;
2333
2334	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2335	    if (ret == 0) {
2336		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2337		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2338		free_AlgorithmIdentifier(&alg);
2339	    }
2340	}
2341
2342    } else {
2343	opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2344
2345	if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2346	    krb5_set_error_message(context, EINVAL,
2347				   N_("No anonymous pkinit support in RSA mode", ""));
2348	    return EINVAL;
2349	}
2350    }
2351
2352    return 0;
2353#else
2354    krb5_set_error_message(context, EINVAL,
2355			   N_("no support for PKINIT compiled in", ""));
2356    return EINVAL;
2357#endif
2358}
2359
2360krb5_error_code KRB5_LIB_FUNCTION
2361krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2362					      krb5_get_init_creds_opt *opt,
2363					      struct hx509_certs_data *certs)
2364{
2365#ifdef PKINIT
2366    if (opt->opt_private == NULL) {
2367	krb5_set_error_message(context, EINVAL,
2368			       N_("PKINIT: on non extendable opt", ""));
2369	return EINVAL;
2370    }
2371    if (opt->opt_private->pk_init_ctx == NULL) {
2372	krb5_set_error_message(context, EINVAL,
2373			       N_("PKINIT: on pkinit context", ""));
2374	return EINVAL;
2375    }
2376
2377    _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2378
2379    return 0;
2380#else
2381    krb5_set_error_message(context, EINVAL,
2382			   N_("no support for PKINIT compiled in", ""));
2383    return EINVAL;
2384#endif
2385}
2386
2387#ifdef PKINIT
2388
2389static int
2390get_ms_san(hx509_context context, hx509_cert cert, char **upn)
2391{
2392    hx509_octet_string_list list;
2393    int ret;
2394
2395    *upn = NULL;
2396
2397    ret = hx509_cert_find_subjectAltName_otherName(context,
2398						   cert,
2399						   &asn1_oid_id_pkinit_ms_san,
2400						   &list);
2401    if (ret)
2402	return 0;
2403
2404    if (list.len > 0 && list.val[0].length > 0)
2405	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2406				upn, NULL);
2407    else
2408	ret = 1;
2409    hx509_free_octet_string_list(&list);
2410
2411    return ret;
2412}
2413
2414static int
2415find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2416{
2417    char *upn;
2418    int ret;
2419
2420    ret = get_ms_san(context, cert, &upn);
2421    if (ret == 0)
2422	free(upn);
2423    return ret;
2424}
2425
2426
2427
2428#endif
2429
2430/*
2431 * Private since it need to be redesigned using krb5_get_init_creds()
2432 */
2433
2434KRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2435krb5_pk_enterprise_cert(krb5_context context,
2436			const char *user_id,
2437			krb5_const_realm realm,
2438			krb5_principal *principal,
2439			struct hx509_certs_data **res)
2440{
2441#ifdef PKINIT
2442    krb5_error_code ret;
2443    hx509_certs certs, result;
2444    hx509_cert cert = NULL;
2445    hx509_query *q;
2446    char *name;
2447
2448    *principal = NULL;
2449    if (res)
2450	*res = NULL;
2451
2452    if (user_id == NULL) {
2453	krb5_set_error_message(context, ENOENT, "no user id");
2454	return ENOENT;
2455    }
2456
2457    ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2458    if (ret) {
2459	pk_copy_error(context, context->hx509ctx, ret,
2460		      "Failed to init cert certs");
2461	goto out;
2462    }
2463
2464    ret = hx509_query_alloc(context->hx509ctx, &q);
2465    if (ret) {
2466	krb5_set_error_message(context, ret, "out of memory");
2467	hx509_certs_free(&certs);
2468	goto out;
2469    }
2470
2471    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2472    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2473    hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2474    hx509_query_match_cmp_func(q, find_ms_san, NULL);
2475
2476    ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2477    hx509_query_free(context->hx509ctx, q);
2478    hx509_certs_free(&certs);
2479    if (ret) {
2480	pk_copy_error(context, context->hx509ctx, ret,
2481		      "Failed to find PKINIT certificate");
2482	return ret;
2483    }
2484
2485    ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2486    hx509_certs_free(&result);
2487    if (ret) {
2488	pk_copy_error(context, context->hx509ctx, ret,
2489		      "Failed to get one cert");
2490	goto out;
2491    }
2492
2493    ret = get_ms_san(context->hx509ctx, cert, &name);
2494    if (ret) {
2495	pk_copy_error(context, context->hx509ctx, ret,
2496		      "Failed to get MS SAN");
2497	goto out;
2498    }
2499
2500    ret = krb5_make_principal(context, principal, realm, name, NULL);
2501    free(name);
2502    if (ret)
2503	goto out;
2504
2505    krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2506
2507    if (res) {
2508	ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2509	if (ret)
2510	    goto out;
2511
2512	ret = hx509_certs_add(context->hx509ctx, *res, cert);
2513	if (ret) {
2514	    hx509_certs_free(res);
2515	    goto out;
2516	}
2517    }
2518
2519 out:
2520    hx509_cert_free(cert);
2521
2522    return ret;
2523#else
2524    krb5_set_error_message(context, EINVAL,
2525			   N_("no support for PKINIT compiled in", ""));
2526    return EINVAL;
2527#endif
2528}
2529