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