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