1178825Sdfr/*
2178825Sdfr * Copyright (c) 2003 - 2007 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "krb5_locl.h"
35178825Sdfr
36178825SdfrRCSID("$Id: pkinit.c 22433 2008-01-13 14:11:46Z lha $");
37178825Sdfr
38178825Sdfrstruct krb5_dh_moduli {
39178825Sdfr    char *name;
40178825Sdfr    unsigned long bits;
41178825Sdfr    heim_integer p;
42178825Sdfr    heim_integer g;
43178825Sdfr    heim_integer q;
44178825Sdfr};
45178825Sdfr
46178825Sdfr#ifdef PKINIT
47178825Sdfr
48178825Sdfr#include <heim_asn1.h>
49178825Sdfr#include <rfc2459_asn1.h>
50178825Sdfr#include <cms_asn1.h>
51178825Sdfr#include <pkcs8_asn1.h>
52178825Sdfr#include <pkcs9_asn1.h>
53178825Sdfr#include <pkcs12_asn1.h>
54178825Sdfr#include <pkinit_asn1.h>
55178825Sdfr#include <asn1_err.h>
56178825Sdfr
57178825Sdfr#include <der.h>
58178825Sdfr
59178825Sdfr#include <hx509.h>
60178825Sdfr
61178825Sdfrenum {
62178825Sdfr    COMPAT_WIN2K = 1,
63178825Sdfr    COMPAT_IETF = 2
64178825Sdfr};
65178825Sdfr
66178825Sdfrstruct krb5_pk_identity {
67178825Sdfr    hx509_context hx509ctx;
68178825Sdfr    hx509_verify_ctx verify_ctx;
69178825Sdfr    hx509_certs certs;
70178825Sdfr    hx509_certs anchors;
71178825Sdfr    hx509_certs certpool;
72178825Sdfr    hx509_revoke_ctx revokectx;
73178825Sdfr};
74178825Sdfr
75178825Sdfrstruct krb5_pk_cert {
76178825Sdfr    hx509_cert cert;
77178825Sdfr};
78178825Sdfr
79178825Sdfrstruct krb5_pk_init_ctx_data {
80178825Sdfr    struct krb5_pk_identity *id;
81178825Sdfr    DH *dh;
82178825Sdfr    krb5_data *clientDHNonce;
83178825Sdfr    struct krb5_dh_moduli **m;
84178825Sdfr    hx509_peer_info peer;
85178825Sdfr    int type;
86178825Sdfr    unsigned int require_binding:1;
87178825Sdfr    unsigned int require_eku:1;
88178825Sdfr    unsigned int require_krbtgt_otherName:1;
89178825Sdfr    unsigned int require_hostname_match:1;
90178825Sdfr    unsigned int trustedCertifiers:1;
91178825Sdfr};
92178825Sdfr
93178825Sdfrstatic void
94178825Sdfr_krb5_pk_copy_error(krb5_context context,
95178825Sdfr		    hx509_context hx509ctx,
96178825Sdfr		    int hxret,
97178825Sdfr		    const char *fmt,
98178825Sdfr		    ...)
99178825Sdfr    __attribute__ ((format (printf, 4, 5)));
100178825Sdfr
101178825Sdfr/*
102178825Sdfr *
103178825Sdfr */
104178825Sdfr
105178825Sdfrvoid KRB5_LIB_FUNCTION
106178825Sdfr_krb5_pk_cert_free(struct krb5_pk_cert *cert)
107178825Sdfr{
108178825Sdfr    if (cert->cert) {
109178825Sdfr	hx509_cert_free(cert->cert);
110178825Sdfr    }
111178825Sdfr    free(cert);
112178825Sdfr}
113178825Sdfr
114178825Sdfrstatic krb5_error_code
115178825SdfrBN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
116178825Sdfr{
117178825Sdfr    integer->length = BN_num_bytes(bn);
118178825Sdfr    integer->data = malloc(integer->length);
119178825Sdfr    if (integer->data == NULL) {
120178825Sdfr	krb5_clear_error_string(context);
121178825Sdfr	return ENOMEM;
122178825Sdfr    }
123178825Sdfr    BN_bn2bin(bn, integer->data);
124178825Sdfr    integer->negative = BN_is_negative(bn);
125178825Sdfr    return 0;
126178825Sdfr}
127178825Sdfr
128178825Sdfrstatic BIGNUM *
129178825Sdfrinteger_to_BN(krb5_context context, const char *field, const heim_integer *f)
130178825Sdfr{
131178825Sdfr    BIGNUM *bn;
132178825Sdfr
133178825Sdfr    bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
134178825Sdfr    if (bn == NULL) {
135178825Sdfr	krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
136178825Sdfr	return NULL;
137178825Sdfr    }
138178825Sdfr    BN_set_negative(bn, f->negative);
139178825Sdfr    return bn;
140178825Sdfr}
141178825Sdfr
142178825Sdfr
143178825Sdfrstatic krb5_error_code
144178825Sdfr_krb5_pk_create_sign(krb5_context context,
145178825Sdfr		     const heim_oid *eContentType,
146178825Sdfr		     krb5_data *eContent,
147178825Sdfr		     struct krb5_pk_identity *id,
148178825Sdfr		     hx509_peer_info peer,
149178825Sdfr		     krb5_data *sd_data)
150178825Sdfr{
151178825Sdfr    hx509_cert cert;
152178825Sdfr    hx509_query *q;
153178825Sdfr    int ret;
154178825Sdfr
155178825Sdfr    ret = hx509_query_alloc(id->hx509ctx, &q);
156178825Sdfr    if (ret) {
157178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
158178825Sdfr			    "Allocate query to find signing certificate");
159178825Sdfr	return ret;
160178825Sdfr    }
161178825Sdfr
162178825Sdfr    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
163178825Sdfr    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
164178825Sdfr
165178825Sdfr    ret = hx509_certs_find(id->hx509ctx, id->certs, q, &cert);
166178825Sdfr    hx509_query_free(id->hx509ctx, q);
167178825Sdfr    if (ret) {
168178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
169178825Sdfr			    "Find certificate to signed CMS data");
170178825Sdfr	return ret;
171178825Sdfr    }
172178825Sdfr
173178825Sdfr    ret = hx509_cms_create_signed_1(id->hx509ctx,
174178825Sdfr				    0,
175178825Sdfr				    eContentType,
176178825Sdfr				    eContent->data,
177178825Sdfr				    eContent->length,
178178825Sdfr				    NULL,
179178825Sdfr				    cert,
180178825Sdfr				    peer,
181178825Sdfr				    NULL,
182178825Sdfr				    id->certs,
183178825Sdfr				    sd_data);
184178825Sdfr    if (ret)
185178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret, "create CMS signedData");
186178825Sdfr    hx509_cert_free(cert);
187178825Sdfr
188178825Sdfr    return ret;
189178825Sdfr}
190178825Sdfr
191178825Sdfrstatic int
192178825Sdfrcert2epi(hx509_context context, void *ctx, hx509_cert c)
193178825Sdfr{
194178825Sdfr    ExternalPrincipalIdentifiers *ids = ctx;
195178825Sdfr    ExternalPrincipalIdentifier id;
196178825Sdfr    hx509_name subject = NULL;
197178825Sdfr    void *p;
198178825Sdfr    int ret;
199178825Sdfr
200178825Sdfr    memset(&id, 0, sizeof(id));
201178825Sdfr
202178825Sdfr    ret = hx509_cert_get_subject(c, &subject);
203178825Sdfr    if (ret)
204178825Sdfr	return ret;
205178825Sdfr
206178825Sdfr    if (hx509_name_is_null_p(subject) != 0) {
207178825Sdfr
208178825Sdfr	id.subjectName = calloc(1, sizeof(*id.subjectName));
209178825Sdfr	if (id.subjectName == NULL) {
210178825Sdfr	    hx509_name_free(&subject);
211178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
212178825Sdfr	    return ENOMEM;
213178825Sdfr	}
214178825Sdfr
215178825Sdfr	ret = hx509_name_binary(subject, id.subjectName);
216178825Sdfr	if (ret) {
217178825Sdfr	    hx509_name_free(&subject);
218178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
219178825Sdfr	    return ret;
220178825Sdfr	}
221178825Sdfr    }
222178825Sdfr    hx509_name_free(&subject);
223178825Sdfr
224178825Sdfr
225178825Sdfr    id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
226178825Sdfr    if (id.issuerAndSerialNumber == NULL) {
227178825Sdfr	free_ExternalPrincipalIdentifier(&id);
228178825Sdfr	return ENOMEM;
229178825Sdfr    }
230178825Sdfr
231178825Sdfr    {
232178825Sdfr	IssuerAndSerialNumber iasn;
233178825Sdfr	hx509_name issuer;
234178825Sdfr	size_t size;
235178825Sdfr
236178825Sdfr	memset(&iasn, 0, sizeof(iasn));
237178825Sdfr
238178825Sdfr	ret = hx509_cert_get_issuer(c, &issuer);
239178825Sdfr	if (ret) {
240178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
241178825Sdfr	    return ret;
242178825Sdfr	}
243178825Sdfr
244178825Sdfr	ret = hx509_name_to_Name(issuer, &iasn.issuer);
245178825Sdfr	hx509_name_free(&issuer);
246178825Sdfr	if (ret) {
247178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
248178825Sdfr	    return ret;
249178825Sdfr	}
250178825Sdfr
251178825Sdfr	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
252178825Sdfr	if (ret) {
253178825Sdfr	    free_IssuerAndSerialNumber(&iasn);
254178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
255178825Sdfr	    return ret;
256178825Sdfr	}
257178825Sdfr
258178825Sdfr	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
259178825Sdfr			   id.issuerAndSerialNumber->data,
260178825Sdfr			   id.issuerAndSerialNumber->length,
261178825Sdfr			   &iasn, &size, ret);
262178825Sdfr	free_IssuerAndSerialNumber(&iasn);
263178825Sdfr	if (ret)
264178825Sdfr	    return ret;
265178825Sdfr	if (id.issuerAndSerialNumber->length != size)
266178825Sdfr	    abort();
267178825Sdfr    }
268178825Sdfr
269178825Sdfr    id.subjectKeyIdentifier = NULL;
270178825Sdfr
271178825Sdfr    p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
272178825Sdfr    if (p == NULL) {
273178825Sdfr	free_ExternalPrincipalIdentifier(&id);
274178825Sdfr	return ENOMEM;
275178825Sdfr    }
276178825Sdfr
277178825Sdfr    ids->val = p;
278178825Sdfr    ids->val[ids->len] = id;
279178825Sdfr    ids->len++;
280178825Sdfr
281178825Sdfr    return 0;
282178825Sdfr}
283178825Sdfr
284178825Sdfrstatic krb5_error_code
285178825Sdfrbuild_edi(krb5_context context,
286178825Sdfr	  hx509_context hx509ctx,
287178825Sdfr	  hx509_certs certs,
288178825Sdfr	  ExternalPrincipalIdentifiers *ids)
289178825Sdfr{
290178825Sdfr    return hx509_certs_iter(hx509ctx, certs, cert2epi, ids);
291178825Sdfr}
292178825Sdfr
293178825Sdfrstatic krb5_error_code
294178825Sdfrbuild_auth_pack(krb5_context context,
295178825Sdfr		unsigned nonce,
296178825Sdfr		krb5_pk_init_ctx ctx,
297178825Sdfr		DH *dh,
298178825Sdfr		const KDC_REQ_BODY *body,
299178825Sdfr		AuthPack *a)
300178825Sdfr{
301178825Sdfr    size_t buf_size, len;
302178825Sdfr    krb5_error_code ret;
303178825Sdfr    void *buf;
304178825Sdfr    krb5_timestamp sec;
305178825Sdfr    int32_t usec;
306178825Sdfr    Checksum checksum;
307178825Sdfr
308178825Sdfr    krb5_clear_error_string(context);
309178825Sdfr
310178825Sdfr    memset(&checksum, 0, sizeof(checksum));
311178825Sdfr
312178825Sdfr    krb5_us_timeofday(context, &sec, &usec);
313178825Sdfr    a->pkAuthenticator.ctime = sec;
314178825Sdfr    a->pkAuthenticator.nonce = nonce;
315178825Sdfr
316178825Sdfr    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
317178825Sdfr    if (ret)
318178825Sdfr	return ret;
319178825Sdfr    if (buf_size != len)
320178825Sdfr	krb5_abortx(context, "internal error in ASN.1 encoder");
321178825Sdfr
322178825Sdfr    ret = krb5_create_checksum(context,
323178825Sdfr			       NULL,
324178825Sdfr			       0,
325178825Sdfr			       CKSUMTYPE_SHA1,
326178825Sdfr			       buf,
327178825Sdfr			       len,
328178825Sdfr			       &checksum);
329178825Sdfr    free(buf);
330178825Sdfr    if (ret)
331178825Sdfr	return ret;
332178825Sdfr
333178825Sdfr    ALLOC(a->pkAuthenticator.paChecksum, 1);
334178825Sdfr    if (a->pkAuthenticator.paChecksum == NULL) {
335178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
336178825Sdfr	return ENOMEM;
337178825Sdfr    }
338178825Sdfr
339178825Sdfr    ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
340178825Sdfr			 checksum.checksum.data, checksum.checksum.length);
341178825Sdfr    free_Checksum(&checksum);
342178825Sdfr    if (ret)
343178825Sdfr	return ret;
344178825Sdfr
345178825Sdfr    if (dh) {
346178825Sdfr	DomainParameters dp;
347178825Sdfr	heim_integer dh_pub_key;
348178825Sdfr	krb5_data dhbuf;
349178825Sdfr	size_t size;
350178825Sdfr
351178825Sdfr	if (1 /* support_cached_dh */) {
352178825Sdfr	    ALLOC(a->clientDHNonce, 1);
353178825Sdfr	    if (a->clientDHNonce == NULL) {
354178825Sdfr		krb5_clear_error_string(context);
355178825Sdfr		return ENOMEM;
356178825Sdfr	    }
357178825Sdfr	    ret = krb5_data_alloc(a->clientDHNonce, 40);
358178825Sdfr	    if (a->clientDHNonce == NULL) {
359178825Sdfr		krb5_clear_error_string(context);
360178825Sdfr		return ENOMEM;
361178825Sdfr	    }
362178825Sdfr	    memset(a->clientDHNonce->data, 0, a->clientDHNonce->length);
363178825Sdfr	    ret = krb5_copy_data(context, a->clientDHNonce,
364178825Sdfr				 &ctx->clientDHNonce);
365178825Sdfr	    if (ret)
366178825Sdfr		return ret;
367178825Sdfr	}
368178825Sdfr
369178825Sdfr	ALLOC(a->clientPublicValue, 1);
370178825Sdfr	if (a->clientPublicValue == NULL)
371178825Sdfr	    return ENOMEM;
372178825Sdfr	ret = der_copy_oid(oid_id_dhpublicnumber(),
373178825Sdfr			   &a->clientPublicValue->algorithm.algorithm);
374178825Sdfr	if (ret)
375178825Sdfr	    return ret;
376178825Sdfr
377178825Sdfr	memset(&dp, 0, sizeof(dp));
378178825Sdfr
379178825Sdfr	ret = BN_to_integer(context, dh->p, &dp.p);
380178825Sdfr	if (ret) {
381178825Sdfr	    free_DomainParameters(&dp);
382178825Sdfr	    return ret;
383178825Sdfr	}
384178825Sdfr	ret = BN_to_integer(context, dh->g, &dp.g);
385178825Sdfr	if (ret) {
386178825Sdfr	    free_DomainParameters(&dp);
387178825Sdfr	    return ret;
388178825Sdfr	}
389178825Sdfr	ret = BN_to_integer(context, dh->q, &dp.q);
390178825Sdfr	if (ret) {
391178825Sdfr	    free_DomainParameters(&dp);
392178825Sdfr	    return ret;
393178825Sdfr	}
394178825Sdfr	dp.j = NULL;
395178825Sdfr	dp.validationParms = NULL;
396178825Sdfr
397178825Sdfr	a->clientPublicValue->algorithm.parameters =
398178825Sdfr	    malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
399178825Sdfr	if (a->clientPublicValue->algorithm.parameters == NULL) {
400178825Sdfr	    free_DomainParameters(&dp);
401178825Sdfr	    return ret;
402178825Sdfr	}
403178825Sdfr
404178825Sdfr	ASN1_MALLOC_ENCODE(DomainParameters,
405178825Sdfr			   a->clientPublicValue->algorithm.parameters->data,
406178825Sdfr			   a->clientPublicValue->algorithm.parameters->length,
407178825Sdfr			   &dp, &size, ret);
408178825Sdfr	free_DomainParameters(&dp);
409178825Sdfr	if (ret)
410178825Sdfr	    return ret;
411178825Sdfr	if (size != a->clientPublicValue->algorithm.parameters->length)
412178825Sdfr	    krb5_abortx(context, "Internal ASN1 encoder error");
413178825Sdfr
414178825Sdfr	ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
415178825Sdfr	if (ret)
416178825Sdfr	    return ret;
417178825Sdfr
418178825Sdfr	ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
419178825Sdfr			   &dh_pub_key, &size, ret);
420178825Sdfr	der_free_heim_integer(&dh_pub_key);
421178825Sdfr	if (ret)
422178825Sdfr	    return ret;
423178825Sdfr	if (size != dhbuf.length)
424178825Sdfr	    krb5_abortx(context, "asn1 internal error");
425178825Sdfr
426178825Sdfr	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
427178825Sdfr	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
428178825Sdfr    }
429178825Sdfr
430178825Sdfr    {
431178825Sdfr	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
432178825Sdfr	if (a->supportedCMSTypes == NULL)
433178825Sdfr	    return ENOMEM;
434178825Sdfr
435178825Sdfr	ret = hx509_crypto_available(ctx->id->hx509ctx, HX509_SELECT_ALL, NULL,
436178825Sdfr				     &a->supportedCMSTypes->val,
437178825Sdfr				     &a->supportedCMSTypes->len);
438178825Sdfr	if (ret)
439178825Sdfr	    return ret;
440178825Sdfr    }
441178825Sdfr
442178825Sdfr    return ret;
443178825Sdfr}
444178825Sdfr
445178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
446178825Sdfr_krb5_pk_mk_ContentInfo(krb5_context context,
447178825Sdfr			const krb5_data *buf,
448178825Sdfr			const heim_oid *oid,
449178825Sdfr			struct ContentInfo *content_info)
450178825Sdfr{
451178825Sdfr    krb5_error_code ret;
452178825Sdfr
453178825Sdfr    ret = der_copy_oid(oid, &content_info->contentType);
454178825Sdfr    if (ret)
455178825Sdfr	return ret;
456178825Sdfr    ALLOC(content_info->content, 1);
457178825Sdfr    if (content_info->content == NULL)
458178825Sdfr	return ENOMEM;
459178825Sdfr    content_info->content->data = malloc(buf->length);
460178825Sdfr    if (content_info->content->data == NULL)
461178825Sdfr	return ENOMEM;
462178825Sdfr    memcpy(content_info->content->data, buf->data, buf->length);
463178825Sdfr    content_info->content->length = buf->length;
464178825Sdfr    return 0;
465178825Sdfr}
466178825Sdfr
467178825Sdfrstatic krb5_error_code
468178825Sdfrpk_mk_padata(krb5_context context,
469178825Sdfr	     krb5_pk_init_ctx ctx,
470178825Sdfr	     const KDC_REQ_BODY *req_body,
471178825Sdfr	     unsigned nonce,
472178825Sdfr	     METHOD_DATA *md)
473178825Sdfr{
474178825Sdfr    struct ContentInfo content_info;
475178825Sdfr    krb5_error_code ret;
476178825Sdfr    const heim_oid *oid;
477178825Sdfr    size_t size;
478178825Sdfr    krb5_data buf, sd_buf;
479178825Sdfr    int pa_type;
480178825Sdfr
481178825Sdfr    krb5_data_zero(&buf);
482178825Sdfr    krb5_data_zero(&sd_buf);
483178825Sdfr    memset(&content_info, 0, sizeof(content_info));
484178825Sdfr
485178825Sdfr    if (ctx->type == COMPAT_WIN2K) {
486178825Sdfr	AuthPack_Win2k ap;
487178825Sdfr	krb5_timestamp sec;
488178825Sdfr	int32_t usec;
489178825Sdfr
490178825Sdfr	memset(&ap, 0, sizeof(ap));
491178825Sdfr
492178825Sdfr	/* fill in PKAuthenticator */
493178825Sdfr	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
494178825Sdfr	if (ret) {
495178825Sdfr	    free_AuthPack_Win2k(&ap);
496178825Sdfr	    krb5_clear_error_string(context);
497178825Sdfr	    goto out;
498178825Sdfr	}
499178825Sdfr	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
500178825Sdfr	if (ret) {
501178825Sdfr	    free_AuthPack_Win2k(&ap);
502178825Sdfr	    krb5_clear_error_string(context);
503178825Sdfr	    goto out;
504178825Sdfr	}
505178825Sdfr
506178825Sdfr	krb5_us_timeofday(context, &sec, &usec);
507178825Sdfr	ap.pkAuthenticator.ctime = sec;
508178825Sdfr	ap.pkAuthenticator.cusec = usec;
509178825Sdfr	ap.pkAuthenticator.nonce = nonce;
510178825Sdfr
511178825Sdfr	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
512178825Sdfr			   &ap, &size, ret);
513178825Sdfr	free_AuthPack_Win2k(&ap);
514178825Sdfr	if (ret) {
515178825Sdfr	    krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
516178825Sdfr	    goto out;
517178825Sdfr	}
518178825Sdfr	if (buf.length != size)
519178825Sdfr	    krb5_abortx(context, "internal ASN1 encoder error");
520178825Sdfr
521178825Sdfr	oid = oid_id_pkcs7_data();
522178825Sdfr    } else if (ctx->type == COMPAT_IETF) {
523178825Sdfr	AuthPack ap;
524178825Sdfr
525178825Sdfr	memset(&ap, 0, sizeof(ap));
526178825Sdfr
527178825Sdfr	ret = build_auth_pack(context, nonce, ctx, ctx->dh, req_body, &ap);
528178825Sdfr	if (ret) {
529178825Sdfr	    free_AuthPack(&ap);
530178825Sdfr	    goto out;
531178825Sdfr	}
532178825Sdfr
533178825Sdfr	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
534178825Sdfr	free_AuthPack(&ap);
535178825Sdfr	if (ret) {
536178825Sdfr	    krb5_set_error_string(context, "AuthPack: %d", ret);
537178825Sdfr	    goto out;
538178825Sdfr	}
539178825Sdfr	if (buf.length != size)
540178825Sdfr	    krb5_abortx(context, "internal ASN1 encoder error");
541178825Sdfr
542178825Sdfr	oid = oid_id_pkauthdata();
543178825Sdfr    } else
544178825Sdfr	krb5_abortx(context, "internal pkinit error");
545178825Sdfr
546178825Sdfr    ret = _krb5_pk_create_sign(context,
547178825Sdfr			       oid,
548178825Sdfr			       &buf,
549178825Sdfr			       ctx->id,
550178825Sdfr			       ctx->peer,
551178825Sdfr			       &sd_buf);
552178825Sdfr    krb5_data_free(&buf);
553178825Sdfr    if (ret)
554178825Sdfr	goto out;
555178825Sdfr
556178825Sdfr    ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), &sd_buf, &buf);
557178825Sdfr    krb5_data_free(&sd_buf);
558178825Sdfr    if (ret) {
559178825Sdfr	krb5_set_error_string(context,
560178825Sdfr			      "ContentInfo wrapping of signedData failed");
561178825Sdfr	goto out;
562178825Sdfr    }
563178825Sdfr
564178825Sdfr    if (ctx->type == COMPAT_WIN2K) {
565178825Sdfr	PA_PK_AS_REQ_Win2k winreq;
566178825Sdfr
567178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
568178825Sdfr
569178825Sdfr	memset(&winreq, 0, sizeof(winreq));
570178825Sdfr
571178825Sdfr	winreq.signed_auth_pack = buf;
572178825Sdfr
573178825Sdfr	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
574178825Sdfr			   &winreq, &size, ret);
575178825Sdfr	free_PA_PK_AS_REQ_Win2k(&winreq);
576178825Sdfr
577178825Sdfr    } else if (ctx->type == COMPAT_IETF) {
578178825Sdfr	PA_PK_AS_REQ req;
579178825Sdfr
580178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REQ;
581178825Sdfr
582178825Sdfr	memset(&req, 0, sizeof(req));
583178825Sdfr	req.signedAuthPack = buf;
584178825Sdfr
585178825Sdfr	if (ctx->trustedCertifiers) {
586178825Sdfr
587178825Sdfr	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
588178825Sdfr	    if (req.trustedCertifiers == NULL) {
589178825Sdfr		krb5_set_error_string(context, "malloc: out of memory");
590178825Sdfr		free_PA_PK_AS_REQ(&req);
591178825Sdfr		goto out;
592178825Sdfr	    }
593178825Sdfr	    ret = build_edi(context, ctx->id->hx509ctx,
594178825Sdfr			    ctx->id->anchors, req.trustedCertifiers);
595178825Sdfr	    if (ret) {
596178825Sdfr		krb5_set_error_string(context, "pk-init: failed to build trustedCertifiers");
597178825Sdfr		free_PA_PK_AS_REQ(&req);
598178825Sdfr		goto out;
599178825Sdfr	    }
600178825Sdfr	}
601178825Sdfr	req.kdcPkId = NULL;
602178825Sdfr
603178825Sdfr	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
604178825Sdfr			   &req, &size, ret);
605178825Sdfr
606178825Sdfr	free_PA_PK_AS_REQ(&req);
607178825Sdfr
608178825Sdfr    } else
609178825Sdfr	krb5_abortx(context, "internal pkinit error");
610178825Sdfr    if (ret) {
611178825Sdfr	krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
612178825Sdfr	goto out;
613178825Sdfr    }
614178825Sdfr    if (buf.length != size)
615178825Sdfr	krb5_abortx(context, "Internal ASN1 encoder error");
616178825Sdfr
617178825Sdfr    ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
618178825Sdfr    if (ret)
619178825Sdfr	free(buf.data);
620178825Sdfr
621178825Sdfr    if (ret == 0 && ctx->type == COMPAT_WIN2K)
622178825Sdfr	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
623178825Sdfr
624178825Sdfrout:
625178825Sdfr    free_ContentInfo(&content_info);
626178825Sdfr
627178825Sdfr    return ret;
628178825Sdfr}
629178825Sdfr
630178825Sdfr
631178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
632178825Sdfr_krb5_pk_mk_padata(krb5_context context,
633178825Sdfr		   void *c,
634178825Sdfr		   const KDC_REQ_BODY *req_body,
635178825Sdfr		   unsigned nonce,
636178825Sdfr		   METHOD_DATA *md)
637178825Sdfr{
638178825Sdfr    krb5_pk_init_ctx ctx = c;
639178825Sdfr    int win2k_compat;
640178825Sdfr
641178825Sdfr    win2k_compat = krb5_config_get_bool_default(context, NULL,
642178825Sdfr						FALSE,
643178825Sdfr						"realms",
644178825Sdfr						req_body->realm,
645178825Sdfr						"pkinit_win2k",
646178825Sdfr						NULL);
647178825Sdfr
648178825Sdfr    if (win2k_compat) {
649178825Sdfr	ctx->require_binding =
650178825Sdfr	    krb5_config_get_bool_default(context, NULL,
651178825Sdfr					 FALSE,
652178825Sdfr					 "realms",
653178825Sdfr					 req_body->realm,
654178825Sdfr					 "pkinit_win2k_require_binding",
655178825Sdfr					 NULL);
656178825Sdfr	ctx->type = COMPAT_WIN2K;
657178825Sdfr    } else
658178825Sdfr	ctx->type = COMPAT_IETF;
659178825Sdfr
660178825Sdfr    ctx->require_eku =
661178825Sdfr	krb5_config_get_bool_default(context, NULL,
662178825Sdfr				     TRUE,
663178825Sdfr				     "realms",
664178825Sdfr				     req_body->realm,
665178825Sdfr				     "pkinit_require_eku",
666178825Sdfr				     NULL);
667178825Sdfr    ctx->require_krbtgt_otherName =
668178825Sdfr	krb5_config_get_bool_default(context, NULL,
669178825Sdfr				     TRUE,
670178825Sdfr				     "realms",
671178825Sdfr				     req_body->realm,
672178825Sdfr				     "pkinit_require_krbtgt_otherName",
673178825Sdfr				     NULL);
674178825Sdfr
675178825Sdfr    ctx->require_hostname_match =
676178825Sdfr	krb5_config_get_bool_default(context, NULL,
677178825Sdfr				     FALSE,
678178825Sdfr				     "realms",
679178825Sdfr				     req_body->realm,
680178825Sdfr				     "pkinit_require_hostname_match",
681178825Sdfr				     NULL);
682178825Sdfr
683178825Sdfr    ctx->trustedCertifiers =
684178825Sdfr	krb5_config_get_bool_default(context, NULL,
685178825Sdfr				     TRUE,
686178825Sdfr				     "realms",
687178825Sdfr				     req_body->realm,
688178825Sdfr				     "pkinit_trustedCertifiers",
689178825Sdfr				     NULL);
690178825Sdfr
691178825Sdfr    return pk_mk_padata(context, ctx, req_body, nonce, md);
692178825Sdfr}
693178825Sdfr
694178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
695178825Sdfr_krb5_pk_verify_sign(krb5_context context,
696178825Sdfr		     const void *data,
697178825Sdfr		     size_t length,
698178825Sdfr		     struct krb5_pk_identity *id,
699178825Sdfr		     heim_oid *contentType,
700178825Sdfr		     krb5_data *content,
701178825Sdfr		     struct krb5_pk_cert **signer)
702178825Sdfr{
703178825Sdfr    hx509_certs signer_certs;
704178825Sdfr    int ret;
705178825Sdfr
706178825Sdfr    *signer = NULL;
707178825Sdfr
708178825Sdfr    ret = hx509_cms_verify_signed(id->hx509ctx,
709178825Sdfr				  id->verify_ctx,
710178825Sdfr				  data,
711178825Sdfr				  length,
712178825Sdfr				  NULL,
713178825Sdfr				  id->certpool,
714178825Sdfr				  contentType,
715178825Sdfr				  content,
716178825Sdfr				  &signer_certs);
717178825Sdfr    if (ret) {
718178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
719178825Sdfr			    "CMS verify signed failed");
720178825Sdfr	return ret;
721178825Sdfr    }
722178825Sdfr
723178825Sdfr    *signer = calloc(1, sizeof(**signer));
724178825Sdfr    if (*signer == NULL) {
725178825Sdfr	krb5_clear_error_string(context);
726178825Sdfr	ret = ENOMEM;
727178825Sdfr	goto out;
728178825Sdfr    }
729178825Sdfr
730178825Sdfr    ret = hx509_get_one_cert(id->hx509ctx, signer_certs, &(*signer)->cert);
731178825Sdfr    if (ret) {
732178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
733178825Sdfr			    "Failed to get on of the signer certs");
734178825Sdfr	goto out;
735178825Sdfr    }
736178825Sdfr
737178825Sdfrout:
738178825Sdfr    hx509_certs_free(&signer_certs);
739178825Sdfr    if (ret) {
740178825Sdfr	if (*signer) {
741178825Sdfr	    hx509_cert_free((*signer)->cert);
742178825Sdfr	    free(*signer);
743178825Sdfr	    *signer = NULL;
744178825Sdfr	}
745178825Sdfr    }
746178825Sdfr
747178825Sdfr    return ret;
748178825Sdfr}
749178825Sdfr
750178825Sdfrstatic krb5_error_code
751178825Sdfrget_reply_key_win(krb5_context context,
752178825Sdfr		  const krb5_data *content,
753178825Sdfr		  unsigned nonce,
754178825Sdfr		  krb5_keyblock **key)
755178825Sdfr{
756178825Sdfr    ReplyKeyPack_Win2k key_pack;
757178825Sdfr    krb5_error_code ret;
758178825Sdfr    size_t size;
759178825Sdfr
760178825Sdfr    ret = decode_ReplyKeyPack_Win2k(content->data,
761178825Sdfr				    content->length,
762178825Sdfr				    &key_pack,
763178825Sdfr				    &size);
764178825Sdfr    if (ret) {
765178825Sdfr	krb5_set_error_string(context, "PKINIT decoding reply key failed");
766178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
767178825Sdfr	return ret;
768178825Sdfr    }
769178825Sdfr
770178825Sdfr    if (key_pack.nonce != nonce) {
771178825Sdfr	krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
772178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
773178825Sdfr	return KRB5KRB_AP_ERR_MODIFIED;
774178825Sdfr    }
775178825Sdfr
776178825Sdfr    *key = malloc (sizeof (**key));
777178825Sdfr    if (*key == NULL) {
778178825Sdfr	krb5_set_error_string(context, "PKINIT failed allocating reply key");
779178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
780178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
781178825Sdfr	return ENOMEM;
782178825Sdfr    }
783178825Sdfr
784178825Sdfr    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
785178825Sdfr    free_ReplyKeyPack_Win2k(&key_pack);
786178825Sdfr    if (ret) {
787178825Sdfr	krb5_set_error_string(context, "PKINIT failed copying reply key");
788178825Sdfr	free(*key);
789178825Sdfr	*key = NULL;
790178825Sdfr    }
791178825Sdfr
792178825Sdfr    return ret;
793178825Sdfr}
794178825Sdfr
795178825Sdfrstatic krb5_error_code
796178825Sdfrget_reply_key(krb5_context context,
797178825Sdfr	      const krb5_data *content,
798178825Sdfr	      const krb5_data *req_buffer,
799178825Sdfr	      krb5_keyblock **key)
800178825Sdfr{
801178825Sdfr    ReplyKeyPack key_pack;
802178825Sdfr    krb5_error_code ret;
803178825Sdfr    size_t size;
804178825Sdfr
805178825Sdfr    ret = decode_ReplyKeyPack(content->data,
806178825Sdfr			      content->length,
807178825Sdfr			      &key_pack,
808178825Sdfr			      &size);
809178825Sdfr    if (ret) {
810178825Sdfr	krb5_set_error_string(context, "PKINIT decoding reply key failed");
811178825Sdfr	free_ReplyKeyPack(&key_pack);
812178825Sdfr	return ret;
813178825Sdfr    }
814178825Sdfr
815178825Sdfr    {
816178825Sdfr	krb5_crypto crypto;
817178825Sdfr
818178825Sdfr	/*
819178825Sdfr	 * XXX Verify kp.replyKey is a allowed enctype in the
820178825Sdfr	 * configuration file
821178825Sdfr	 */
822178825Sdfr
823178825Sdfr	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
824178825Sdfr	if (ret) {
825178825Sdfr	    free_ReplyKeyPack(&key_pack);
826178825Sdfr	    return ret;
827178825Sdfr	}
828178825Sdfr
829178825Sdfr	ret = krb5_verify_checksum(context, crypto, 6,
830178825Sdfr				   req_buffer->data, req_buffer->length,
831178825Sdfr				   &key_pack.asChecksum);
832178825Sdfr	krb5_crypto_destroy(context, crypto);
833178825Sdfr	if (ret) {
834178825Sdfr	    free_ReplyKeyPack(&key_pack);
835178825Sdfr	    return ret;
836178825Sdfr	}
837178825Sdfr    }
838178825Sdfr
839178825Sdfr    *key = malloc (sizeof (**key));
840178825Sdfr    if (*key == NULL) {
841178825Sdfr	krb5_set_error_string(context, "PKINIT failed allocating reply key");
842178825Sdfr	free_ReplyKeyPack(&key_pack);
843178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
844178825Sdfr	return ENOMEM;
845178825Sdfr    }
846178825Sdfr
847178825Sdfr    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
848178825Sdfr    free_ReplyKeyPack(&key_pack);
849178825Sdfr    if (ret) {
850178825Sdfr	krb5_set_error_string(context, "PKINIT failed copying reply key");
851178825Sdfr	free(*key);
852178825Sdfr	*key = NULL;
853178825Sdfr    }
854178825Sdfr
855178825Sdfr    return ret;
856178825Sdfr}
857178825Sdfr
858178825Sdfr
859178825Sdfrstatic krb5_error_code
860178825Sdfrpk_verify_host(krb5_context context,
861178825Sdfr	       const char *realm,
862178825Sdfr	       const krb5_krbhst_info *hi,
863178825Sdfr	       struct krb5_pk_init_ctx_data *ctx,
864178825Sdfr	       struct krb5_pk_cert *host)
865178825Sdfr{
866178825Sdfr    krb5_error_code ret = 0;
867178825Sdfr
868178825Sdfr    if (ctx->require_eku) {
869178825Sdfr	ret = hx509_cert_check_eku(ctx->id->hx509ctx, host->cert,
870178825Sdfr				   oid_id_pkkdcekuoid(), 0);
871178825Sdfr	if (ret) {
872178825Sdfr	    krb5_set_error_string(context, "No PK-INIT KDC EKU in kdc certificate");
873178825Sdfr	    return ret;
874178825Sdfr	}
875178825Sdfr    }
876178825Sdfr    if (ctx->require_krbtgt_otherName) {
877178825Sdfr	hx509_octet_string_list list;
878178825Sdfr	int i;
879178825Sdfr
880178825Sdfr	ret = hx509_cert_find_subjectAltName_otherName(ctx->id->hx509ctx,
881178825Sdfr						       host->cert,
882178825Sdfr						       oid_id_pkinit_san(),
883178825Sdfr						       &list);
884178825Sdfr	if (ret) {
885178825Sdfr	    krb5_set_error_string(context, "Failed to find the PK-INIT "
886178825Sdfr				  "subjectAltName in the KDC certificate");
887178825Sdfr
888178825Sdfr	    return ret;
889178825Sdfr	}
890178825Sdfr
891178825Sdfr	for (i = 0; i < list.len; i++) {
892178825Sdfr	    KRB5PrincipalName r;
893178825Sdfr
894178825Sdfr	    ret = decode_KRB5PrincipalName(list.val[i].data,
895178825Sdfr					   list.val[i].length,
896178825Sdfr					   &r,
897178825Sdfr					   NULL);
898178825Sdfr	    if (ret) {
899178825Sdfr		krb5_set_error_string(context, "Failed to decode the PK-INIT "
900178825Sdfr				      "subjectAltName in the KDC certificate");
901178825Sdfr
902178825Sdfr		break;
903178825Sdfr	    }
904178825Sdfr
905178825Sdfr	    if (r.principalName.name_string.len != 2 ||
906178825Sdfr		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
907178825Sdfr		strcmp(r.principalName.name_string.val[1], realm) != 0 ||
908178825Sdfr		strcmp(r.realm, realm) != 0)
909178825Sdfr	    {
910178825Sdfr		krb5_set_error_string(context, "KDC have wrong realm name in "
911178825Sdfr				      "the certificate");
912178825Sdfr		ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
913178825Sdfr	    }
914178825Sdfr
915178825Sdfr	    free_KRB5PrincipalName(&r);
916178825Sdfr	    if (ret)
917178825Sdfr		break;
918178825Sdfr	}
919178825Sdfr	hx509_free_octet_string_list(&list);
920178825Sdfr    }
921178825Sdfr    if (ret)
922178825Sdfr	return ret;
923178825Sdfr
924178825Sdfr    if (hi) {
925178825Sdfr	ret = hx509_verify_hostname(ctx->id->hx509ctx, host->cert,
926178825Sdfr				    ctx->require_hostname_match,
927178825Sdfr				    HX509_HN_HOSTNAME,
928178825Sdfr				    hi->hostname,
929178825Sdfr				    hi->ai->ai_addr, hi->ai->ai_addrlen);
930178825Sdfr
931178825Sdfr	if (ret)
932178825Sdfr	    krb5_set_error_string(context, "Address mismatch in "
933178825Sdfr				  "the KDC certificate");
934178825Sdfr    }
935178825Sdfr    return ret;
936178825Sdfr}
937178825Sdfr
938178825Sdfrstatic krb5_error_code
939178825Sdfrpk_rd_pa_reply_enckey(krb5_context context,
940178825Sdfr		      int type,
941178825Sdfr		      const heim_octet_string *indata,
942178825Sdfr		      const heim_oid *dataType,
943178825Sdfr		      const char *realm,
944178825Sdfr		      krb5_pk_init_ctx ctx,
945178825Sdfr		      krb5_enctype etype,
946178825Sdfr		      const krb5_krbhst_info *hi,
947178825Sdfr	       	      unsigned nonce,
948178825Sdfr		      const krb5_data *req_buffer,
949178825Sdfr	       	      PA_DATA *pa,
950178825Sdfr	       	      krb5_keyblock **key)
951178825Sdfr{
952178825Sdfr    krb5_error_code ret;
953178825Sdfr    struct krb5_pk_cert *host = NULL;
954178825Sdfr    krb5_data content;
955178825Sdfr    heim_oid contentType = { 0, NULL };
956178825Sdfr
957178825Sdfr    if (der_heim_oid_cmp(oid_id_pkcs7_envelopedData(), dataType)) {
958178825Sdfr	krb5_set_error_string(context, "PKINIT: Invalid content type");
959178825Sdfr	return EINVAL;
960178825Sdfr    }
961178825Sdfr
962178825Sdfr    ret = hx509_cms_unenvelope(ctx->id->hx509ctx,
963178825Sdfr			       ctx->id->certs,
964178825Sdfr			       HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT,
965178825Sdfr			       indata->data,
966178825Sdfr			       indata->length,
967178825Sdfr			       NULL,
968178825Sdfr			       &contentType,
969178825Sdfr			       &content);
970178825Sdfr    if (ret) {
971178825Sdfr	_krb5_pk_copy_error(context, ctx->id->hx509ctx, ret,
972178825Sdfr			    "Failed to unenvelope CMS data in PK-INIT reply");
973178825Sdfr	return ret;
974178825Sdfr    }
975178825Sdfr    der_free_oid(&contentType);
976178825Sdfr
977178825Sdfr#if 0 /* windows LH with interesting CMS packets, leaks memory */
978178825Sdfr    {
979178825Sdfr	size_t ph = 1 + der_length_len (length);
980178825Sdfr	unsigned char *ptr = malloc(length + ph);
981178825Sdfr	size_t l;
982178825Sdfr
983178825Sdfr	memcpy(ptr + ph, p, length);
984178825Sdfr
985178825Sdfr	ret = der_put_length_and_tag (ptr + ph - 1, ph, length,
986178825Sdfr				      ASN1_C_UNIV, CONS, UT_Sequence, &l);
987178825Sdfr	if (ret)
988178825Sdfr	    return ret;
989178825Sdfr	ptr += ph - l;
990178825Sdfr	length += l;
991178825Sdfr	p = ptr;
992178825Sdfr    }
993178825Sdfr#endif
994178825Sdfr
995178825Sdfr    /* win2k uses ContentInfo */
996178825Sdfr    if (type == COMPAT_WIN2K) {
997178825Sdfr	heim_oid type;
998178825Sdfr	heim_octet_string out;
999178825Sdfr
1000178825Sdfr	ret = hx509_cms_unwrap_ContentInfo(&content, &type, &out, NULL);
1001178825Sdfr	if (der_heim_oid_cmp(&type, oid_id_pkcs7_signedData())) {
1002178825Sdfr	    ret = EINVAL; /* XXX */
1003178825Sdfr	    krb5_set_error_string(context, "PKINIT: Invalid content type");
1004178825Sdfr	    der_free_oid(&type);
1005178825Sdfr	    der_free_octet_string(&out);
1006178825Sdfr	    goto out;
1007178825Sdfr	}
1008178825Sdfr	der_free_oid(&type);
1009178825Sdfr	krb5_data_free(&content);
1010178825Sdfr	ret = krb5_data_copy(&content, out.data, out.length);
1011178825Sdfr	der_free_octet_string(&out);
1012178825Sdfr	if (ret) {
1013178825Sdfr	    krb5_set_error_string(context, "PKINIT: out of memory");
1014178825Sdfr	    goto out;
1015178825Sdfr	}
1016178825Sdfr    }
1017178825Sdfr
1018178825Sdfr    ret = _krb5_pk_verify_sign(context,
1019178825Sdfr			       content.data,
1020178825Sdfr			       content.length,
1021178825Sdfr			       ctx->id,
1022178825Sdfr			       &contentType,
1023178825Sdfr			       &content,
1024178825Sdfr			       &host);
1025178825Sdfr    if (ret)
1026178825Sdfr	goto out;
1027178825Sdfr
1028178825Sdfr    /* make sure that it is the kdc's certificate */
1029178825Sdfr    ret = pk_verify_host(context, realm, hi, ctx, host);
1030178825Sdfr    if (ret) {
1031178825Sdfr	goto out;
1032178825Sdfr    }
1033178825Sdfr
1034178825Sdfr#if 0
1035178825Sdfr    if (type == COMPAT_WIN2K) {
1036178825Sdfr	if (der_heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1037178825Sdfr	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1038178825Sdfr	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1039178825Sdfr	    goto out;
1040178825Sdfr	}
1041178825Sdfr    } else {
1042178825Sdfr	if (der_heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1043178825Sdfr	    krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1044178825Sdfr	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1045178825Sdfr	    goto out;
1046178825Sdfr	}
1047178825Sdfr    }
1048178825Sdfr#endif
1049178825Sdfr
1050178825Sdfr    switch(type) {
1051178825Sdfr    case COMPAT_WIN2K:
1052178825Sdfr	ret = get_reply_key(context, &content, req_buffer, key);
1053178825Sdfr	if (ret != 0 && ctx->require_binding == 0)
1054178825Sdfr	    ret = get_reply_key_win(context, &content, nonce, key);
1055178825Sdfr	break;
1056178825Sdfr    case COMPAT_IETF:
1057178825Sdfr	ret = get_reply_key(context, &content, req_buffer, key);
1058178825Sdfr	break;
1059178825Sdfr    }
1060178825Sdfr    if (ret)
1061178825Sdfr	goto out;
1062178825Sdfr
1063178825Sdfr    /* XXX compare given etype with key->etype */
1064178825Sdfr
1065178825Sdfr out:
1066178825Sdfr    if (host)
1067178825Sdfr	_krb5_pk_cert_free(host);
1068178825Sdfr    der_free_oid(&contentType);
1069178825Sdfr    krb5_data_free(&content);
1070178825Sdfr
1071178825Sdfr    return ret;
1072178825Sdfr}
1073178825Sdfr
1074178825Sdfrstatic krb5_error_code
1075178825Sdfrpk_rd_pa_reply_dh(krb5_context context,
1076178825Sdfr		  const heim_octet_string *indata,
1077178825Sdfr		  const heim_oid *dataType,
1078178825Sdfr		  const char *realm,
1079178825Sdfr		  krb5_pk_init_ctx ctx,
1080178825Sdfr		  krb5_enctype etype,
1081178825Sdfr		  const krb5_krbhst_info *hi,
1082178825Sdfr		  const DHNonce *c_n,
1083178825Sdfr		  const DHNonce *k_n,
1084178825Sdfr                  unsigned nonce,
1085178825Sdfr                  PA_DATA *pa,
1086178825Sdfr                  krb5_keyblock **key)
1087178825Sdfr{
1088178825Sdfr    unsigned char *p, *dh_gen_key = NULL;
1089178825Sdfr    struct krb5_pk_cert *host = NULL;
1090178825Sdfr    BIGNUM *kdc_dh_pubkey = NULL;
1091178825Sdfr    KDCDHKeyInfo kdc_dh_info;
1092178825Sdfr    heim_oid contentType = { 0, NULL };
1093178825Sdfr    krb5_data content;
1094178825Sdfr    krb5_error_code ret;
1095178825Sdfr    int dh_gen_keylen;
1096178825Sdfr    size_t size;
1097178825Sdfr
1098178825Sdfr    krb5_data_zero(&content);
1099178825Sdfr    memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1100178825Sdfr
1101178825Sdfr    if (der_heim_oid_cmp(oid_id_pkcs7_signedData(), dataType)) {
1102178825Sdfr	krb5_set_error_string(context, "PKINIT: Invalid content type");
1103178825Sdfr	return EINVAL;
1104178825Sdfr    }
1105178825Sdfr
1106178825Sdfr    ret = _krb5_pk_verify_sign(context,
1107178825Sdfr			       indata->data,
1108178825Sdfr			       indata->length,
1109178825Sdfr			       ctx->id,
1110178825Sdfr			       &contentType,
1111178825Sdfr			       &content,
1112178825Sdfr			       &host);
1113178825Sdfr    if (ret)
1114178825Sdfr	goto out;
1115178825Sdfr
1116178825Sdfr    /* make sure that it is the kdc's certificate */
1117178825Sdfr    ret = pk_verify_host(context, realm, hi, ctx, host);
1118178825Sdfr    if (ret)
1119178825Sdfr	goto out;
1120178825Sdfr
1121178825Sdfr    if (der_heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1122178825Sdfr	krb5_set_error_string(context, "pkinit - dh reply contains wrong oid");
1123178825Sdfr	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1124178825Sdfr	goto out;
1125178825Sdfr    }
1126178825Sdfr
1127178825Sdfr    ret = decode_KDCDHKeyInfo(content.data,
1128178825Sdfr			      content.length,
1129178825Sdfr			      &kdc_dh_info,
1130178825Sdfr			      &size);
1131178825Sdfr
1132178825Sdfr    if (ret) {
1133178825Sdfr	krb5_set_error_string(context, "pkinit - "
1134178825Sdfr			      "failed to decode KDC DH Key Info");
1135178825Sdfr	goto out;
1136178825Sdfr    }
1137178825Sdfr
1138178825Sdfr    if (kdc_dh_info.nonce != nonce) {
1139178825Sdfr	krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1140178825Sdfr	ret = KRB5KRB_AP_ERR_MODIFIED;
1141178825Sdfr	goto out;
1142178825Sdfr    }
1143178825Sdfr
1144178825Sdfr    if (kdc_dh_info.dhKeyExpiration) {
1145178825Sdfr	if (k_n == NULL) {
1146178825Sdfr	    krb5_set_error_string(context, "pkinit; got key expiration "
1147178825Sdfr				  "without server nonce");
1148178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1149178825Sdfr	    goto out;
1150178825Sdfr	}
1151178825Sdfr	if (c_n == NULL) {
1152178825Sdfr	    krb5_set_error_string(context, "pkinit; got DH reuse but no "
1153178825Sdfr				  "client nonce");
1154178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1155178825Sdfr	    goto out;
1156178825Sdfr	}
1157178825Sdfr    } else {
1158178825Sdfr	if (k_n) {
1159178825Sdfr	    krb5_set_error_string(context, "pkinit: got server nonce "
1160178825Sdfr				  "without key expiration");
1161178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1162178825Sdfr	    goto out;
1163178825Sdfr	}
1164178825Sdfr	c_n = NULL;
1165178825Sdfr    }
1166178825Sdfr
1167178825Sdfr
1168178825Sdfr    p = kdc_dh_info.subjectPublicKey.data;
1169178825Sdfr    size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1170178825Sdfr
1171178825Sdfr    {
1172178825Sdfr	DHPublicKey k;
1173178825Sdfr	ret = decode_DHPublicKey(p, size, &k, NULL);
1174178825Sdfr	if (ret) {
1175178825Sdfr	    krb5_set_error_string(context, "pkinit: can't decode "
1176178825Sdfr				  "without key expiration");
1177178825Sdfr	    goto out;
1178178825Sdfr	}
1179178825Sdfr
1180178825Sdfr	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1181178825Sdfr	free_DHPublicKey(&k);
1182178825Sdfr	if (kdc_dh_pubkey == NULL) {
1183178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1184178825Sdfr	    goto out;
1185178825Sdfr	}
1186178825Sdfr    }
1187178825Sdfr
1188178825Sdfr    dh_gen_keylen = DH_size(ctx->dh);
1189178825Sdfr    size = BN_num_bytes(ctx->dh->p);
1190178825Sdfr    if (size < dh_gen_keylen)
1191178825Sdfr	size = dh_gen_keylen;
1192178825Sdfr
1193178825Sdfr    dh_gen_key = malloc(size);
1194178825Sdfr    if (dh_gen_key == NULL) {
1195178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1196178825Sdfr	ret = ENOMEM;
1197178825Sdfr	goto out;
1198178825Sdfr    }
1199178825Sdfr    memset(dh_gen_key, 0, size - dh_gen_keylen);
1200178825Sdfr
1201178825Sdfr    dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
1202178825Sdfr				   kdc_dh_pubkey, ctx->dh);
1203178825Sdfr    if (dh_gen_keylen == -1) {
1204178825Sdfr	krb5_set_error_string(context,
1205178825Sdfr			      "PKINIT: Can't compute Diffie-Hellman key");
1206178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
1207178825Sdfr	goto out;
1208178825Sdfr    }
1209178825Sdfr
1210178825Sdfr    *key = malloc (sizeof (**key));
1211178825Sdfr    if (*key == NULL) {
1212178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1213178825Sdfr	ret = ENOMEM;
1214178825Sdfr	goto out;
1215178825Sdfr    }
1216178825Sdfr
1217178825Sdfr    ret = _krb5_pk_octetstring2key(context,
1218178825Sdfr				   etype,
1219178825Sdfr				   dh_gen_key, dh_gen_keylen,
1220178825Sdfr				   c_n, k_n,
1221178825Sdfr				   *key);
1222178825Sdfr    if (ret) {
1223178825Sdfr	krb5_set_error_string(context,
1224178825Sdfr			      "PKINIT: can't create key from DH key");
1225178825Sdfr	free(*key);
1226178825Sdfr	*key = NULL;
1227178825Sdfr	goto out;
1228178825Sdfr    }
1229178825Sdfr
1230178825Sdfr out:
1231178825Sdfr    if (kdc_dh_pubkey)
1232178825Sdfr	BN_free(kdc_dh_pubkey);
1233178825Sdfr    if (dh_gen_key) {
1234178825Sdfr	memset(dh_gen_key, 0, DH_size(ctx->dh));
1235178825Sdfr	free(dh_gen_key);
1236178825Sdfr    }
1237178825Sdfr    if (host)
1238178825Sdfr	_krb5_pk_cert_free(host);
1239178825Sdfr    if (content.data)
1240178825Sdfr	krb5_data_free(&content);
1241178825Sdfr    der_free_oid(&contentType);
1242178825Sdfr    free_KDCDHKeyInfo(&kdc_dh_info);
1243178825Sdfr
1244178825Sdfr    return ret;
1245178825Sdfr}
1246178825Sdfr
1247178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1248178825Sdfr_krb5_pk_rd_pa_reply(krb5_context context,
1249178825Sdfr		     const char *realm,
1250178825Sdfr		     void *c,
1251178825Sdfr		     krb5_enctype etype,
1252178825Sdfr		     const krb5_krbhst_info *hi,
1253178825Sdfr		     unsigned nonce,
1254178825Sdfr		     const krb5_data *req_buffer,
1255178825Sdfr		     PA_DATA *pa,
1256178825Sdfr		     krb5_keyblock **key)
1257178825Sdfr{
1258178825Sdfr    krb5_pk_init_ctx ctx = c;
1259178825Sdfr    krb5_error_code ret;
1260178825Sdfr    size_t size;
1261178825Sdfr
1262178825Sdfr    /* Check for IETF PK-INIT first */
1263178825Sdfr    if (ctx->type == COMPAT_IETF) {
1264178825Sdfr	PA_PK_AS_REP rep;
1265178825Sdfr	heim_octet_string os, data;
1266178825Sdfr	heim_oid oid;
1267178825Sdfr
1268178825Sdfr	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1269178825Sdfr	    krb5_set_error_string(context, "PKINIT: wrong padata recv");
1270178825Sdfr	    return EINVAL;
1271178825Sdfr	}
1272178825Sdfr
1273178825Sdfr	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1274178825Sdfr				  pa->padata_value.length,
1275178825Sdfr				  &rep,
1276178825Sdfr				  &size);
1277178825Sdfr	if (ret) {
1278178825Sdfr	    krb5_set_error_string(context, "Failed to decode pkinit AS rep");
1279178825Sdfr	    return ret;
1280178825Sdfr	}
1281178825Sdfr
1282178825Sdfr	switch (rep.element) {
1283178825Sdfr	case choice_PA_PK_AS_REP_dhInfo:
1284178825Sdfr	    os = rep.u.dhInfo.dhSignedData;
1285178825Sdfr	    break;
1286178825Sdfr	case choice_PA_PK_AS_REP_encKeyPack:
1287178825Sdfr	    os = rep.u.encKeyPack;
1288178825Sdfr	    break;
1289178825Sdfr	default:
1290178825Sdfr	    free_PA_PK_AS_REP(&rep);
1291178825Sdfr	    krb5_set_error_string(context, "PKINIT: -27 reply "
1292178825Sdfr				  "invalid content type");
1293178825Sdfr	    return EINVAL;
1294178825Sdfr	}
1295178825Sdfr
1296178825Sdfr	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1297178825Sdfr	if (ret) {
1298178825Sdfr	    free_PA_PK_AS_REP(&rep);
1299178825Sdfr	    krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1300178825Sdfr	    return ret;
1301178825Sdfr	}
1302178825Sdfr
1303178825Sdfr	switch (rep.element) {
1304178825Sdfr	case choice_PA_PK_AS_REP_dhInfo:
1305178825Sdfr	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1306178825Sdfr				    ctx->clientDHNonce,
1307178825Sdfr				    rep.u.dhInfo.serverDHNonce,
1308178825Sdfr				    nonce, pa, key);
1309178825Sdfr	    break;
1310178825Sdfr	case choice_PA_PK_AS_REP_encKeyPack:
1311178825Sdfr	    ret = pk_rd_pa_reply_enckey(context, COMPAT_IETF, &data, &oid, realm,
1312178825Sdfr					ctx, etype, hi, nonce, req_buffer, pa, key);
1313178825Sdfr	    break;
1314178825Sdfr	default:
1315178825Sdfr	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1316178825Sdfr	}
1317178825Sdfr	der_free_octet_string(&data);
1318178825Sdfr	der_free_oid(&oid);
1319178825Sdfr	free_PA_PK_AS_REP(&rep);
1320178825Sdfr
1321178825Sdfr    } else if (ctx->type == COMPAT_WIN2K) {
1322178825Sdfr	PA_PK_AS_REP_Win2k w2krep;
1323178825Sdfr
1324178825Sdfr	/* Check for Windows encoding of the AS-REP pa data */
1325178825Sdfr
1326178825Sdfr#if 0 /* should this be ? */
1327178825Sdfr	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1328178825Sdfr	    krb5_set_error_string(context, "PKINIT: wrong padata recv");
1329178825Sdfr	    return EINVAL;
1330178825Sdfr	}
1331178825Sdfr#endif
1332178825Sdfr
1333178825Sdfr	memset(&w2krep, 0, sizeof(w2krep));
1334178825Sdfr
1335178825Sdfr	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1336178825Sdfr					pa->padata_value.length,
1337178825Sdfr					&w2krep,
1338178825Sdfr					&size);
1339178825Sdfr	if (ret) {
1340178825Sdfr	    krb5_set_error_string(context, "PKINIT: Failed decoding windows "
1341178825Sdfr				  "pkinit reply %d", ret);
1342178825Sdfr	    return ret;
1343178825Sdfr	}
1344178825Sdfr
1345178825Sdfr	krb5_clear_error_string(context);
1346178825Sdfr
1347178825Sdfr	switch (w2krep.element) {
1348178825Sdfr	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1349178825Sdfr	    heim_octet_string data;
1350178825Sdfr	    heim_oid oid;
1351178825Sdfr
1352178825Sdfr	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1353178825Sdfr					       &oid, &data, NULL);
1354178825Sdfr	    free_PA_PK_AS_REP_Win2k(&w2krep);
1355178825Sdfr	    if (ret) {
1356178825Sdfr		krb5_set_error_string(context, "PKINIT: failed to unwrap CI");
1357178825Sdfr		return ret;
1358178825Sdfr	    }
1359178825Sdfr
1360178825Sdfr	    ret = pk_rd_pa_reply_enckey(context, COMPAT_WIN2K, &data, &oid, realm,
1361178825Sdfr					ctx, etype, hi, nonce, req_buffer, pa, key);
1362178825Sdfr	    der_free_octet_string(&data);
1363178825Sdfr	    der_free_oid(&oid);
1364178825Sdfr
1365178825Sdfr	    break;
1366178825Sdfr	}
1367178825Sdfr	default:
1368178825Sdfr	    free_PA_PK_AS_REP_Win2k(&w2krep);
1369178825Sdfr	    krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1370178825Sdfr				  "content type");
1371178825Sdfr	    ret = EINVAL;
1372178825Sdfr	    break;
1373178825Sdfr	}
1374178825Sdfr
1375178825Sdfr    } else {
1376178825Sdfr	krb5_set_error_string(context, "PKINIT: unknown reply type");
1377178825Sdfr	ret = EINVAL;
1378178825Sdfr    }
1379178825Sdfr
1380178825Sdfr    return ret;
1381178825Sdfr}
1382178825Sdfr
1383178825Sdfrstruct prompter {
1384178825Sdfr    krb5_context context;
1385178825Sdfr    krb5_prompter_fct prompter;
1386178825Sdfr    void *prompter_data;
1387178825Sdfr};
1388178825Sdfr
1389178825Sdfrstatic int
1390178825Sdfrhx_pass_prompter(void *data, const hx509_prompt *prompter)
1391178825Sdfr{
1392178825Sdfr    krb5_error_code ret;
1393178825Sdfr    krb5_prompt prompt;
1394178825Sdfr    krb5_data password_data;
1395178825Sdfr    struct prompter *p = data;
1396178825Sdfr
1397178825Sdfr    password_data.data   = prompter->reply.data;
1398178825Sdfr    password_data.length = prompter->reply.length;
1399178825Sdfr
1400178825Sdfr    prompt.prompt = prompter->prompt;
1401178825Sdfr    prompt.hidden = hx509_prompt_hidden(prompter->type);
1402178825Sdfr    prompt.reply  = &password_data;
1403178825Sdfr
1404178825Sdfr    switch (prompter->type) {
1405178825Sdfr    case HX509_PROMPT_TYPE_INFO:
1406178825Sdfr	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1407178825Sdfr	break;
1408178825Sdfr    case HX509_PROMPT_TYPE_PASSWORD:
1409178825Sdfr    case HX509_PROMPT_TYPE_QUESTION:
1410178825Sdfr    default:
1411178825Sdfr	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1412178825Sdfr	break;
1413178825Sdfr    }
1414178825Sdfr
1415178825Sdfr    ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1416178825Sdfr    if (ret) {
1417178825Sdfr	memset (prompter->reply.data, 0, prompter->reply.length);
1418178825Sdfr	return 1;
1419178825Sdfr    }
1420178825Sdfr    return 0;
1421178825Sdfr}
1422178825Sdfr
1423178825Sdfr
1424178825Sdfrvoid KRB5_LIB_FUNCTION
1425178825Sdfr_krb5_pk_allow_proxy_certificate(struct krb5_pk_identity *id,
1426178825Sdfr				 int boolean)
1427178825Sdfr{
1428178825Sdfr    hx509_verify_set_proxy_certificate(id->verify_ctx, boolean);
1429178825Sdfr}
1430178825Sdfr
1431178825Sdfr
1432178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1433178825Sdfr_krb5_pk_load_id(krb5_context context,
1434178825Sdfr		 struct krb5_pk_identity **ret_id,
1435178825Sdfr		 const char *user_id,
1436178825Sdfr		 const char *anchor_id,
1437178825Sdfr		 char * const *chain_list,
1438178825Sdfr		 char * const *revoke_list,
1439178825Sdfr		 krb5_prompter_fct prompter,
1440178825Sdfr		 void *prompter_data,
1441178825Sdfr		 char *password)
1442178825Sdfr{
1443178825Sdfr    struct krb5_pk_identity *id = NULL;
1444178825Sdfr    hx509_lock lock = NULL;
1445178825Sdfr    struct prompter p;
1446178825Sdfr    int ret;
1447178825Sdfr
1448178825Sdfr    *ret_id = NULL;
1449178825Sdfr
1450178825Sdfr    if (anchor_id == NULL) {
1451178825Sdfr	krb5_set_error_string(context, "PKINIT: No anchor given");
1452178825Sdfr	return HEIM_PKINIT_NO_VALID_CA;
1453178825Sdfr    }
1454178825Sdfr
1455178825Sdfr    if (user_id == NULL) {
1456178825Sdfr	krb5_set_error_string(context,
1457178825Sdfr			      "PKINIT: No user certificate given");
1458178825Sdfr	return HEIM_PKINIT_NO_PRIVATE_KEY;
1459178825Sdfr    }
1460178825Sdfr
1461178825Sdfr    /* load cert */
1462178825Sdfr
1463178825Sdfr    id = calloc(1, sizeof(*id));
1464178825Sdfr    if (id == NULL) {
1465178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1466178825Sdfr	return ENOMEM;
1467178825Sdfr    }
1468178825Sdfr
1469178825Sdfr    ret = hx509_context_init(&id->hx509ctx);
1470178825Sdfr    if (ret)
1471178825Sdfr	goto out;
1472178825Sdfr
1473178825Sdfr    ret = hx509_lock_init(id->hx509ctx, &lock);
1474178825Sdfr    if (password && password[0])
1475178825Sdfr	hx509_lock_add_password(lock, password);
1476178825Sdfr
1477178825Sdfr    if (prompter) {
1478178825Sdfr	p.context = context;
1479178825Sdfr	p.prompter = prompter;
1480178825Sdfr	p.prompter_data = prompter_data;
1481178825Sdfr
1482178825Sdfr	ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1483178825Sdfr	if (ret)
1484178825Sdfr	    goto out;
1485178825Sdfr    }
1486178825Sdfr
1487178825Sdfr    ret = hx509_certs_init(id->hx509ctx, user_id, 0, lock, &id->certs);
1488178825Sdfr    if (ret) {
1489178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1490178825Sdfr			    "Failed to init cert certs");
1491178825Sdfr	goto out;
1492178825Sdfr    }
1493178825Sdfr
1494178825Sdfr    ret = hx509_certs_init(id->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1495178825Sdfr    if (ret) {
1496178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1497178825Sdfr			    "Failed to init anchors");
1498178825Sdfr	goto out;
1499178825Sdfr    }
1500178825Sdfr
1501178825Sdfr    ret = hx509_certs_init(id->hx509ctx, "MEMORY:pkinit-cert-chain",
1502178825Sdfr			   0, NULL, &id->certpool);
1503178825Sdfr    if (ret) {
1504178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1505178825Sdfr			    "Failed to init chain");
1506178825Sdfr	goto out;
1507178825Sdfr    }
1508178825Sdfr
1509178825Sdfr    while (chain_list && *chain_list) {
1510178825Sdfr	ret = hx509_certs_append(id->hx509ctx, id->certpool,
1511178825Sdfr				 NULL, *chain_list);
1512178825Sdfr	if (ret) {
1513178825Sdfr	    _krb5_pk_copy_error(context, id->hx509ctx, ret,
1514178825Sdfr				"Failed to laod chain %s",
1515178825Sdfr				*chain_list);
1516178825Sdfr	    goto out;
1517178825Sdfr	}
1518178825Sdfr	chain_list++;
1519178825Sdfr    }
1520178825Sdfr
1521178825Sdfr    if (revoke_list) {
1522178825Sdfr	ret = hx509_revoke_init(id->hx509ctx, &id->revokectx);
1523178825Sdfr	if (ret) {
1524178825Sdfr	    _krb5_pk_copy_error(context, id->hx509ctx, ret,
1525178825Sdfr				"Failed init revoke list");
1526178825Sdfr	    goto out;
1527178825Sdfr	}
1528178825Sdfr
1529178825Sdfr	while (*revoke_list) {
1530178825Sdfr	    ret = hx509_revoke_add_crl(id->hx509ctx,
1531178825Sdfr				       id->revokectx,
1532178825Sdfr				       *revoke_list);
1533178825Sdfr	    if (ret) {
1534178825Sdfr		_krb5_pk_copy_error(context, id->hx509ctx, ret,
1535178825Sdfr				    "Failed load revoke list");
1536178825Sdfr		goto out;
1537178825Sdfr	    }
1538178825Sdfr	    revoke_list++;
1539178825Sdfr	}
1540178825Sdfr    } else
1541178825Sdfr	hx509_context_set_missing_revoke(id->hx509ctx, 1);
1542178825Sdfr
1543178825Sdfr    ret = hx509_verify_init_ctx(id->hx509ctx, &id->verify_ctx);
1544178825Sdfr    if (ret) {
1545178825Sdfr	_krb5_pk_copy_error(context, id->hx509ctx, ret,
1546178825Sdfr			    "Failed init verify context");
1547178825Sdfr	goto out;
1548178825Sdfr    }
1549178825Sdfr
1550178825Sdfr    hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1551178825Sdfr    hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1552178825Sdfr
1553178825Sdfrout:
1554178825Sdfr    if (ret) {
1555178825Sdfr	hx509_verify_destroy_ctx(id->verify_ctx);
1556178825Sdfr	hx509_certs_free(&id->certs);
1557178825Sdfr	hx509_certs_free(&id->anchors);
1558178825Sdfr	hx509_certs_free(&id->certpool);
1559178825Sdfr	hx509_revoke_free(&id->revokectx);
1560178825Sdfr	hx509_context_free(&id->hx509ctx);
1561178825Sdfr	free(id);
1562178825Sdfr    } else
1563178825Sdfr	*ret_id = id;
1564178825Sdfr
1565178825Sdfr    hx509_lock_free(lock);
1566178825Sdfr
1567178825Sdfr    return ret;
1568178825Sdfr}
1569178825Sdfr
1570178825Sdfrstatic krb5_error_code
1571178825Sdfrselect_dh_group(krb5_context context, DH *dh, unsigned long bits,
1572178825Sdfr		struct krb5_dh_moduli **moduli)
1573178825Sdfr{
1574178825Sdfr    const struct krb5_dh_moduli *m;
1575178825Sdfr
1576178825Sdfr    if (bits == 0) {
1577178825Sdfr	m = moduli[1]; /* XXX */
1578178825Sdfr	if (m == NULL)
1579178825Sdfr	    m = moduli[0]; /* XXX */
1580178825Sdfr    } else {
1581178825Sdfr	int i;
1582178825Sdfr	for (i = 0; moduli[i] != NULL; i++) {
1583178825Sdfr	    if (bits < moduli[i]->bits)
1584178825Sdfr		break;
1585178825Sdfr	}
1586178825Sdfr	if (moduli[i] == NULL) {
1587178825Sdfr	    krb5_set_error_string(context,
1588178825Sdfr				  "Did not find a DH group parameter "
1589178825Sdfr				  "matching requirement of %lu bits",
1590178825Sdfr				  bits);
1591178825Sdfr	    return EINVAL;
1592178825Sdfr	}
1593178825Sdfr	m = moduli[i];
1594178825Sdfr    }
1595178825Sdfr
1596178825Sdfr    dh->p = integer_to_BN(context, "p", &m->p);
1597178825Sdfr    if (dh->p == NULL)
1598178825Sdfr	return ENOMEM;
1599178825Sdfr    dh->g = integer_to_BN(context, "g", &m->g);
1600178825Sdfr    if (dh->g == NULL)
1601178825Sdfr	return ENOMEM;
1602178825Sdfr    dh->q = integer_to_BN(context, "q", &m->q);
1603178825Sdfr    if (dh->q == NULL)
1604178825Sdfr	return ENOMEM;
1605178825Sdfr
1606178825Sdfr    return 0;
1607178825Sdfr}
1608178825Sdfr
1609178825Sdfr#endif /* PKINIT */
1610178825Sdfr
1611178825Sdfrstatic int
1612178825Sdfrparse_integer(krb5_context context, char **p, const char *file, int lineno,
1613178825Sdfr	      const char *name, heim_integer *integer)
1614178825Sdfr{
1615178825Sdfr    int ret;
1616178825Sdfr    char *p1;
1617178825Sdfr    p1 = strsep(p, " \t");
1618178825Sdfr    if (p1 == NULL) {
1619178825Sdfr	krb5_set_error_string(context, "moduli file %s missing %s on line %d",
1620178825Sdfr			      file, name, lineno);
1621178825Sdfr	return EINVAL;
1622178825Sdfr    }
1623178825Sdfr    ret = der_parse_hex_heim_integer(p1, integer);
1624178825Sdfr    if (ret) {
1625178825Sdfr	krb5_set_error_string(context, "moduli file %s failed parsing %s "
1626178825Sdfr			      "on line %d",
1627178825Sdfr			      file, name, lineno);
1628178825Sdfr	return ret;
1629178825Sdfr    }
1630178825Sdfr
1631178825Sdfr    return 0;
1632178825Sdfr}
1633178825Sdfr
1634178825Sdfrkrb5_error_code
1635178825Sdfr_krb5_parse_moduli_line(krb5_context context,
1636178825Sdfr			const char *file,
1637178825Sdfr			int lineno,
1638178825Sdfr			char *p,
1639178825Sdfr			struct krb5_dh_moduli **m)
1640178825Sdfr{
1641178825Sdfr    struct krb5_dh_moduli *m1;
1642178825Sdfr    char *p1;
1643178825Sdfr    int ret;
1644178825Sdfr
1645178825Sdfr    *m = NULL;
1646178825Sdfr
1647178825Sdfr    m1 = calloc(1, sizeof(*m1));
1648178825Sdfr    if (m1 == NULL) {
1649178825Sdfr	krb5_set_error_string(context, "malloc - out of memory");
1650178825Sdfr	return ENOMEM;
1651178825Sdfr    }
1652178825Sdfr
1653178825Sdfr    while (isspace((unsigned char)*p))
1654178825Sdfr	p++;
1655178825Sdfr    if (*p  == '#')
1656178825Sdfr	return 0;
1657178825Sdfr    ret = EINVAL;
1658178825Sdfr
1659178825Sdfr    p1 = strsep(&p, " \t");
1660178825Sdfr    if (p1 == NULL) {
1661178825Sdfr	krb5_set_error_string(context, "moduli file %s missing name "
1662178825Sdfr			      "on line %d", file, lineno);
1663178825Sdfr	goto out;
1664178825Sdfr    }
1665178825Sdfr    m1->name = strdup(p1);
1666178825Sdfr    if (p1 == NULL) {
1667178825Sdfr	krb5_set_error_string(context, "malloc - out of memeory");
1668178825Sdfr	ret = ENOMEM;
1669178825Sdfr	goto out;
1670178825Sdfr    }
1671178825Sdfr
1672178825Sdfr    p1 = strsep(&p, " \t");
1673178825Sdfr    if (p1 == NULL) {
1674178825Sdfr	krb5_set_error_string(context, "moduli file %s missing bits on line %d",
1675178825Sdfr			      file, lineno);
1676178825Sdfr	goto out;
1677178825Sdfr    }
1678178825Sdfr
1679178825Sdfr    m1->bits = atoi(p1);
1680178825Sdfr    if (m1->bits == 0) {
1681178825Sdfr	krb5_set_error_string(context, "moduli file %s have un-parsable "
1682178825Sdfr			      "bits on line %d", file, lineno);
1683178825Sdfr	goto out;
1684178825Sdfr    }
1685178825Sdfr
1686178825Sdfr    ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
1687178825Sdfr    if (ret)
1688178825Sdfr	goto out;
1689178825Sdfr    ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
1690178825Sdfr    if (ret)
1691178825Sdfr	goto out;
1692178825Sdfr    ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
1693178825Sdfr    if (ret)
1694178825Sdfr	goto out;
1695178825Sdfr
1696178825Sdfr    *m = m1;
1697178825Sdfr
1698178825Sdfr    return 0;
1699178825Sdfrout:
1700178825Sdfr    free(m1->name);
1701178825Sdfr    der_free_heim_integer(&m1->p);
1702178825Sdfr    der_free_heim_integer(&m1->g);
1703178825Sdfr    der_free_heim_integer(&m1->q);
1704178825Sdfr    free(m1);
1705178825Sdfr    return ret;
1706178825Sdfr}
1707178825Sdfr
1708178825Sdfrvoid
1709178825Sdfr_krb5_free_moduli(struct krb5_dh_moduli **moduli)
1710178825Sdfr{
1711178825Sdfr    int i;
1712178825Sdfr    for (i = 0; moduli[i] != NULL; i++) {
1713178825Sdfr	free(moduli[i]->name);
1714178825Sdfr	der_free_heim_integer(&moduli[i]->p);
1715178825Sdfr	der_free_heim_integer(&moduli[i]->g);
1716178825Sdfr	der_free_heim_integer(&moduli[i]->q);
1717178825Sdfr	free(moduli[i]);
1718178825Sdfr    }
1719178825Sdfr    free(moduli);
1720178825Sdfr}
1721178825Sdfr
1722178825Sdfrstatic const char *default_moduli_RFC2412_MODP_group2 =
1723178825Sdfr    /* name */
1724178825Sdfr    "RFC2412-MODP-group2 "
1725178825Sdfr    /* bits */
1726178825Sdfr    "1024 "
1727178825Sdfr    /* p */
1728178825Sdfr    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1729178825Sdfr    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1730178825Sdfr    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1731178825Sdfr    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1732178825Sdfr    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
1733178825Sdfr    "FFFFFFFF" "FFFFFFFF "
1734178825Sdfr    /* g */
1735178825Sdfr    "02 "
1736178825Sdfr    /* q */
1737178825Sdfr    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1738178825Sdfr    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1739178825Sdfr    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1740178825Sdfr    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1741178825Sdfr    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
1742178825Sdfr    "FFFFFFFF" "FFFFFFFF";
1743178825Sdfr
1744178825Sdfrstatic const char *default_moduli_rfc3526_MODP_group14 =
1745178825Sdfr    /* name */
1746178825Sdfr    "rfc3526-MODP-group14 "
1747178825Sdfr    /* bits */
1748178825Sdfr    "1760 "
1749178825Sdfr    /* p */
1750178825Sdfr    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
1751178825Sdfr    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
1752178825Sdfr    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
1753178825Sdfr    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
1754178825Sdfr    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
1755178825Sdfr    "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
1756178825Sdfr    "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
1757178825Sdfr    "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
1758178825Sdfr    "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
1759178825Sdfr    "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
1760178825Sdfr    "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
1761178825Sdfr    /* g */
1762178825Sdfr    "02 "
1763178825Sdfr    /* q */
1764178825Sdfr    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
1765178825Sdfr    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
1766178825Sdfr    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
1767178825Sdfr    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
1768178825Sdfr    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
1769178825Sdfr    "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
1770178825Sdfr    "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
1771178825Sdfr    "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
1772178825Sdfr    "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
1773178825Sdfr    "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
1774178825Sdfr    "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
1775178825Sdfr
1776178825Sdfrkrb5_error_code
1777178825Sdfr_krb5_parse_moduli(krb5_context context, const char *file,
1778178825Sdfr		   struct krb5_dh_moduli ***moduli)
1779178825Sdfr{
1780178825Sdfr    /* name bits P G Q */
1781178825Sdfr    krb5_error_code ret;
1782178825Sdfr    struct krb5_dh_moduli **m = NULL, **m2;
1783178825Sdfr    char buf[4096];
1784178825Sdfr    FILE *f;
1785178825Sdfr    int lineno = 0, n = 0;
1786178825Sdfr
1787178825Sdfr    *moduli = NULL;
1788178825Sdfr
1789178825Sdfr    m = calloc(1, sizeof(m[0]) * 3);
1790178825Sdfr    if (m == NULL) {
1791178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1792178825Sdfr	return ENOMEM;
1793178825Sdfr    }
1794178825Sdfr
1795178825Sdfr    strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
1796178825Sdfr    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
1797178825Sdfr    if (ret) {
1798178825Sdfr	_krb5_free_moduli(m);
1799178825Sdfr	return ret;
1800178825Sdfr    }
1801178825Sdfr    n++;
1802178825Sdfr
1803178825Sdfr    strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
1804178825Sdfr    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
1805178825Sdfr    if (ret) {
1806178825Sdfr	_krb5_free_moduli(m);
1807178825Sdfr	return ret;
1808178825Sdfr    }
1809178825Sdfr    n++;
1810178825Sdfr
1811178825Sdfr
1812178825Sdfr    if (file == NULL)
1813178825Sdfr	file = MODULI_FILE;
1814178825Sdfr
1815178825Sdfr    f = fopen(file, "r");
1816178825Sdfr    if (f == NULL) {
1817178825Sdfr	*moduli = m;
1818178825Sdfr	return 0;
1819178825Sdfr    }
1820178825Sdfr
1821178825Sdfr    while(fgets(buf, sizeof(buf), f) != NULL) {
1822178825Sdfr	struct krb5_dh_moduli *element;
1823178825Sdfr
1824178825Sdfr	buf[strcspn(buf, "\n")] = '\0';
1825178825Sdfr	lineno++;
1826178825Sdfr
1827178825Sdfr	m2 = realloc(m, (n + 2) * sizeof(m[0]));
1828178825Sdfr	if (m2 == NULL) {
1829178825Sdfr	    krb5_set_error_string(context, "malloc: out of memory");
1830178825Sdfr	    _krb5_free_moduli(m);
1831178825Sdfr	    return ENOMEM;
1832178825Sdfr	}
1833178825Sdfr	m = m2;
1834178825Sdfr
1835178825Sdfr	m[n] = NULL;
1836178825Sdfr
1837178825Sdfr	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
1838178825Sdfr	if (ret) {
1839178825Sdfr	    _krb5_free_moduli(m);
1840178825Sdfr	    return ret;
1841178825Sdfr	}
1842178825Sdfr	if (element == NULL)
1843178825Sdfr	    continue;
1844178825Sdfr
1845178825Sdfr	m[n] = element;
1846178825Sdfr	m[n + 1] = NULL;
1847178825Sdfr	n++;
1848178825Sdfr    }
1849178825Sdfr    *moduli = m;
1850178825Sdfr    return 0;
1851178825Sdfr}
1852178825Sdfr
1853178825Sdfrkrb5_error_code
1854178825Sdfr_krb5_dh_group_ok(krb5_context context, unsigned long bits,
1855178825Sdfr		  heim_integer *p, heim_integer *g, heim_integer *q,
1856178825Sdfr		  struct krb5_dh_moduli **moduli,
1857178825Sdfr		  char **name)
1858178825Sdfr{
1859178825Sdfr    int i;
1860178825Sdfr
1861178825Sdfr    if (name)
1862178825Sdfr	*name = NULL;
1863178825Sdfr
1864178825Sdfr    for (i = 0; moduli[i] != NULL; i++) {
1865178825Sdfr	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
1866178825Sdfr	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
1867178825Sdfr	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
1868178825Sdfr	{
1869178825Sdfr	    if (bits && bits > moduli[i]->bits) {
1870178825Sdfr		krb5_set_error_string(context, "PKINIT: DH group parameter %s "
1871178825Sdfr				      "no accepted, not enough bits generated",
1872178825Sdfr				      moduli[i]->name);
1873178825Sdfr		return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1874178825Sdfr	    }
1875178825Sdfr	    if (name)
1876178825Sdfr		*name = strdup(moduli[i]->name);
1877178825Sdfr	    return 0;
1878178825Sdfr	}
1879178825Sdfr    }
1880178825Sdfr    krb5_set_error_string(context, "PKINIT: DH group parameter no ok");
1881178825Sdfr    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
1882178825Sdfr}
1883178825Sdfr
1884178825Sdfrvoid KRB5_LIB_FUNCTION
1885178825Sdfr_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
1886178825Sdfr{
1887178825Sdfr#ifdef PKINIT
1888178825Sdfr    krb5_pk_init_ctx ctx;
1889178825Sdfr
1890178825Sdfr    if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
1891178825Sdfr	return;
1892178825Sdfr    ctx = opt->opt_private->pk_init_ctx;
1893178825Sdfr    if (ctx->dh)
1894178825Sdfr	DH_free(ctx->dh);
1895178825Sdfr	ctx->dh = NULL;
1896178825Sdfr    if (ctx->id) {
1897178825Sdfr	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
1898178825Sdfr	hx509_certs_free(&ctx->id->certs);
1899178825Sdfr	hx509_certs_free(&ctx->id->anchors);
1900178825Sdfr	hx509_certs_free(&ctx->id->certpool);
1901178825Sdfr	hx509_context_free(&ctx->id->hx509ctx);
1902178825Sdfr
1903178825Sdfr	if (ctx->clientDHNonce) {
1904178825Sdfr	    krb5_free_data(NULL, ctx->clientDHNonce);
1905178825Sdfr	    ctx->clientDHNonce = NULL;
1906178825Sdfr	}
1907178825Sdfr	if (ctx->m)
1908178825Sdfr	    _krb5_free_moduli(ctx->m);
1909178825Sdfr	free(ctx->id);
1910178825Sdfr	ctx->id = NULL;
1911178825Sdfr    }
1912178825Sdfr    free(opt->opt_private->pk_init_ctx);
1913178825Sdfr    opt->opt_private->pk_init_ctx = NULL;
1914178825Sdfr#endif
1915178825Sdfr}
1916178825Sdfr
1917178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1918178825Sdfrkrb5_get_init_creds_opt_set_pkinit(krb5_context context,
1919178825Sdfr				   krb5_get_init_creds_opt *opt,
1920178825Sdfr				   krb5_principal principal,
1921178825Sdfr				   const char *user_id,
1922178825Sdfr				   const char *x509_anchors,
1923178825Sdfr				   char * const * pool,
1924178825Sdfr				   char * const * pki_revoke,
1925178825Sdfr				   int flags,
1926178825Sdfr				   krb5_prompter_fct prompter,
1927178825Sdfr				   void *prompter_data,
1928178825Sdfr				   char *password)
1929178825Sdfr{
1930178825Sdfr#ifdef PKINIT
1931178825Sdfr    krb5_error_code ret;
1932178825Sdfr    char *anchors = NULL;
1933178825Sdfr
1934178825Sdfr    if (opt->opt_private == NULL) {
1935178825Sdfr	krb5_set_error_string(context, "PKINIT: on non extendable opt");
1936178825Sdfr	return EINVAL;
1937178825Sdfr    }
1938178825Sdfr
1939178825Sdfr    opt->opt_private->pk_init_ctx =
1940178825Sdfr	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
1941178825Sdfr    if (opt->opt_private->pk_init_ctx == NULL) {
1942178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1943178825Sdfr	return ENOMEM;
1944178825Sdfr    }
1945178825Sdfr    opt->opt_private->pk_init_ctx->dh = NULL;
1946178825Sdfr    opt->opt_private->pk_init_ctx->id = NULL;
1947178825Sdfr    opt->opt_private->pk_init_ctx->clientDHNonce = NULL;
1948178825Sdfr    opt->opt_private->pk_init_ctx->require_binding = 0;
1949178825Sdfr    opt->opt_private->pk_init_ctx->require_eku = 1;
1950178825Sdfr    opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
1951178825Sdfr    opt->opt_private->pk_init_ctx->peer = NULL;
1952178825Sdfr
1953178825Sdfr    /* XXX implement krb5_appdefault_strings  */
1954178825Sdfr    if (pool == NULL)
1955178825Sdfr	pool = krb5_config_get_strings(context, NULL,
1956178825Sdfr				       "appdefaults",
1957178825Sdfr				       "pkinit_pool",
1958178825Sdfr				       NULL);
1959178825Sdfr
1960178825Sdfr    if (pki_revoke == NULL)
1961178825Sdfr	pki_revoke = krb5_config_get_strings(context, NULL,
1962178825Sdfr					     "appdefaults",
1963178825Sdfr					     "pkinit_revoke",
1964178825Sdfr					     NULL);
1965178825Sdfr
1966178825Sdfr    if (x509_anchors == NULL) {
1967178825Sdfr	krb5_appdefault_string(context, "kinit",
1968178825Sdfr			       krb5_principal_get_realm(context, principal),
1969178825Sdfr			       "pkinit_anchors", NULL, &anchors);
1970178825Sdfr	x509_anchors = anchors;
1971178825Sdfr    }
1972178825Sdfr
1973178825Sdfr    ret = _krb5_pk_load_id(context,
1974178825Sdfr			   &opt->opt_private->pk_init_ctx->id,
1975178825Sdfr			   user_id,
1976178825Sdfr			   x509_anchors,
1977178825Sdfr			   pool,
1978178825Sdfr			   pki_revoke,
1979178825Sdfr			   prompter,
1980178825Sdfr			   prompter_data,
1981178825Sdfr			   password);
1982178825Sdfr    if (ret) {
1983178825Sdfr	free(opt->opt_private->pk_init_ctx);
1984178825Sdfr	opt->opt_private->pk_init_ctx = NULL;
1985178825Sdfr	return ret;
1986178825Sdfr    }
1987178825Sdfr
1988178825Sdfr    if ((flags & 2) == 0) {
1989178825Sdfr	const char *moduli_file;
1990178825Sdfr	unsigned long dh_min_bits;
1991178825Sdfr
1992178825Sdfr	moduli_file = krb5_config_get_string(context, NULL,
1993178825Sdfr					     "libdefaults",
1994178825Sdfr					     "moduli",
1995178825Sdfr					     NULL);
1996178825Sdfr
1997178825Sdfr	dh_min_bits =
1998178825Sdfr	    krb5_config_get_int_default(context, NULL, 0,
1999178825Sdfr					"libdefaults",
2000178825Sdfr					"pkinit_dh_min_bits",
2001178825Sdfr					NULL);
2002178825Sdfr
2003178825Sdfr	ret = _krb5_parse_moduli(context, moduli_file,
2004178825Sdfr				 &opt->opt_private->pk_init_ctx->m);
2005178825Sdfr	if (ret) {
2006178825Sdfr	    _krb5_get_init_creds_opt_free_pkinit(opt);
2007178825Sdfr	    return ret;
2008178825Sdfr	}
2009178825Sdfr
2010178825Sdfr	opt->opt_private->pk_init_ctx->dh = DH_new();
2011178825Sdfr	if (opt->opt_private->pk_init_ctx->dh == NULL) {
2012178825Sdfr	    krb5_set_error_string(context, "malloc: out of memory");
2013178825Sdfr	    _krb5_get_init_creds_opt_free_pkinit(opt);
2014178825Sdfr	    return ENOMEM;
2015178825Sdfr	}
2016178825Sdfr
2017178825Sdfr	ret = select_dh_group(context, opt->opt_private->pk_init_ctx->dh,
2018178825Sdfr			      dh_min_bits,
2019178825Sdfr			      opt->opt_private->pk_init_ctx->m);
2020178825Sdfr	if (ret) {
2021178825Sdfr	    _krb5_get_init_creds_opt_free_pkinit(opt);
2022178825Sdfr	    return ret;
2023178825Sdfr	}
2024178825Sdfr
2025178825Sdfr	if (DH_generate_key(opt->opt_private->pk_init_ctx->dh) != 1) {
2026178825Sdfr	    krb5_set_error_string(context, "pkinit: failed to generate DH key");
2027178825Sdfr	    _krb5_get_init_creds_opt_free_pkinit(opt);
2028178825Sdfr	    return ENOMEM;
2029178825Sdfr	}
2030178825Sdfr    }
2031178825Sdfr
2032178825Sdfr    return 0;
2033178825Sdfr#else
2034178825Sdfr    krb5_set_error_string(context, "no support for PKINIT compiled in");
2035178825Sdfr    return EINVAL;
2036178825Sdfr#endif
2037178825Sdfr}
2038178825Sdfr
2039178825Sdfr/*
2040178825Sdfr *
2041178825Sdfr */
2042178825Sdfr
2043178825Sdfrstatic void
2044178825Sdfr_krb5_pk_copy_error(krb5_context context,
2045178825Sdfr		    hx509_context hx509ctx,
2046178825Sdfr		    int hxret,
2047178825Sdfr		    const char *fmt,
2048178825Sdfr		    ...)
2049178825Sdfr{
2050178825Sdfr    va_list va;
2051178825Sdfr    char *s, *f;
2052178825Sdfr
2053178825Sdfr    va_start(va, fmt);
2054178825Sdfr    vasprintf(&f, fmt, va);
2055178825Sdfr    va_end(va);
2056178825Sdfr    if (f == NULL) {
2057178825Sdfr	krb5_clear_error_string(context);
2058178825Sdfr	return;
2059178825Sdfr    }
2060178825Sdfr
2061178825Sdfr    s = hx509_get_error_string(hx509ctx, hxret);
2062178825Sdfr    if (s == NULL) {
2063178825Sdfr	krb5_clear_error_string(context);
2064178825Sdfr	free(f);
2065178825Sdfr	return;
2066178825Sdfr    }
2067178825Sdfr    krb5_set_error_string(context, "%s: %s", f, s);
2068178825Sdfr    free(s);
2069178825Sdfr    free(f);
2070178825Sdfr}
2071