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