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