1178825Sdfr/*
2233294Sstas * Copyright (c) 2003 - 2007 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7178825Sdfr *
8233294Sstas * Redistribution and use in source and binary forms, with or without
9233294Sstas * modification, are permitted provided that the following conditions
10233294Sstas * are met:
11178825Sdfr *
12233294Sstas * 1. Redistributions of source code must retain the above copyright
13233294Sstas *    notice, this list of conditions and the following disclaimer.
14178825Sdfr *
15233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
16233294Sstas *    notice, this list of conditions and the following disclaimer in the
17233294Sstas *    documentation and/or other materials provided with the distribution.
18178825Sdfr *
19233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
20233294Sstas *    may be used to endorse or promote products derived from this software
21233294Sstas *    without specific prior written permission.
22233294Sstas *
23233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33233294Sstas * SUCH DAMAGE.
34178825Sdfr */
35178825Sdfr
36178825Sdfr#include "krb5_locl.h"
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 <cms_asn1.h>
49178825Sdfr#include <pkcs8_asn1.h>
50178825Sdfr#include <pkcs9_asn1.h>
51178825Sdfr#include <pkcs12_asn1.h>
52178825Sdfr#include <pkinit_asn1.h>
53178825Sdfr#include <asn1_err.h>
54178825Sdfr
55178825Sdfr#include <der.h>
56178825Sdfr
57178825Sdfrstruct krb5_pk_cert {
58178825Sdfr    hx509_cert cert;
59178825Sdfr};
60178825Sdfr
61178825Sdfrstruct krb5_pk_init_ctx_data {
62178825Sdfr    struct krb5_pk_identity *id;
63233294Sstas    enum { USE_RSA, USE_DH, USE_ECDH } keyex;
64233294Sstas    union {
65233294Sstas	DH *dh;
66233294Sstas#ifdef HAVE_OPENSSL
67233294Sstas	EC_KEY *eckey;
68233294Sstas#endif
69233294Sstas    } u;
70178825Sdfr    krb5_data *clientDHNonce;
71178825Sdfr    struct krb5_dh_moduli **m;
72178825Sdfr    hx509_peer_info peer;
73233294Sstas    enum krb5_pk_type type;
74178825Sdfr    unsigned int require_binding:1;
75178825Sdfr    unsigned int require_eku:1;
76178825Sdfr    unsigned int require_krbtgt_otherName:1;
77178825Sdfr    unsigned int require_hostname_match:1;
78178825Sdfr    unsigned int trustedCertifiers:1;
79233294Sstas    unsigned int anonymous:1;
80178825Sdfr};
81178825Sdfr
82178825Sdfrstatic void
83233294Sstaspk_copy_error(krb5_context context,
84233294Sstas	      hx509_context hx509ctx,
85233294Sstas	      int hxret,
86233294Sstas	      const char *fmt,
87233294Sstas	      ...)
88178825Sdfr    __attribute__ ((format (printf, 4, 5)));
89178825Sdfr
90178825Sdfr/*
91178825Sdfr *
92178825Sdfr */
93178825Sdfr
94233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
95178825Sdfr_krb5_pk_cert_free(struct krb5_pk_cert *cert)
96178825Sdfr{
97178825Sdfr    if (cert->cert) {
98178825Sdfr	hx509_cert_free(cert->cert);
99178825Sdfr    }
100178825Sdfr    free(cert);
101178825Sdfr}
102178825Sdfr
103178825Sdfrstatic krb5_error_code
104178825SdfrBN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
105178825Sdfr{
106178825Sdfr    integer->length = BN_num_bytes(bn);
107178825Sdfr    integer->data = malloc(integer->length);
108178825Sdfr    if (integer->data == NULL) {
109233294Sstas	krb5_clear_error_message(context);
110178825Sdfr	return ENOMEM;
111178825Sdfr    }
112178825Sdfr    BN_bn2bin(bn, integer->data);
113178825Sdfr    integer->negative = BN_is_negative(bn);
114178825Sdfr    return 0;
115178825Sdfr}
116178825Sdfr
117178825Sdfrstatic BIGNUM *
118178825Sdfrinteger_to_BN(krb5_context context, const char *field, const heim_integer *f)
119178825Sdfr{
120178825Sdfr    BIGNUM *bn;
121178825Sdfr
122178825Sdfr    bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
123178825Sdfr    if (bn == NULL) {
124233294Sstas	krb5_set_error_message(context, ENOMEM,
125233294Sstas			       N_("PKINIT: parsing BN failed %s", ""), field);
126178825Sdfr	return NULL;
127178825Sdfr    }
128178825Sdfr    BN_set_negative(bn, f->negative);
129178825Sdfr    return bn;
130178825Sdfr}
131178825Sdfr
132178825Sdfrstatic krb5_error_code
133233294Sstasselect_dh_group(krb5_context context, DH *dh, unsigned long bits,
134233294Sstas		struct krb5_dh_moduli **moduli)
135178825Sdfr{
136233294Sstas    const struct krb5_dh_moduli *m;
137178825Sdfr
138233294Sstas    if (bits == 0) {
139233294Sstas	m = moduli[1]; /* XXX */
140233294Sstas	if (m == NULL)
141233294Sstas	    m = moduli[0]; /* XXX */
142233294Sstas    } else {
143233294Sstas	int i;
144233294Sstas	for (i = 0; moduli[i] != NULL; i++) {
145233294Sstas	    if (bits < moduli[i]->bits)
146233294Sstas		break;
147233294Sstas	}
148233294Sstas	if (moduli[i] == NULL) {
149233294Sstas	    krb5_set_error_message(context, EINVAL,
150233294Sstas				   N_("Did not find a DH group parameter "
151233294Sstas				      "matching requirement of %lu bits", ""),
152233294Sstas				   bits);
153233294Sstas	    return EINVAL;
154233294Sstas	}
155233294Sstas	m = moduli[i];
156178825Sdfr    }
157178825Sdfr
158233294Sstas    dh->p = integer_to_BN(context, "p", &m->p);
159233294Sstas    if (dh->p == NULL)
160233294Sstas	return ENOMEM;
161233294Sstas    dh->g = integer_to_BN(context, "g", &m->g);
162233294Sstas    if (dh->g == NULL)
163233294Sstas	return ENOMEM;
164233294Sstas    dh->q = integer_to_BN(context, "q", &m->q);
165233294Sstas    if (dh->q == NULL)
166233294Sstas	return ENOMEM;
167178825Sdfr
168233294Sstas    return 0;
169233294Sstas}
170233294Sstas
171233294Sstasstruct certfind {
172233294Sstas    const char *type;
173233294Sstas    const heim_oid *oid;
174233294Sstas};
175233294Sstas
176233294Sstas/*
177233294Sstas * Try searchin the key by to use by first looking for for PK-INIT
178233294Sstas * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
179233294Sstas */
180233294Sstas
181233294Sstasstatic krb5_error_code
182233294Sstasfind_cert(krb5_context context, struct krb5_pk_identity *id,
183233294Sstas	  hx509_query *q, hx509_cert *cert)
184233294Sstas{
185233294Sstas    struct certfind cf[4] = {
186233294Sstas	{ "MobileMe EKU" },
187233294Sstas	{ "PKINIT EKU" },
188233294Sstas	{ "MS EKU" },
189233294Sstas	{ "any (or no)" }
190233294Sstas    };
191233294Sstas    int ret = HX509_CERT_NOT_FOUND;
192233294Sstas    size_t i, start = 1;
193233294Sstas    unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
194233294Sstas    const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
195233294Sstas
196233294Sstas
197233294Sstas    if (id->flags & PKINIT_BTMM)
198233294Sstas	start = 0;
199233294Sstas
200233294Sstas    cf[0].oid = &mobileMe;
201233294Sstas    cf[1].oid = &asn1_oid_id_pkekuoid;
202233294Sstas    cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
203233294Sstas    cf[3].oid = NULL;
204233294Sstas
205233294Sstas    for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
206233294Sstas	ret = hx509_query_match_eku(q, cf[i].oid);
207233294Sstas	if (ret) {
208233294Sstas	    pk_copy_error(context, context->hx509ctx, ret,
209233294Sstas			  "Failed setting %s OID", cf[i].type);
210233294Sstas	    return ret;
211233294Sstas	}
212233294Sstas
213233294Sstas	ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
214233294Sstas	if (ret == 0)
215233294Sstas	    break;
216233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
217233294Sstas		      "Failed finding certificate with %s OID", cf[i].type);
218178825Sdfr    }
219233294Sstas    return ret;
220233294Sstas}
221178825Sdfr
222233294Sstas
223233294Sstasstatic krb5_error_code
224233294Sstascreate_signature(krb5_context context,
225233294Sstas		 const heim_oid *eContentType,
226233294Sstas		 krb5_data *eContent,
227233294Sstas		 struct krb5_pk_identity *id,
228233294Sstas		 hx509_peer_info peer,
229233294Sstas		 krb5_data *sd_data)
230233294Sstas{
231233294Sstas    int ret, flags = 0;
232233294Sstas
233233294Sstas    if (id->cert == NULL)
234233294Sstas	flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
235233294Sstas
236233294Sstas    ret = hx509_cms_create_signed_1(context->hx509ctx,
237233294Sstas				    flags,
238178825Sdfr				    eContentType,
239178825Sdfr				    eContent->data,
240178825Sdfr				    eContent->length,
241178825Sdfr				    NULL,
242233294Sstas				    id->cert,
243178825Sdfr				    peer,
244178825Sdfr				    NULL,
245178825Sdfr				    id->certs,
246178825Sdfr				    sd_data);
247233294Sstas    if (ret) {
248233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
249233294Sstas		      "Create CMS signedData");
250233294Sstas	return ret;
251233294Sstas    }
252178825Sdfr
253233294Sstas    return 0;
254178825Sdfr}
255178825Sdfr
256178825Sdfrstatic int
257178825Sdfrcert2epi(hx509_context context, void *ctx, hx509_cert c)
258178825Sdfr{
259178825Sdfr    ExternalPrincipalIdentifiers *ids = ctx;
260178825Sdfr    ExternalPrincipalIdentifier id;
261178825Sdfr    hx509_name subject = NULL;
262178825Sdfr    void *p;
263178825Sdfr    int ret;
264178825Sdfr
265233294Sstas    if (ids->len > 10)
266233294Sstas	return 0;
267233294Sstas
268178825Sdfr    memset(&id, 0, sizeof(id));
269178825Sdfr
270178825Sdfr    ret = hx509_cert_get_subject(c, &subject);
271178825Sdfr    if (ret)
272178825Sdfr	return ret;
273178825Sdfr
274178825Sdfr    if (hx509_name_is_null_p(subject) != 0) {
275178825Sdfr
276178825Sdfr	id.subjectName = calloc(1, sizeof(*id.subjectName));
277178825Sdfr	if (id.subjectName == NULL) {
278178825Sdfr	    hx509_name_free(&subject);
279178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
280178825Sdfr	    return ENOMEM;
281178825Sdfr	}
282233294Sstas
283178825Sdfr	ret = hx509_name_binary(subject, id.subjectName);
284178825Sdfr	if (ret) {
285178825Sdfr	    hx509_name_free(&subject);
286178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
287178825Sdfr	    return ret;
288178825Sdfr	}
289178825Sdfr    }
290178825Sdfr    hx509_name_free(&subject);
291178825Sdfr
292178825Sdfr
293178825Sdfr    id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
294178825Sdfr    if (id.issuerAndSerialNumber == NULL) {
295178825Sdfr	free_ExternalPrincipalIdentifier(&id);
296178825Sdfr	return ENOMEM;
297178825Sdfr    }
298178825Sdfr
299178825Sdfr    {
300178825Sdfr	IssuerAndSerialNumber iasn;
301178825Sdfr	hx509_name issuer;
302233294Sstas	size_t size = 0;
303233294Sstas
304178825Sdfr	memset(&iasn, 0, sizeof(iasn));
305178825Sdfr
306178825Sdfr	ret = hx509_cert_get_issuer(c, &issuer);
307178825Sdfr	if (ret) {
308178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
309178825Sdfr	    return ret;
310178825Sdfr	}
311178825Sdfr
312178825Sdfr	ret = hx509_name_to_Name(issuer, &iasn.issuer);
313178825Sdfr	hx509_name_free(&issuer);
314178825Sdfr	if (ret) {
315178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
316178825Sdfr	    return ret;
317178825Sdfr	}
318233294Sstas
319178825Sdfr	ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
320178825Sdfr	if (ret) {
321178825Sdfr	    free_IssuerAndSerialNumber(&iasn);
322178825Sdfr	    free_ExternalPrincipalIdentifier(&id);
323178825Sdfr	    return ret;
324178825Sdfr	}
325178825Sdfr
326178825Sdfr	ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
327233294Sstas			   id.issuerAndSerialNumber->data,
328178825Sdfr			   id.issuerAndSerialNumber->length,
329178825Sdfr			   &iasn, &size, ret);
330178825Sdfr	free_IssuerAndSerialNumber(&iasn);
331178825Sdfr	if (ret)
332178825Sdfr	    return ret;
333178825Sdfr	if (id.issuerAndSerialNumber->length != size)
334178825Sdfr	    abort();
335178825Sdfr    }
336178825Sdfr
337178825Sdfr    id.subjectKeyIdentifier = NULL;
338178825Sdfr
339233294Sstas    p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
340178825Sdfr    if (p == NULL) {
341178825Sdfr	free_ExternalPrincipalIdentifier(&id);
342178825Sdfr	return ENOMEM;
343178825Sdfr    }
344178825Sdfr
345178825Sdfr    ids->val = p;
346178825Sdfr    ids->val[ids->len] = id;
347178825Sdfr    ids->len++;
348178825Sdfr
349178825Sdfr    return 0;
350178825Sdfr}
351178825Sdfr
352178825Sdfrstatic krb5_error_code
353178825Sdfrbuild_edi(krb5_context context,
354178825Sdfr	  hx509_context hx509ctx,
355178825Sdfr	  hx509_certs certs,
356178825Sdfr	  ExternalPrincipalIdentifiers *ids)
357178825Sdfr{
358233294Sstas    return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
359178825Sdfr}
360178825Sdfr
361178825Sdfrstatic krb5_error_code
362178825Sdfrbuild_auth_pack(krb5_context context,
363178825Sdfr		unsigned nonce,
364178825Sdfr		krb5_pk_init_ctx ctx,
365178825Sdfr		const KDC_REQ_BODY *body,
366178825Sdfr		AuthPack *a)
367178825Sdfr{
368233294Sstas    size_t buf_size, len = 0;
369178825Sdfr    krb5_error_code ret;
370178825Sdfr    void *buf;
371178825Sdfr    krb5_timestamp sec;
372178825Sdfr    int32_t usec;
373178825Sdfr    Checksum checksum;
374178825Sdfr
375233294Sstas    krb5_clear_error_message(context);
376178825Sdfr
377178825Sdfr    memset(&checksum, 0, sizeof(checksum));
378178825Sdfr
379178825Sdfr    krb5_us_timeofday(context, &sec, &usec);
380178825Sdfr    a->pkAuthenticator.ctime = sec;
381178825Sdfr    a->pkAuthenticator.nonce = nonce;
382178825Sdfr
383178825Sdfr    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
384178825Sdfr    if (ret)
385178825Sdfr	return ret;
386178825Sdfr    if (buf_size != len)
387178825Sdfr	krb5_abortx(context, "internal error in ASN.1 encoder");
388178825Sdfr
389178825Sdfr    ret = krb5_create_checksum(context,
390178825Sdfr			       NULL,
391178825Sdfr			       0,
392178825Sdfr			       CKSUMTYPE_SHA1,
393178825Sdfr			       buf,
394178825Sdfr			       len,
395178825Sdfr			       &checksum);
396178825Sdfr    free(buf);
397233294Sstas    if (ret)
398178825Sdfr	return ret;
399178825Sdfr
400178825Sdfr    ALLOC(a->pkAuthenticator.paChecksum, 1);
401178825Sdfr    if (a->pkAuthenticator.paChecksum == NULL) {
402233294Sstas	krb5_set_error_message(context, ENOMEM,
403233294Sstas			       N_("malloc: out of memory", ""));
404178825Sdfr	return ENOMEM;
405178825Sdfr    }
406178825Sdfr
407178825Sdfr    ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
408178825Sdfr			 checksum.checksum.data, checksum.checksum.length);
409178825Sdfr    free_Checksum(&checksum);
410178825Sdfr    if (ret)
411178825Sdfr	return ret;
412178825Sdfr
413233294Sstas    if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
414233294Sstas	const char *moduli_file;
415233294Sstas	unsigned long dh_min_bits;
416178825Sdfr	krb5_data dhbuf;
417233294Sstas	size_t size = 0;
418178825Sdfr
419233294Sstas	krb5_data_zero(&dhbuf);
420233294Sstas
421233294Sstas
422233294Sstas
423233294Sstas	moduli_file = krb5_config_get_string(context, NULL,
424233294Sstas					     "libdefaults",
425233294Sstas					     "moduli",
426233294Sstas					     NULL);
427233294Sstas
428233294Sstas	dh_min_bits =
429233294Sstas	    krb5_config_get_int_default(context, NULL, 0,
430233294Sstas					"libdefaults",
431233294Sstas					"pkinit_dh_min_bits",
432233294Sstas					NULL);
433233294Sstas
434233294Sstas	ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
435233294Sstas	if (ret)
436233294Sstas	    return ret;
437233294Sstas
438233294Sstas	ctx->u.dh = DH_new();
439233294Sstas	if (ctx->u.dh == NULL) {
440233294Sstas	    krb5_set_error_message(context, ENOMEM,
441233294Sstas				   N_("malloc: out of memory", ""));
442233294Sstas	    return ENOMEM;
443233294Sstas	}
444233294Sstas
445233294Sstas	ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
446233294Sstas	if (ret)
447233294Sstas	    return ret;
448233294Sstas
449233294Sstas	if (DH_generate_key(ctx->u.dh) != 1) {
450233294Sstas	    krb5_set_error_message(context, ENOMEM,
451233294Sstas				   N_("pkinit: failed to generate DH key", ""));
452233294Sstas	    return ENOMEM;
453233294Sstas	}
454233294Sstas
455233294Sstas
456178825Sdfr	if (1 /* support_cached_dh */) {
457178825Sdfr	    ALLOC(a->clientDHNonce, 1);
458178825Sdfr	    if (a->clientDHNonce == NULL) {
459233294Sstas		krb5_clear_error_message(context);
460178825Sdfr		return ENOMEM;
461178825Sdfr	    }
462178825Sdfr	    ret = krb5_data_alloc(a->clientDHNonce, 40);
463178825Sdfr	    if (a->clientDHNonce == NULL) {
464233294Sstas		krb5_clear_error_message(context);
465233294Sstas		return ret;
466178825Sdfr	    }
467233294Sstas	    RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
468233294Sstas	    ret = krb5_copy_data(context, a->clientDHNonce,
469178825Sdfr				 &ctx->clientDHNonce);
470178825Sdfr	    if (ret)
471178825Sdfr		return ret;
472178825Sdfr	}
473178825Sdfr
474178825Sdfr	ALLOC(a->clientPublicValue, 1);
475178825Sdfr	if (a->clientPublicValue == NULL)
476178825Sdfr	    return ENOMEM;
477178825Sdfr
478233294Sstas	if (ctx->keyex == USE_DH) {
479233294Sstas	    DH *dh = ctx->u.dh;
480233294Sstas	    DomainParameters dp;
481233294Sstas	    heim_integer dh_pub_key;
482178825Sdfr
483233294Sstas	    ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
484233294Sstas			       &a->clientPublicValue->algorithm.algorithm);
485233294Sstas	    if (ret)
486233294Sstas		return ret;
487233294Sstas
488233294Sstas	    memset(&dp, 0, sizeof(dp));
489233294Sstas
490233294Sstas	    ret = BN_to_integer(context, dh->p, &dp.p);
491233294Sstas	    if (ret) {
492233294Sstas		free_DomainParameters(&dp);
493233294Sstas		return ret;
494233294Sstas	    }
495233294Sstas	    ret = BN_to_integer(context, dh->g, &dp.g);
496233294Sstas	    if (ret) {
497233294Sstas		free_DomainParameters(&dp);
498233294Sstas		return ret;
499233294Sstas	    }
500233294Sstas	    ret = BN_to_integer(context, dh->q, &dp.q);
501233294Sstas	    if (ret) {
502233294Sstas		free_DomainParameters(&dp);
503233294Sstas		return ret;
504233294Sstas	    }
505233294Sstas	    dp.j = NULL;
506233294Sstas	    dp.validationParms = NULL;
507233294Sstas
508233294Sstas	    a->clientPublicValue->algorithm.parameters =
509233294Sstas		malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
510233294Sstas	    if (a->clientPublicValue->algorithm.parameters == NULL) {
511233294Sstas		free_DomainParameters(&dp);
512233294Sstas		return ret;
513233294Sstas	    }
514233294Sstas
515233294Sstas	    ASN1_MALLOC_ENCODE(DomainParameters,
516233294Sstas			       a->clientPublicValue->algorithm.parameters->data,
517233294Sstas			       a->clientPublicValue->algorithm.parameters->length,
518233294Sstas			       &dp, &size, ret);
519178825Sdfr	    free_DomainParameters(&dp);
520233294Sstas	    if (ret)
521233294Sstas		return ret;
522233294Sstas	    if (size != a->clientPublicValue->algorithm.parameters->length)
523233294Sstas		krb5_abortx(context, "Internal ASN1 encoder error");
524178825Sdfr
525233294Sstas	    ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
526233294Sstas	    if (ret)
527233294Sstas		return ret;
528178825Sdfr
529233294Sstas	    ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
530233294Sstas			       &dh_pub_key, &size, ret);
531233294Sstas	    der_free_heim_integer(&dh_pub_key);
532233294Sstas	    if (ret)
533233294Sstas		return ret;
534233294Sstas	    if (size != dhbuf.length)
535233294Sstas		krb5_abortx(context, "asn1 internal error");
536233294Sstas	} else if (ctx->keyex == USE_ECDH) {
537233294Sstas#ifdef HAVE_OPENSSL
538233294Sstas	    ECParameters ecp;
539233294Sstas	    unsigned char *p;
540233294Sstas	    int xlen;
541178825Sdfr
542233294Sstas	    /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
543178825Sdfr
544233294Sstas	    ecp.element = choice_ECParameters_namedCurve;
545233294Sstas	    ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
546233294Sstas			       &ecp.u.namedCurve);
547233294Sstas	    if (ret)
548233294Sstas		return ret;
549233294Sstas
550233294Sstas	    ALLOC(a->clientPublicValue->algorithm.parameters, 1);
551233294Sstas	    if (a->clientPublicValue->algorithm.parameters == NULL) {
552233294Sstas		free_ECParameters(&ecp);
553233294Sstas		return ENOMEM;
554233294Sstas	    }
555233294Sstas	    ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
556233294Sstas	    free_ECParameters(&ecp);
557233294Sstas	    if (ret)
558233294Sstas		return ret;
559233294Sstas	    if ((int)size != xlen)
560233294Sstas		krb5_abortx(context, "asn1 internal error");
561233294Sstas
562233294Sstas	    a->clientPublicValue->algorithm.parameters->data = p;
563233294Sstas	    a->clientPublicValue->algorithm.parameters->length = size;
564233294Sstas
565233294Sstas	    /* copy in public key */
566233294Sstas
567233294Sstas	    ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
568233294Sstas			       &a->clientPublicValue->algorithm.algorithm);
569233294Sstas	    if (ret)
570233294Sstas		return ret;
571233294Sstas
572233294Sstas	    ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
573233294Sstas	    if (ctx->u.eckey == NULL)
574233294Sstas		return ENOMEM;
575233294Sstas
576233294Sstas	    ret = EC_KEY_generate_key(ctx->u.eckey);
577233294Sstas	    if (ret != 1)
578233294Sstas		return EINVAL;
579233294Sstas
580233294Sstas	    /* encode onto dhkey */
581233294Sstas
582233294Sstas	    xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
583233294Sstas	    if (xlen <= 0)
584233294Sstas		abort();
585233294Sstas
586233294Sstas	    dhbuf.data = malloc(xlen);
587233294Sstas	    if (dhbuf.data == NULL)
588233294Sstas		abort();
589233294Sstas	    dhbuf.length = xlen;
590233294Sstas	    p = dhbuf.data;
591233294Sstas
592233294Sstas	    xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
593233294Sstas	    if (xlen <= 0)
594233294Sstas		abort();
595233294Sstas
596233294Sstas	    /* XXX verify that this is right with RFC3279 */
597233294Sstas#else
598233294Sstas	    return EINVAL;
599233294Sstas#endif
600233294Sstas	} else
601233294Sstas	    krb5_abortx(context, "internal error");
602178825Sdfr	a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
603178825Sdfr	a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
604178825Sdfr    }
605178825Sdfr
606178825Sdfr    {
607178825Sdfr	a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
608178825Sdfr	if (a->supportedCMSTypes == NULL)
609178825Sdfr	    return ENOMEM;
610178825Sdfr
611233294Sstas	ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
612233294Sstas				     ctx->id->cert,
613178825Sdfr				     &a->supportedCMSTypes->val,
614178825Sdfr				     &a->supportedCMSTypes->len);
615178825Sdfr	if (ret)
616178825Sdfr	    return ret;
617178825Sdfr    }
618178825Sdfr
619178825Sdfr    return ret;
620178825Sdfr}
621178825Sdfr
622233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
623178825Sdfr_krb5_pk_mk_ContentInfo(krb5_context context,
624233294Sstas			const krb5_data *buf,
625178825Sdfr			const heim_oid *oid,
626178825Sdfr			struct ContentInfo *content_info)
627178825Sdfr{
628178825Sdfr    krb5_error_code ret;
629178825Sdfr
630178825Sdfr    ret = der_copy_oid(oid, &content_info->contentType);
631178825Sdfr    if (ret)
632178825Sdfr	return ret;
633178825Sdfr    ALLOC(content_info->content, 1);
634178825Sdfr    if (content_info->content == NULL)
635178825Sdfr	return ENOMEM;
636178825Sdfr    content_info->content->data = malloc(buf->length);
637178825Sdfr    if (content_info->content->data == NULL)
638178825Sdfr	return ENOMEM;
639178825Sdfr    memcpy(content_info->content->data, buf->data, buf->length);
640178825Sdfr    content_info->content->length = buf->length;
641178825Sdfr    return 0;
642178825Sdfr}
643178825Sdfr
644178825Sdfrstatic krb5_error_code
645178825Sdfrpk_mk_padata(krb5_context context,
646178825Sdfr	     krb5_pk_init_ctx ctx,
647178825Sdfr	     const KDC_REQ_BODY *req_body,
648178825Sdfr	     unsigned nonce,
649178825Sdfr	     METHOD_DATA *md)
650178825Sdfr{
651178825Sdfr    struct ContentInfo content_info;
652178825Sdfr    krb5_error_code ret;
653233294Sstas    const heim_oid *oid = NULL;
654233294Sstas    size_t size = 0;
655178825Sdfr    krb5_data buf, sd_buf;
656233294Sstas    int pa_type = -1;
657178825Sdfr
658178825Sdfr    krb5_data_zero(&buf);
659178825Sdfr    krb5_data_zero(&sd_buf);
660178825Sdfr    memset(&content_info, 0, sizeof(content_info));
661178825Sdfr
662233294Sstas    if (ctx->type == PKINIT_WIN2K) {
663178825Sdfr	AuthPack_Win2k ap;
664178825Sdfr	krb5_timestamp sec;
665178825Sdfr	int32_t usec;
666178825Sdfr
667178825Sdfr	memset(&ap, 0, sizeof(ap));
668178825Sdfr
669178825Sdfr	/* fill in PKAuthenticator */
670178825Sdfr	ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
671178825Sdfr	if (ret) {
672178825Sdfr	    free_AuthPack_Win2k(&ap);
673233294Sstas	    krb5_clear_error_message(context);
674178825Sdfr	    goto out;
675178825Sdfr	}
676178825Sdfr	ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
677178825Sdfr	if (ret) {
678178825Sdfr	    free_AuthPack_Win2k(&ap);
679233294Sstas	    krb5_clear_error_message(context);
680178825Sdfr	    goto out;
681178825Sdfr	}
682178825Sdfr
683178825Sdfr	krb5_us_timeofday(context, &sec, &usec);
684178825Sdfr	ap.pkAuthenticator.ctime = sec;
685178825Sdfr	ap.pkAuthenticator.cusec = usec;
686178825Sdfr	ap.pkAuthenticator.nonce = nonce;
687178825Sdfr
688178825Sdfr	ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
689178825Sdfr			   &ap, &size, ret);
690178825Sdfr	free_AuthPack_Win2k(&ap);
691178825Sdfr	if (ret) {
692233294Sstas	    krb5_set_error_message(context, ret,
693233294Sstas				   N_("Failed encoding AuthPackWin: %d", ""),
694233294Sstas				   (int)ret);
695178825Sdfr	    goto out;
696178825Sdfr	}
697178825Sdfr	if (buf.length != size)
698178825Sdfr	    krb5_abortx(context, "internal ASN1 encoder error");
699178825Sdfr
700233294Sstas	oid = &asn1_oid_id_pkcs7_data;
701233294Sstas    } else if (ctx->type == PKINIT_27) {
702178825Sdfr	AuthPack ap;
703233294Sstas
704178825Sdfr	memset(&ap, 0, sizeof(ap));
705178825Sdfr
706233294Sstas	ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
707178825Sdfr	if (ret) {
708178825Sdfr	    free_AuthPack(&ap);
709178825Sdfr	    goto out;
710178825Sdfr	}
711178825Sdfr
712178825Sdfr	ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
713178825Sdfr	free_AuthPack(&ap);
714178825Sdfr	if (ret) {
715233294Sstas	    krb5_set_error_message(context, ret,
716233294Sstas				   N_("Failed encoding AuthPack: %d", ""),
717233294Sstas				   (int)ret);
718178825Sdfr	    goto out;
719178825Sdfr	}
720178825Sdfr	if (buf.length != size)
721178825Sdfr	    krb5_abortx(context, "internal ASN1 encoder error");
722178825Sdfr
723233294Sstas	oid = &asn1_oid_id_pkauthdata;
724178825Sdfr    } else
725178825Sdfr	krb5_abortx(context, "internal pkinit error");
726178825Sdfr
727233294Sstas    ret = create_signature(context, oid, &buf, ctx->id,
728233294Sstas			   ctx->peer, &sd_buf);
729178825Sdfr    krb5_data_free(&buf);
730178825Sdfr    if (ret)
731178825Sdfr	goto out;
732178825Sdfr
733233294Sstas    ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
734178825Sdfr    krb5_data_free(&sd_buf);
735178825Sdfr    if (ret) {
736233294Sstas	krb5_set_error_message(context, ret,
737233294Sstas			       N_("ContentInfo wrapping of signedData failed",""));
738178825Sdfr	goto out;
739178825Sdfr    }
740178825Sdfr
741233294Sstas    if (ctx->type == PKINIT_WIN2K) {
742178825Sdfr	PA_PK_AS_REQ_Win2k winreq;
743178825Sdfr
744178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
745178825Sdfr
746178825Sdfr	memset(&winreq, 0, sizeof(winreq));
747178825Sdfr
748178825Sdfr	winreq.signed_auth_pack = buf;
749178825Sdfr
750178825Sdfr	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
751178825Sdfr			   &winreq, &size, ret);
752178825Sdfr	free_PA_PK_AS_REQ_Win2k(&winreq);
753178825Sdfr
754233294Sstas    } else if (ctx->type == PKINIT_27) {
755178825Sdfr	PA_PK_AS_REQ req;
756178825Sdfr
757178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REQ;
758178825Sdfr
759178825Sdfr	memset(&req, 0, sizeof(req));
760233294Sstas	req.signedAuthPack = buf;
761178825Sdfr
762178825Sdfr	if (ctx->trustedCertifiers) {
763178825Sdfr
764178825Sdfr	    req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
765178825Sdfr	    if (req.trustedCertifiers == NULL) {
766233294Sstas		ret = ENOMEM;
767233294Sstas		krb5_set_error_message(context, ret,
768233294Sstas				       N_("malloc: out of memory", ""));
769178825Sdfr		free_PA_PK_AS_REQ(&req);
770178825Sdfr		goto out;
771178825Sdfr	    }
772233294Sstas	    ret = build_edi(context, context->hx509ctx,
773178825Sdfr			    ctx->id->anchors, req.trustedCertifiers);
774178825Sdfr	    if (ret) {
775233294Sstas		krb5_set_error_message(context, ret,
776233294Sstas				       N_("pk-init: failed to build "
777233294Sstas					  "trustedCertifiers", ""));
778178825Sdfr		free_PA_PK_AS_REQ(&req);
779178825Sdfr		goto out;
780178825Sdfr	    }
781178825Sdfr	}
782178825Sdfr	req.kdcPkId = NULL;
783178825Sdfr
784178825Sdfr	ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
785178825Sdfr			   &req, &size, ret);
786178825Sdfr
787178825Sdfr	free_PA_PK_AS_REQ(&req);
788178825Sdfr
789178825Sdfr    } else
790178825Sdfr	krb5_abortx(context, "internal pkinit error");
791178825Sdfr    if (ret) {
792233294Sstas	krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
793178825Sdfr	goto out;
794178825Sdfr    }
795178825Sdfr    if (buf.length != size)
796178825Sdfr	krb5_abortx(context, "Internal ASN1 encoder error");
797178825Sdfr
798178825Sdfr    ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
799178825Sdfr    if (ret)
800178825Sdfr	free(buf.data);
801178825Sdfr
802233294Sstas    if (ret == 0)
803233294Sstas    	krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
804178825Sdfr
805233294Sstas out:
806178825Sdfr    free_ContentInfo(&content_info);
807178825Sdfr
808178825Sdfr    return ret;
809178825Sdfr}
810178825Sdfr
811178825Sdfr
812233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
813178825Sdfr_krb5_pk_mk_padata(krb5_context context,
814178825Sdfr		   void *c,
815233294Sstas		   int ic_flags,
816233294Sstas		   int win2k,
817178825Sdfr		   const KDC_REQ_BODY *req_body,
818178825Sdfr		   unsigned nonce,
819178825Sdfr		   METHOD_DATA *md)
820178825Sdfr{
821178825Sdfr    krb5_pk_init_ctx ctx = c;
822178825Sdfr    int win2k_compat;
823178825Sdfr
824233294Sstas    if (ctx->id->certs == NULL && ctx->anonymous == 0) {
825233294Sstas	krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
826233294Sstas			       N_("PKINIT: No user certificate given", ""));
827233294Sstas	return HEIM_PKINIT_NO_PRIVATE_KEY;
828233294Sstas    }
829233294Sstas
830178825Sdfr    win2k_compat = krb5_config_get_bool_default(context, NULL,
831233294Sstas						win2k,
832178825Sdfr						"realms",
833178825Sdfr						req_body->realm,
834178825Sdfr						"pkinit_win2k",
835178825Sdfr						NULL);
836178825Sdfr
837178825Sdfr    if (win2k_compat) {
838233294Sstas	ctx->require_binding =
839178825Sdfr	    krb5_config_get_bool_default(context, NULL,
840233294Sstas					 TRUE,
841178825Sdfr					 "realms",
842178825Sdfr					 req_body->realm,
843178825Sdfr					 "pkinit_win2k_require_binding",
844178825Sdfr					 NULL);
845233294Sstas	ctx->type = PKINIT_WIN2K;
846178825Sdfr    } else
847233294Sstas	ctx->type = PKINIT_27;
848178825Sdfr
849233294Sstas    ctx->require_eku =
850178825Sdfr	krb5_config_get_bool_default(context, NULL,
851178825Sdfr				     TRUE,
852178825Sdfr				     "realms",
853178825Sdfr				     req_body->realm,
854178825Sdfr				     "pkinit_require_eku",
855178825Sdfr				     NULL);
856233294Sstas    if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
857233294Sstas	ctx->require_eku = 0;
858233294Sstas    if (ctx->id->flags & PKINIT_BTMM)
859233294Sstas	ctx->require_eku = 0;
860233294Sstas
861233294Sstas    ctx->require_krbtgt_otherName =
862178825Sdfr	krb5_config_get_bool_default(context, NULL,
863178825Sdfr				     TRUE,
864178825Sdfr				     "realms",
865178825Sdfr				     req_body->realm,
866178825Sdfr				     "pkinit_require_krbtgt_otherName",
867178825Sdfr				     NULL);
868178825Sdfr
869233294Sstas    ctx->require_hostname_match =
870178825Sdfr	krb5_config_get_bool_default(context, NULL,
871178825Sdfr				     FALSE,
872178825Sdfr				     "realms",
873178825Sdfr				     req_body->realm,
874178825Sdfr				     "pkinit_require_hostname_match",
875178825Sdfr				     NULL);
876178825Sdfr
877233294Sstas    ctx->trustedCertifiers =
878178825Sdfr	krb5_config_get_bool_default(context, NULL,
879178825Sdfr				     TRUE,
880178825Sdfr				     "realms",
881178825Sdfr				     req_body->realm,
882178825Sdfr				     "pkinit_trustedCertifiers",
883178825Sdfr				     NULL);
884178825Sdfr
885178825Sdfr    return pk_mk_padata(context, ctx, req_body, nonce, md);
886178825Sdfr}
887178825Sdfr
888233294Sstasstatic krb5_error_code
889233294Sstaspk_verify_sign(krb5_context context,
890233294Sstas	       const void *data,
891233294Sstas	       size_t length,
892233294Sstas	       struct krb5_pk_identity *id,
893233294Sstas	       heim_oid *contentType,
894233294Sstas	       krb5_data *content,
895233294Sstas	       struct krb5_pk_cert **signer)
896178825Sdfr{
897178825Sdfr    hx509_certs signer_certs;
898233294Sstas    int ret, flags = 0;
899178825Sdfr
900233294Sstas    /* BTMM is broken in Leo and SnowLeo */
901233294Sstas    if (id->flags & PKINIT_BTMM) {
902233294Sstas	flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
903233294Sstas	flags |= HX509_CMS_VS_NO_KU_CHECK;
904233294Sstas	flags |= HX509_CMS_VS_NO_VALIDATE;
905233294Sstas    }
906233294Sstas
907178825Sdfr    *signer = NULL;
908178825Sdfr
909233294Sstas    ret = hx509_cms_verify_signed(context->hx509ctx,
910178825Sdfr				  id->verify_ctx,
911233294Sstas				  flags,
912178825Sdfr				  data,
913178825Sdfr				  length,
914178825Sdfr				  NULL,
915178825Sdfr				  id->certpool,
916178825Sdfr				  contentType,
917178825Sdfr				  content,
918178825Sdfr				  &signer_certs);
919178825Sdfr    if (ret) {
920233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
921233294Sstas		      "CMS verify signed failed");
922178825Sdfr	return ret;
923178825Sdfr    }
924178825Sdfr
925178825Sdfr    *signer = calloc(1, sizeof(**signer));
926178825Sdfr    if (*signer == NULL) {
927233294Sstas	krb5_clear_error_message(context);
928178825Sdfr	ret = ENOMEM;
929178825Sdfr	goto out;
930178825Sdfr    }
931233294Sstas
932233294Sstas    ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
933178825Sdfr    if (ret) {
934233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
935233294Sstas		      "Failed to get on of the signer certs");
936178825Sdfr	goto out;
937178825Sdfr    }
938178825Sdfr
939233294Sstas out:
940178825Sdfr    hx509_certs_free(&signer_certs);
941178825Sdfr    if (ret) {
942178825Sdfr	if (*signer) {
943178825Sdfr	    hx509_cert_free((*signer)->cert);
944178825Sdfr	    free(*signer);
945178825Sdfr	    *signer = NULL;
946178825Sdfr	}
947178825Sdfr    }
948178825Sdfr
949178825Sdfr    return ret;
950178825Sdfr}
951178825Sdfr
952178825Sdfrstatic krb5_error_code
953178825Sdfrget_reply_key_win(krb5_context context,
954178825Sdfr		  const krb5_data *content,
955178825Sdfr		  unsigned nonce,
956178825Sdfr		  krb5_keyblock **key)
957178825Sdfr{
958178825Sdfr    ReplyKeyPack_Win2k key_pack;
959178825Sdfr    krb5_error_code ret;
960178825Sdfr    size_t size;
961178825Sdfr
962178825Sdfr    ret = decode_ReplyKeyPack_Win2k(content->data,
963178825Sdfr				    content->length,
964178825Sdfr				    &key_pack,
965178825Sdfr				    &size);
966178825Sdfr    if (ret) {
967233294Sstas	krb5_set_error_message(context, ret,
968233294Sstas			       N_("PKINIT decoding reply key failed", ""));
969178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
970178825Sdfr	return ret;
971178825Sdfr    }
972233294Sstas
973233294Sstas    if ((unsigned)key_pack.nonce != nonce) {
974233294Sstas	krb5_set_error_message(context, ret,
975233294Sstas			       N_("PKINIT enckey nonce is wrong", ""));
976178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
977178825Sdfr	return KRB5KRB_AP_ERR_MODIFIED;
978178825Sdfr    }
979178825Sdfr
980178825Sdfr    *key = malloc (sizeof (**key));
981178825Sdfr    if (*key == NULL) {
982178825Sdfr	free_ReplyKeyPack_Win2k(&key_pack);
983233294Sstas	krb5_set_error_message(context, ENOMEM,
984233294Sstas			       N_("malloc: out of memory", ""));
985178825Sdfr	return ENOMEM;
986178825Sdfr    }
987178825Sdfr
988178825Sdfr    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
989178825Sdfr    free_ReplyKeyPack_Win2k(&key_pack);
990178825Sdfr    if (ret) {
991233294Sstas	krb5_set_error_message(context, ret,
992233294Sstas			       N_("PKINIT failed copying reply key", ""));
993178825Sdfr	free(*key);
994178825Sdfr	*key = NULL;
995178825Sdfr    }
996178825Sdfr
997178825Sdfr    return ret;
998178825Sdfr}
999178825Sdfr
1000178825Sdfrstatic krb5_error_code
1001178825Sdfrget_reply_key(krb5_context context,
1002178825Sdfr	      const krb5_data *content,
1003178825Sdfr	      const krb5_data *req_buffer,
1004178825Sdfr	      krb5_keyblock **key)
1005178825Sdfr{
1006178825Sdfr    ReplyKeyPack key_pack;
1007178825Sdfr    krb5_error_code ret;
1008178825Sdfr    size_t size;
1009178825Sdfr
1010178825Sdfr    ret = decode_ReplyKeyPack(content->data,
1011178825Sdfr			      content->length,
1012178825Sdfr			      &key_pack,
1013178825Sdfr			      &size);
1014178825Sdfr    if (ret) {
1015233294Sstas	krb5_set_error_message(context, ret,
1016233294Sstas			       N_("PKINIT decoding reply key failed", ""));
1017178825Sdfr	free_ReplyKeyPack(&key_pack);
1018178825Sdfr	return ret;
1019178825Sdfr    }
1020233294Sstas
1021178825Sdfr    {
1022178825Sdfr	krb5_crypto crypto;
1023178825Sdfr
1024233294Sstas	/*
1025178825Sdfr	 * XXX Verify kp.replyKey is a allowed enctype in the
1026178825Sdfr	 * configuration file
1027178825Sdfr	 */
1028178825Sdfr
1029178825Sdfr	ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
1030178825Sdfr	if (ret) {
1031178825Sdfr	    free_ReplyKeyPack(&key_pack);
1032178825Sdfr	    return ret;
1033178825Sdfr	}
1034178825Sdfr
1035178825Sdfr	ret = krb5_verify_checksum(context, crypto, 6,
1036178825Sdfr				   req_buffer->data, req_buffer->length,
1037178825Sdfr				   &key_pack.asChecksum);
1038178825Sdfr	krb5_crypto_destroy(context, crypto);
1039178825Sdfr	if (ret) {
1040178825Sdfr	    free_ReplyKeyPack(&key_pack);
1041178825Sdfr	    return ret;
1042178825Sdfr	}
1043178825Sdfr    }
1044178825Sdfr
1045178825Sdfr    *key = malloc (sizeof (**key));
1046178825Sdfr    if (*key == NULL) {
1047178825Sdfr	free_ReplyKeyPack(&key_pack);
1048233294Sstas	krb5_set_error_message(context, ENOMEM,
1049233294Sstas			       N_("malloc: out of memory", ""));
1050178825Sdfr	return ENOMEM;
1051178825Sdfr    }
1052178825Sdfr
1053178825Sdfr    ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1054178825Sdfr    free_ReplyKeyPack(&key_pack);
1055178825Sdfr    if (ret) {
1056233294Sstas	krb5_set_error_message(context, ret,
1057233294Sstas			       N_("PKINIT failed copying reply key", ""));
1058178825Sdfr	free(*key);
1059178825Sdfr	*key = NULL;
1060178825Sdfr    }
1061178825Sdfr
1062178825Sdfr    return ret;
1063178825Sdfr}
1064178825Sdfr
1065178825Sdfr
1066178825Sdfrstatic krb5_error_code
1067178825Sdfrpk_verify_host(krb5_context context,
1068178825Sdfr	       const char *realm,
1069178825Sdfr	       const krb5_krbhst_info *hi,
1070178825Sdfr	       struct krb5_pk_init_ctx_data *ctx,
1071178825Sdfr	       struct krb5_pk_cert *host)
1072178825Sdfr{
1073178825Sdfr    krb5_error_code ret = 0;
1074178825Sdfr
1075178825Sdfr    if (ctx->require_eku) {
1076233294Sstas	ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
1077233294Sstas				   &asn1_oid_id_pkkdcekuoid, 0);
1078178825Sdfr	if (ret) {
1079233294Sstas	    krb5_set_error_message(context, ret,
1080233294Sstas				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
1081178825Sdfr	    return ret;
1082178825Sdfr	}
1083178825Sdfr    }
1084178825Sdfr    if (ctx->require_krbtgt_otherName) {
1085178825Sdfr	hx509_octet_string_list list;
1086233294Sstas	size_t i;
1087178825Sdfr
1088233294Sstas	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
1089178825Sdfr						       host->cert,
1090233294Sstas						       &asn1_oid_id_pkinit_san,
1091178825Sdfr						       &list);
1092178825Sdfr	if (ret) {
1093233294Sstas	    krb5_set_error_message(context, ret,
1094233294Sstas				   N_("Failed to find the PK-INIT "
1095233294Sstas				      "subjectAltName in the KDC "
1096233294Sstas				      "certificate", ""));
1097178825Sdfr
1098178825Sdfr	    return ret;
1099178825Sdfr	}
1100178825Sdfr
1101178825Sdfr	for (i = 0; i < list.len; i++) {
1102178825Sdfr	    KRB5PrincipalName r;
1103178825Sdfr
1104178825Sdfr	    ret = decode_KRB5PrincipalName(list.val[i].data,
1105178825Sdfr					   list.val[i].length,
1106178825Sdfr					   &r,
1107178825Sdfr					   NULL);
1108178825Sdfr	    if (ret) {
1109233294Sstas		krb5_set_error_message(context, ret,
1110233294Sstas				       N_("Failed to decode the PK-INIT "
1111233294Sstas					  "subjectAltName in the "
1112233294Sstas					  "KDC certificate", ""));
1113178825Sdfr
1114178825Sdfr		break;
1115178825Sdfr	    }
1116178825Sdfr
1117178825Sdfr	    if (r.principalName.name_string.len != 2 ||
1118178825Sdfr		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
1119178825Sdfr		strcmp(r.principalName.name_string.val[1], realm) != 0 ||
1120178825Sdfr		strcmp(r.realm, realm) != 0)
1121233294Sstas		{
1122233294Sstas		    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
1123233294Sstas		    krb5_set_error_message(context, ret,
1124233294Sstas					   N_("KDC have wrong realm name in "
1125233294Sstas					      "the certificate", ""));
1126233294Sstas		}
1127178825Sdfr
1128178825Sdfr	    free_KRB5PrincipalName(&r);
1129178825Sdfr	    if (ret)
1130178825Sdfr		break;
1131178825Sdfr	}
1132178825Sdfr	hx509_free_octet_string_list(&list);
1133178825Sdfr    }
1134178825Sdfr    if (ret)
1135178825Sdfr	return ret;
1136233294Sstas
1137178825Sdfr    if (hi) {
1138233294Sstas	ret = hx509_verify_hostname(context->hx509ctx, host->cert,
1139178825Sdfr				    ctx->require_hostname_match,
1140178825Sdfr				    HX509_HN_HOSTNAME,
1141178825Sdfr				    hi->hostname,
1142178825Sdfr				    hi->ai->ai_addr, hi->ai->ai_addrlen);
1143178825Sdfr
1144178825Sdfr	if (ret)
1145233294Sstas	    krb5_set_error_message(context, ret,
1146233294Sstas				   N_("Address mismatch in "
1147233294Sstas				      "the KDC certificate", ""));
1148178825Sdfr    }
1149178825Sdfr    return ret;
1150178825Sdfr}
1151178825Sdfr
1152178825Sdfrstatic krb5_error_code
1153178825Sdfrpk_rd_pa_reply_enckey(krb5_context context,
1154178825Sdfr		      int type,
1155178825Sdfr		      const heim_octet_string *indata,
1156178825Sdfr		      const heim_oid *dataType,
1157178825Sdfr		      const char *realm,
1158178825Sdfr		      krb5_pk_init_ctx ctx,
1159178825Sdfr		      krb5_enctype etype,
1160178825Sdfr		      const krb5_krbhst_info *hi,
1161178825Sdfr	       	      unsigned nonce,
1162178825Sdfr		      const krb5_data *req_buffer,
1163178825Sdfr	       	      PA_DATA *pa,
1164233294Sstas	       	      krb5_keyblock **key)
1165178825Sdfr{
1166178825Sdfr    krb5_error_code ret;
1167178825Sdfr    struct krb5_pk_cert *host = NULL;
1168178825Sdfr    krb5_data content;
1169178825Sdfr    heim_oid contentType = { 0, NULL };
1170233294Sstas    int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
1171178825Sdfr
1172233294Sstas    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
1173233294Sstas	krb5_set_error_message(context, EINVAL,
1174233294Sstas			       N_("PKINIT: Invalid content type", ""));
1175178825Sdfr	return EINVAL;
1176178825Sdfr    }
1177178825Sdfr
1178233294Sstas    if (ctx->type == PKINIT_WIN2K)
1179233294Sstas	flags |= HX509_CMS_UE_ALLOW_WEAK;
1180233294Sstas
1181233294Sstas    ret = hx509_cms_unenvelope(context->hx509ctx,
1182178825Sdfr			       ctx->id->certs,
1183233294Sstas			       flags,
1184178825Sdfr			       indata->data,
1185178825Sdfr			       indata->length,
1186178825Sdfr			       NULL,
1187233294Sstas			       0,
1188178825Sdfr			       &contentType,
1189178825Sdfr			       &content);
1190178825Sdfr    if (ret) {
1191233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
1192233294Sstas		      "Failed to unenvelope CMS data in PK-INIT reply");
1193178825Sdfr	return ret;
1194178825Sdfr    }
1195178825Sdfr    der_free_oid(&contentType);
1196178825Sdfr
1197233294Sstas    /* win2k uses ContentInfo */
1198233294Sstas    if (type == PKINIT_WIN2K) {
1199233294Sstas	heim_oid type2;
1200233294Sstas	heim_octet_string out;
1201178825Sdfr
1202233294Sstas	ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1203233294Sstas	if (ret) {
1204233294Sstas	    /* windows LH with interesting CMS packets */
1205233294Sstas	    size_t ph = 1 + der_length_len(content.length);
1206233294Sstas	    unsigned char *ptr = malloc(content.length + ph);
1207233294Sstas	    size_t l;
1208178825Sdfr
1209233294Sstas	    memcpy(ptr + ph, content.data, content.length);
1210178825Sdfr
1211233294Sstas	    ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
1212233294Sstas					  ASN1_C_UNIV, CONS, UT_Sequence, &l);
1213233294Sstas	    if (ret)
1214233294Sstas		return ret;
1215233294Sstas	    free(content.data);
1216233294Sstas	    content.data = ptr;
1217233294Sstas	    content.length += ph;
1218178825Sdfr
1219233294Sstas	    ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL);
1220233294Sstas	    if (ret)
1221233294Sstas		goto out;
1222233294Sstas	}
1223233294Sstas	if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
1224178825Sdfr	    ret = EINVAL; /* XXX */
1225233294Sstas	    krb5_set_error_message(context, ret,
1226233294Sstas				   N_("PKINIT: Invalid content type", ""));
1227233294Sstas	    der_free_oid(&type2);
1228178825Sdfr	    der_free_octet_string(&out);
1229178825Sdfr	    goto out;
1230178825Sdfr	}
1231233294Sstas	der_free_oid(&type2);
1232178825Sdfr	krb5_data_free(&content);
1233178825Sdfr	ret = krb5_data_copy(&content, out.data, out.length);
1234178825Sdfr	der_free_octet_string(&out);
1235178825Sdfr	if (ret) {
1236233294Sstas	    krb5_set_error_message(context, ret,
1237233294Sstas				   N_("malloc: out of memory", ""));
1238178825Sdfr	    goto out;
1239178825Sdfr	}
1240178825Sdfr    }
1241178825Sdfr
1242233294Sstas    ret = pk_verify_sign(context,
1243233294Sstas			 content.data,
1244233294Sstas			 content.length,
1245233294Sstas			 ctx->id,
1246233294Sstas			 &contentType,
1247233294Sstas			 &content,
1248233294Sstas			 &host);
1249178825Sdfr    if (ret)
1250178825Sdfr	goto out;
1251178825Sdfr
1252178825Sdfr    /* make sure that it is the kdc's certificate */
1253178825Sdfr    ret = pk_verify_host(context, realm, hi, ctx, host);
1254178825Sdfr    if (ret) {
1255178825Sdfr	goto out;
1256178825Sdfr    }
1257178825Sdfr
1258178825Sdfr#if 0
1259233294Sstas    if (type == PKINIT_WIN2K) {
1260233294Sstas	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
1261178825Sdfr	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1262233294Sstas	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1263178825Sdfr	    goto out;
1264178825Sdfr	}
1265178825Sdfr    } else {
1266233294Sstas	if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
1267178825Sdfr	    ret = KRB5KRB_AP_ERR_MSG_TYPE;
1268233294Sstas	    krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
1269178825Sdfr	    goto out;
1270178825Sdfr	}
1271178825Sdfr    }
1272178825Sdfr#endif
1273178825Sdfr
1274178825Sdfr    switch(type) {
1275233294Sstas    case PKINIT_WIN2K:
1276178825Sdfr	ret = get_reply_key(context, &content, req_buffer, key);
1277178825Sdfr	if (ret != 0 && ctx->require_binding == 0)
1278178825Sdfr	    ret = get_reply_key_win(context, &content, nonce, key);
1279178825Sdfr	break;
1280233294Sstas    case PKINIT_27:
1281178825Sdfr	ret = get_reply_key(context, &content, req_buffer, key);
1282178825Sdfr	break;
1283178825Sdfr    }
1284178825Sdfr    if (ret)
1285178825Sdfr	goto out;
1286178825Sdfr
1287178825Sdfr    /* XXX compare given etype with key->etype */
1288178825Sdfr
1289178825Sdfr out:
1290178825Sdfr    if (host)
1291178825Sdfr	_krb5_pk_cert_free(host);
1292178825Sdfr    der_free_oid(&contentType);
1293178825Sdfr    krb5_data_free(&content);
1294178825Sdfr
1295178825Sdfr    return ret;
1296178825Sdfr}
1297178825Sdfr
1298178825Sdfrstatic krb5_error_code
1299178825Sdfrpk_rd_pa_reply_dh(krb5_context context,
1300178825Sdfr		  const heim_octet_string *indata,
1301178825Sdfr		  const heim_oid *dataType,
1302178825Sdfr		  const char *realm,
1303178825Sdfr		  krb5_pk_init_ctx ctx,
1304178825Sdfr		  krb5_enctype etype,
1305178825Sdfr		  const krb5_krbhst_info *hi,
1306178825Sdfr		  const DHNonce *c_n,
1307178825Sdfr		  const DHNonce *k_n,
1308178825Sdfr                  unsigned nonce,
1309178825Sdfr                  PA_DATA *pa,
1310178825Sdfr                  krb5_keyblock **key)
1311178825Sdfr{
1312233294Sstas    const unsigned char *p;
1313233294Sstas    unsigned char *dh_gen_key = NULL;
1314178825Sdfr    struct krb5_pk_cert *host = NULL;
1315178825Sdfr    BIGNUM *kdc_dh_pubkey = NULL;
1316178825Sdfr    KDCDHKeyInfo kdc_dh_info;
1317178825Sdfr    heim_oid contentType = { 0, NULL };
1318178825Sdfr    krb5_data content;
1319178825Sdfr    krb5_error_code ret;
1320233294Sstas    int dh_gen_keylen = 0;
1321178825Sdfr    size_t size;
1322178825Sdfr
1323178825Sdfr    krb5_data_zero(&content);
1324178825Sdfr    memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1325178825Sdfr
1326233294Sstas    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
1327233294Sstas	krb5_set_error_message(context, EINVAL,
1328233294Sstas			       N_("PKINIT: Invalid content type", ""));
1329178825Sdfr	return EINVAL;
1330178825Sdfr    }
1331178825Sdfr
1332233294Sstas    ret = pk_verify_sign(context,
1333233294Sstas			 indata->data,
1334233294Sstas			 indata->length,
1335233294Sstas			 ctx->id,
1336233294Sstas			 &contentType,
1337233294Sstas			 &content,
1338233294Sstas			 &host);
1339178825Sdfr    if (ret)
1340178825Sdfr	goto out;
1341178825Sdfr
1342178825Sdfr    /* make sure that it is the kdc's certificate */
1343178825Sdfr    ret = pk_verify_host(context, realm, hi, ctx, host);
1344178825Sdfr    if (ret)
1345178825Sdfr	goto out;
1346178825Sdfr
1347233294Sstas    if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
1348178825Sdfr	ret = KRB5KRB_AP_ERR_MSG_TYPE;
1349233294Sstas	krb5_set_error_message(context, ret,
1350233294Sstas			       N_("pkinit - dh reply contains wrong oid", ""));
1351178825Sdfr	goto out;
1352178825Sdfr    }
1353178825Sdfr
1354178825Sdfr    ret = decode_KDCDHKeyInfo(content.data,
1355178825Sdfr			      content.length,
1356178825Sdfr			      &kdc_dh_info,
1357178825Sdfr			      &size);
1358178825Sdfr
1359178825Sdfr    if (ret) {
1360233294Sstas	krb5_set_error_message(context, ret,
1361233294Sstas			       N_("pkinit - failed to decode "
1362233294Sstas				  "KDC DH Key Info", ""));
1363178825Sdfr	goto out;
1364178825Sdfr    }
1365178825Sdfr
1366178825Sdfr    if (kdc_dh_info.nonce != nonce) {
1367178825Sdfr	ret = KRB5KRB_AP_ERR_MODIFIED;
1368233294Sstas	krb5_set_error_message(context, ret,
1369233294Sstas			       N_("PKINIT: DH nonce is wrong", ""));
1370178825Sdfr	goto out;
1371178825Sdfr    }
1372178825Sdfr
1373178825Sdfr    if (kdc_dh_info.dhKeyExpiration) {
1374178825Sdfr	if (k_n == NULL) {
1375178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1376233294Sstas	    krb5_set_error_message(context, ret,
1377233294Sstas				   N_("pkinit; got key expiration "
1378233294Sstas				      "without server nonce", ""));
1379178825Sdfr	    goto out;
1380178825Sdfr	}
1381178825Sdfr	if (c_n == NULL) {
1382178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1383233294Sstas	    krb5_set_error_message(context, ret,
1384233294Sstas				   N_("pkinit; got DH reuse but no "
1385233294Sstas				      "client nonce", ""));
1386178825Sdfr	    goto out;
1387178825Sdfr	}
1388178825Sdfr    } else {
1389178825Sdfr	if (k_n) {
1390178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1391233294Sstas	    krb5_set_error_message(context, ret,
1392233294Sstas				   N_("pkinit: got server nonce "
1393233294Sstas				      "without key expiration", ""));
1394178825Sdfr	    goto out;
1395178825Sdfr	}
1396178825Sdfr	c_n = NULL;
1397178825Sdfr    }
1398178825Sdfr
1399178825Sdfr
1400178825Sdfr    p = kdc_dh_info.subjectPublicKey.data;
1401178825Sdfr    size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1402178825Sdfr
1403233294Sstas    if (ctx->keyex == USE_DH) {
1404178825Sdfr	DHPublicKey k;
1405178825Sdfr	ret = decode_DHPublicKey(p, size, &k, NULL);
1406178825Sdfr	if (ret) {
1407233294Sstas	    krb5_set_error_message(context, ret,
1408233294Sstas				   N_("pkinit: can't decode "
1409233294Sstas				      "without key expiration", ""));
1410178825Sdfr	    goto out;
1411178825Sdfr	}
1412178825Sdfr
1413178825Sdfr	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
1414178825Sdfr	free_DHPublicKey(&k);
1415178825Sdfr	if (kdc_dh_pubkey == NULL) {
1416233294Sstas	    ret = ENOMEM;
1417233294Sstas	    goto out;
1418233294Sstas	}
1419233294Sstas
1420233294Sstas
1421233294Sstas	size = DH_size(ctx->u.dh);
1422233294Sstas
1423233294Sstas	dh_gen_key = malloc(size);
1424233294Sstas	if (dh_gen_key == NULL) {
1425233294Sstas	    ret = ENOMEM;
1426233294Sstas	    krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
1427233294Sstas	    goto out;
1428233294Sstas	}
1429233294Sstas
1430233294Sstas	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
1431233294Sstas	if (dh_gen_keylen == -1) {
1432178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1433233294Sstas	    dh_gen_keylen = 0;
1434233294Sstas	    krb5_set_error_message(context, ret,
1435233294Sstas				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
1436178825Sdfr	    goto out;
1437178825Sdfr	}
1438233294Sstas	if (dh_gen_keylen < (int)size) {
1439233294Sstas	    size -= dh_gen_keylen;
1440233294Sstas	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
1441233294Sstas	    memset(dh_gen_key, 0, size);
1442233294Sstas	}
1443178825Sdfr
1444233294Sstas    } else {
1445233294Sstas#ifdef HAVE_OPENSSL
1446233294Sstas	const EC_GROUP *group;
1447233294Sstas	EC_KEY *public = NULL;
1448233294Sstas
1449233294Sstas	group = EC_KEY_get0_group(ctx->u.eckey);
1450233294Sstas
1451233294Sstas	public = EC_KEY_new();
1452233294Sstas	if (public == NULL) {
1453233294Sstas	    ret = ENOMEM;
1454233294Sstas	    goto out;
1455233294Sstas	}
1456233294Sstas	if (EC_KEY_set_group(public, group) != 1) {
1457233294Sstas	    EC_KEY_free(public);
1458233294Sstas	    ret = ENOMEM;
1459233294Sstas	    goto out;
1460233294Sstas	}
1461233294Sstas
1462233294Sstas	if (o2i_ECPublicKey(&public, &p, size) == NULL) {
1463233294Sstas	    EC_KEY_free(public);
1464233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
1465233294Sstas	    krb5_set_error_message(context, ret,
1466233294Sstas				   N_("PKINIT: Can't parse ECDH public key", ""));
1467233294Sstas	    goto out;
1468233294Sstas	}
1469233294Sstas
1470233294Sstas	size = (EC_GROUP_get_degree(group) + 7) / 8;
1471233294Sstas	dh_gen_key = malloc(size);
1472233294Sstas	if (dh_gen_key == NULL) {
1473233294Sstas	    EC_KEY_free(public);
1474233294Sstas	    ret = ENOMEM;
1475233294Sstas	    krb5_set_error_message(context, ret,
1476233294Sstas				   N_("malloc: out of memory", ""));
1477233294Sstas	    goto out;
1478233294Sstas	}
1479233294Sstas	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
1480233294Sstas					 EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
1481233294Sstas	EC_KEY_free(public);
1482233294Sstas	if (dh_gen_keylen == -1) {
1483233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
1484233294Sstas	    dh_gen_keylen = 0;
1485233294Sstas	    krb5_set_error_message(context, ret,
1486233294Sstas				   N_("PKINIT: Can't compute ECDH public key", ""));
1487233294Sstas	    goto out;
1488233294Sstas	}
1489233294Sstas#else
1490233294Sstas	ret = EINVAL;
1491233294Sstas#endif
1492178825Sdfr    }
1493178825Sdfr
1494233294Sstas    if (dh_gen_keylen <= 0) {
1495233294Sstas	ret = EINVAL;
1496233294Sstas	krb5_set_error_message(context, ret,
1497233294Sstas			       N_("PKINIT: resulting DH key <= 0", ""));
1498233294Sstas	dh_gen_keylen = 0;
1499178825Sdfr	goto out;
1500178825Sdfr    }
1501178825Sdfr
1502178825Sdfr    *key = malloc (sizeof (**key));
1503178825Sdfr    if (*key == NULL) {
1504178825Sdfr	ret = ENOMEM;
1505233294Sstas	krb5_set_error_message(context, ret,
1506233294Sstas			       N_("malloc: out of memory", ""));
1507178825Sdfr	goto out;
1508178825Sdfr    }
1509178825Sdfr
1510178825Sdfr    ret = _krb5_pk_octetstring2key(context,
1511178825Sdfr				   etype,
1512178825Sdfr				   dh_gen_key, dh_gen_keylen,
1513178825Sdfr				   c_n, k_n,
1514178825Sdfr				   *key);
1515178825Sdfr    if (ret) {
1516233294Sstas	krb5_set_error_message(context, ret,
1517233294Sstas			       N_("PKINIT: can't create key from DH key", ""));
1518178825Sdfr	free(*key);
1519178825Sdfr	*key = NULL;
1520178825Sdfr	goto out;
1521178825Sdfr    }
1522178825Sdfr
1523178825Sdfr out:
1524178825Sdfr    if (kdc_dh_pubkey)
1525178825Sdfr	BN_free(kdc_dh_pubkey);
1526178825Sdfr    if (dh_gen_key) {
1527233294Sstas	memset(dh_gen_key, 0, dh_gen_keylen);
1528178825Sdfr	free(dh_gen_key);
1529178825Sdfr    }
1530178825Sdfr    if (host)
1531178825Sdfr	_krb5_pk_cert_free(host);
1532178825Sdfr    if (content.data)
1533178825Sdfr	krb5_data_free(&content);
1534178825Sdfr    der_free_oid(&contentType);
1535178825Sdfr    free_KDCDHKeyInfo(&kdc_dh_info);
1536178825Sdfr
1537178825Sdfr    return ret;
1538178825Sdfr}
1539178825Sdfr
1540233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1541178825Sdfr_krb5_pk_rd_pa_reply(krb5_context context,
1542178825Sdfr		     const char *realm,
1543178825Sdfr		     void *c,
1544178825Sdfr		     krb5_enctype etype,
1545178825Sdfr		     const krb5_krbhst_info *hi,
1546178825Sdfr		     unsigned nonce,
1547178825Sdfr		     const krb5_data *req_buffer,
1548178825Sdfr		     PA_DATA *pa,
1549178825Sdfr		     krb5_keyblock **key)
1550178825Sdfr{
1551178825Sdfr    krb5_pk_init_ctx ctx = c;
1552178825Sdfr    krb5_error_code ret;
1553178825Sdfr    size_t size;
1554178825Sdfr
1555178825Sdfr    /* Check for IETF PK-INIT first */
1556233294Sstas    if (ctx->type == PKINIT_27) {
1557178825Sdfr	PA_PK_AS_REP rep;
1558178825Sdfr	heim_octet_string os, data;
1559178825Sdfr	heim_oid oid;
1560233294Sstas
1561178825Sdfr	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1562233294Sstas	    krb5_set_error_message(context, EINVAL,
1563233294Sstas				   N_("PKINIT: wrong padata recv", ""));
1564178825Sdfr	    return EINVAL;
1565178825Sdfr	}
1566178825Sdfr
1567178825Sdfr	ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1568178825Sdfr				  pa->padata_value.length,
1569178825Sdfr				  &rep,
1570178825Sdfr				  &size);
1571178825Sdfr	if (ret) {
1572233294Sstas	    krb5_set_error_message(context, ret,
1573233294Sstas				   N_("Failed to decode pkinit AS rep", ""));
1574178825Sdfr	    return ret;
1575178825Sdfr	}
1576178825Sdfr
1577178825Sdfr	switch (rep.element) {
1578178825Sdfr	case choice_PA_PK_AS_REP_dhInfo:
1579233294Sstas	    _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
1580178825Sdfr	    os = rep.u.dhInfo.dhSignedData;
1581178825Sdfr	    break;
1582178825Sdfr	case choice_PA_PK_AS_REP_encKeyPack:
1583233294Sstas	    _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
1584178825Sdfr	    os = rep.u.encKeyPack;
1585178825Sdfr	    break;
1586233294Sstas	default: {
1587233294Sstas	    PA_PK_AS_REP_BTMM btmm;
1588178825Sdfr	    free_PA_PK_AS_REP(&rep);
1589233294Sstas	    memset(&rep, 0, sizeof(rep));
1590233294Sstas
1591233294Sstas	    _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
1592233294Sstas
1593233294Sstas	    ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
1594233294Sstas					   pa->padata_value.length,
1595233294Sstas					   &btmm,
1596233294Sstas					   &size);
1597233294Sstas	    if (ret) {
1598233294Sstas		krb5_set_error_message(context, EINVAL,
1599233294Sstas				       N_("PKINIT: -27 reply "
1600233294Sstas					  "invalid content type", ""));
1601233294Sstas		return EINVAL;
1602233294Sstas	    }
1603233294Sstas
1604233294Sstas	    if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
1605233294Sstas		free_PA_PK_AS_REP_BTMM(&btmm);
1606233294Sstas		ret = EINVAL;
1607233294Sstas		krb5_set_error_message(context, ret,
1608233294Sstas				       N_("DH mode not supported for BTMM mode", ""));
1609233294Sstas		return ret;
1610233294Sstas	    }
1611233294Sstas
1612233294Sstas	    /*
1613233294Sstas	     * Transform to IETF style PK-INIT reply so that free works below
1614233294Sstas	     */
1615233294Sstas
1616233294Sstas	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1617233294Sstas	    rep.u.encKeyPack.data = btmm.encKeyPack->data;
1618233294Sstas	    rep.u.encKeyPack.length = btmm.encKeyPack->length;
1619233294Sstas	    btmm.encKeyPack->data = NULL;
1620233294Sstas	    btmm.encKeyPack->length = 0;
1621233294Sstas	    free_PA_PK_AS_REP_BTMM(&btmm);
1622233294Sstas	    os = rep.u.encKeyPack;
1623178825Sdfr	}
1624233294Sstas	}
1625178825Sdfr
1626178825Sdfr	ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
1627178825Sdfr	if (ret) {
1628178825Sdfr	    free_PA_PK_AS_REP(&rep);
1629233294Sstas	    krb5_set_error_message(context, ret,
1630233294Sstas				   N_("PKINIT: failed to unwrap CI", ""));
1631178825Sdfr	    return ret;
1632178825Sdfr	}
1633178825Sdfr
1634178825Sdfr	switch (rep.element) {
1635178825Sdfr	case choice_PA_PK_AS_REP_dhInfo:
1636178825Sdfr	    ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi,
1637178825Sdfr				    ctx->clientDHNonce,
1638178825Sdfr				    rep.u.dhInfo.serverDHNonce,
1639178825Sdfr				    nonce, pa, key);
1640178825Sdfr	    break;
1641178825Sdfr	case choice_PA_PK_AS_REP_encKeyPack:
1642233294Sstas	    ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
1643178825Sdfr					ctx, etype, hi, nonce, req_buffer, pa, key);
1644178825Sdfr	    break;
1645178825Sdfr	default:
1646178825Sdfr	    krb5_abortx(context, "pk-init as-rep case not possible to happen");
1647178825Sdfr	}
1648178825Sdfr	der_free_octet_string(&data);
1649178825Sdfr	der_free_oid(&oid);
1650178825Sdfr	free_PA_PK_AS_REP(&rep);
1651178825Sdfr
1652233294Sstas    } else if (ctx->type == PKINIT_WIN2K) {
1653178825Sdfr	PA_PK_AS_REP_Win2k w2krep;
1654178825Sdfr
1655233294Sstas	/* Check for Windows encoding of the AS-REP pa data */
1656178825Sdfr
1657178825Sdfr#if 0 /* should this be ? */
1658178825Sdfr	if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
1659233294Sstas	    krb5_set_error_message(context, EINVAL,
1660233294Sstas				   "PKINIT: wrong padata recv");
1661178825Sdfr	    return EINVAL;
1662178825Sdfr	}
1663178825Sdfr#endif
1664178825Sdfr
1665178825Sdfr	memset(&w2krep, 0, sizeof(w2krep));
1666233294Sstas
1667178825Sdfr	ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1668178825Sdfr					pa->padata_value.length,
1669178825Sdfr					&w2krep,
1670178825Sdfr					&size);
1671178825Sdfr	if (ret) {
1672233294Sstas	    krb5_set_error_message(context, ret,
1673233294Sstas				   N_("PKINIT: Failed decoding windows "
1674233294Sstas				      "pkinit reply %d", ""), (int)ret);
1675178825Sdfr	    return ret;
1676178825Sdfr	}
1677178825Sdfr
1678233294Sstas	krb5_clear_error_message(context);
1679233294Sstas
1680178825Sdfr	switch (w2krep.element) {
1681178825Sdfr	case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
1682178825Sdfr	    heim_octet_string data;
1683178825Sdfr	    heim_oid oid;
1684233294Sstas
1685233294Sstas	    ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
1686178825Sdfr					       &oid, &data, NULL);
1687178825Sdfr	    free_PA_PK_AS_REP_Win2k(&w2krep);
1688178825Sdfr	    if (ret) {
1689233294Sstas		krb5_set_error_message(context, ret,
1690233294Sstas				       N_("PKINIT: failed to unwrap CI", ""));
1691178825Sdfr		return ret;
1692178825Sdfr	    }
1693178825Sdfr
1694233294Sstas	    ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
1695178825Sdfr					ctx, etype, hi, nonce, req_buffer, pa, key);
1696178825Sdfr	    der_free_octet_string(&data);
1697178825Sdfr	    der_free_oid(&oid);
1698178825Sdfr
1699178825Sdfr	    break;
1700178825Sdfr	}
1701178825Sdfr	default:
1702178825Sdfr	    free_PA_PK_AS_REP_Win2k(&w2krep);
1703178825Sdfr	    ret = EINVAL;
1704233294Sstas	    krb5_set_error_message(context, ret,
1705233294Sstas				   N_("PKINIT: win2k reply invalid "
1706233294Sstas				      "content type", ""));
1707178825Sdfr	    break;
1708178825Sdfr	}
1709233294Sstas
1710178825Sdfr    } else {
1711178825Sdfr	ret = EINVAL;
1712233294Sstas	krb5_set_error_message(context, ret,
1713233294Sstas			       N_("PKINIT: unknown reply type", ""));
1714178825Sdfr    }
1715178825Sdfr
1716178825Sdfr    return ret;
1717178825Sdfr}
1718178825Sdfr
1719178825Sdfrstruct prompter {
1720178825Sdfr    krb5_context context;
1721178825Sdfr    krb5_prompter_fct prompter;
1722178825Sdfr    void *prompter_data;
1723178825Sdfr};
1724178825Sdfr
1725233294Sstasstatic int
1726178825Sdfrhx_pass_prompter(void *data, const hx509_prompt *prompter)
1727178825Sdfr{
1728178825Sdfr    krb5_error_code ret;
1729178825Sdfr    krb5_prompt prompt;
1730178825Sdfr    krb5_data password_data;
1731178825Sdfr    struct prompter *p = data;
1732233294Sstas
1733178825Sdfr    password_data.data   = prompter->reply.data;
1734178825Sdfr    password_data.length = prompter->reply.length;
1735178825Sdfr
1736178825Sdfr    prompt.prompt = prompter->prompt;
1737178825Sdfr    prompt.hidden = hx509_prompt_hidden(prompter->type);
1738178825Sdfr    prompt.reply  = &password_data;
1739178825Sdfr
1740178825Sdfr    switch (prompter->type) {
1741178825Sdfr    case HX509_PROMPT_TYPE_INFO:
1742178825Sdfr	prompt.type   = KRB5_PROMPT_TYPE_INFO;
1743178825Sdfr	break;
1744178825Sdfr    case HX509_PROMPT_TYPE_PASSWORD:
1745178825Sdfr    case HX509_PROMPT_TYPE_QUESTION:
1746178825Sdfr    default:
1747178825Sdfr	prompt.type   = KRB5_PROMPT_TYPE_PASSWORD;
1748178825Sdfr	break;
1749233294Sstas    }
1750233294Sstas
1751178825Sdfr    ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
1752178825Sdfr    if (ret) {
1753178825Sdfr	memset (prompter->reply.data, 0, prompter->reply.length);
1754178825Sdfr	return 1;
1755178825Sdfr    }
1756178825Sdfr    return 0;
1757178825Sdfr}
1758178825Sdfr
1759233294Sstasstatic krb5_error_code
1760233294Sstas_krb5_pk_set_user_id(krb5_context context,
1761233294Sstas		     krb5_principal principal,
1762233294Sstas		     krb5_pk_init_ctx ctx,
1763233294Sstas		     struct hx509_certs_data *certs)
1764233294Sstas{
1765233294Sstas    hx509_certs c = hx509_certs_ref(certs);
1766233294Sstas    hx509_query *q = NULL;
1767233294Sstas    int ret;
1768178825Sdfr
1769233294Sstas    if (ctx->id->certs)
1770233294Sstas	hx509_certs_free(&ctx->id->certs);
1771233294Sstas    if (ctx->id->cert) {
1772233294Sstas	hx509_cert_free(ctx->id->cert);
1773233294Sstas	ctx->id->cert = NULL;
1774233294Sstas    }
1775233294Sstas
1776233294Sstas    ctx->id->certs = c;
1777233294Sstas    ctx->anonymous = 0;
1778233294Sstas
1779233294Sstas    ret = hx509_query_alloc(context->hx509ctx, &q);
1780233294Sstas    if (ret) {
1781233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
1782233294Sstas		      "Allocate query to find signing certificate");
1783233294Sstas	return ret;
1784233294Sstas    }
1785233294Sstas
1786233294Sstas    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1787233294Sstas    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1788233294Sstas
1789233294Sstas    if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
1790233294Sstas	ctx->id->flags |= PKINIT_BTMM;
1791233294Sstas    }
1792233294Sstas
1793233294Sstas    ret = find_cert(context, ctx->id, q, &ctx->id->cert);
1794233294Sstas    hx509_query_free(context->hx509ctx, q);
1795233294Sstas
1796233294Sstas    if (ret == 0 && _krb5_have_debug(context, 2)) {
1797233294Sstas	hx509_name name;
1798233294Sstas	char *str, *sn;
1799233294Sstas	heim_integer i;
1800233294Sstas
1801233294Sstas	ret = hx509_cert_get_subject(ctx->id->cert, &name);
1802233294Sstas	if (ret)
1803233294Sstas	    goto out;
1804233294Sstas
1805233294Sstas	ret = hx509_name_to_string(name, &str);
1806233294Sstas	hx509_name_free(&name);
1807233294Sstas	if (ret)
1808233294Sstas	    goto out;
1809233294Sstas
1810233294Sstas	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
1811233294Sstas	if (ret) {
1812233294Sstas	    free(str);
1813233294Sstas	    goto out;
1814233294Sstas	}
1815233294Sstas
1816233294Sstas	ret = der_print_hex_heim_integer(&i, &sn);
1817233294Sstas	der_free_heim_integer(&i);
1818233294Sstas	if (ret) {
1819233294Sstas	    free(name);
1820233294Sstas	    goto out;
1821233294Sstas	}
1822233294Sstas
1823233294Sstas	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
1824233294Sstas	free(str);
1825233294Sstas	free(sn);
1826233294Sstas    }
1827233294Sstas out:
1828233294Sstas
1829233294Sstas    return ret;
1830178825Sdfr}
1831178825Sdfr
1832233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1833178825Sdfr_krb5_pk_load_id(krb5_context context,
1834178825Sdfr		 struct krb5_pk_identity **ret_id,
1835178825Sdfr		 const char *user_id,
1836178825Sdfr		 const char *anchor_id,
1837178825Sdfr		 char * const *chain_list,
1838178825Sdfr		 char * const *revoke_list,
1839178825Sdfr		 krb5_prompter_fct prompter,
1840178825Sdfr		 void *prompter_data,
1841178825Sdfr		 char *password)
1842178825Sdfr{
1843178825Sdfr    struct krb5_pk_identity *id = NULL;
1844178825Sdfr    struct prompter p;
1845178825Sdfr    int ret;
1846178825Sdfr
1847178825Sdfr    *ret_id = NULL;
1848178825Sdfr
1849178825Sdfr    if (anchor_id == NULL) {
1850233294Sstas	krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
1851233294Sstas			       N_("PKINIT: No anchor given", ""));
1852178825Sdfr	return HEIM_PKINIT_NO_VALID_CA;
1853178825Sdfr    }
1854178825Sdfr
1855178825Sdfr    /* load cert */
1856178825Sdfr
1857178825Sdfr    id = calloc(1, sizeof(*id));
1858178825Sdfr    if (id == NULL) {
1859233294Sstas	krb5_set_error_message(context, ENOMEM,
1860233294Sstas			       N_("malloc: out of memory", ""));
1861178825Sdfr	return ENOMEM;
1862233294Sstas    }
1863178825Sdfr
1864233294Sstas    if (user_id) {
1865233294Sstas	hx509_lock lock;
1866178825Sdfr
1867233294Sstas	ret = hx509_lock_init(context->hx509ctx, &lock);
1868233294Sstas	if (ret) {
1869233294Sstas	    pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
1870233294Sstas	    goto out;
1871233294Sstas	}
1872178825Sdfr
1873233294Sstas	if (password && password[0])
1874233294Sstas	    hx509_lock_add_password(lock, password);
1875178825Sdfr
1876233294Sstas	if (prompter) {
1877233294Sstas	    p.context = context;
1878233294Sstas	    p.prompter = prompter;
1879233294Sstas	    p.prompter_data = prompter_data;
1880233294Sstas
1881233294Sstas	    ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
1882233294Sstas	    if (ret) {
1883233294Sstas		hx509_lock_free(lock);
1884233294Sstas		goto out;
1885233294Sstas	    }
1886233294Sstas	}
1887233294Sstas
1888233294Sstas	ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
1889233294Sstas        hx509_lock_free(lock);
1890233294Sstas	if (ret) {
1891233294Sstas	    pk_copy_error(context, context->hx509ctx, ret,
1892233294Sstas			  "Failed to init cert certs");
1893178825Sdfr	    goto out;
1894233294Sstas	}
1895233294Sstas    } else {
1896233294Sstas	id->certs = NULL;
1897178825Sdfr    }
1898178825Sdfr
1899233294Sstas    ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
1900178825Sdfr    if (ret) {
1901233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
1902233294Sstas		      "Failed to init anchors");
1903178825Sdfr	goto out;
1904178825Sdfr    }
1905178825Sdfr
1906233294Sstas    ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
1907178825Sdfr			   0, NULL, &id->certpool);
1908178825Sdfr    if (ret) {
1909233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
1910233294Sstas		      "Failed to init chain");
1911178825Sdfr	goto out;
1912178825Sdfr    }
1913178825Sdfr
1914178825Sdfr    while (chain_list && *chain_list) {
1915233294Sstas	ret = hx509_certs_append(context->hx509ctx, id->certpool,
1916178825Sdfr				 NULL, *chain_list);
1917178825Sdfr	if (ret) {
1918233294Sstas	    pk_copy_error(context, context->hx509ctx, ret,
1919233294Sstas			  "Failed to laod chain %s",
1920233294Sstas			  *chain_list);
1921178825Sdfr	    goto out;
1922178825Sdfr	}
1923178825Sdfr	chain_list++;
1924178825Sdfr    }
1925178825Sdfr
1926178825Sdfr    if (revoke_list) {
1927233294Sstas	ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
1928178825Sdfr	if (ret) {
1929233294Sstas	    pk_copy_error(context, context->hx509ctx, ret,
1930233294Sstas			  "Failed init revoke list");
1931178825Sdfr	    goto out;
1932178825Sdfr	}
1933178825Sdfr
1934178825Sdfr	while (*revoke_list) {
1935233294Sstas	    ret = hx509_revoke_add_crl(context->hx509ctx,
1936178825Sdfr				       id->revokectx,
1937178825Sdfr				       *revoke_list);
1938178825Sdfr	    if (ret) {
1939233294Sstas		pk_copy_error(context, context->hx509ctx, ret,
1940233294Sstas			      "Failed load revoke list");
1941178825Sdfr		goto out;
1942178825Sdfr	    }
1943178825Sdfr	    revoke_list++;
1944178825Sdfr	}
1945178825Sdfr    } else
1946233294Sstas	hx509_context_set_missing_revoke(context->hx509ctx, 1);
1947178825Sdfr
1948233294Sstas    ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
1949178825Sdfr    if (ret) {
1950233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
1951233294Sstas		      "Failed init verify context");
1952178825Sdfr	goto out;
1953178825Sdfr    }
1954178825Sdfr
1955178825Sdfr    hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
1956178825Sdfr    hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
1957178825Sdfr
1958233294Sstas out:
1959178825Sdfr    if (ret) {
1960178825Sdfr	hx509_verify_destroy_ctx(id->verify_ctx);
1961178825Sdfr	hx509_certs_free(&id->certs);
1962178825Sdfr	hx509_certs_free(&id->anchors);
1963178825Sdfr	hx509_certs_free(&id->certpool);
1964178825Sdfr	hx509_revoke_free(&id->revokectx);
1965178825Sdfr	free(id);
1966178825Sdfr    } else
1967178825Sdfr	*ret_id = id;
1968178825Sdfr
1969178825Sdfr    return ret;
1970178825Sdfr}
1971178825Sdfr
1972233294Sstas/*
1973233294Sstas *
1974233294Sstas */
1975233294Sstas
1976233294Sstasstatic void
1977233294Sstaspk_copy_error(krb5_context context,
1978233294Sstas	      hx509_context hx509ctx,
1979233294Sstas	      int hxret,
1980233294Sstas	      const char *fmt,
1981233294Sstas	      ...)
1982178825Sdfr{
1983233294Sstas    va_list va;
1984233294Sstas    char *s, *f;
1985233294Sstas    int ret;
1986178825Sdfr
1987233294Sstas    va_start(va, fmt);
1988233294Sstas    ret = vasprintf(&f, fmt, va);
1989233294Sstas    va_end(va);
1990233294Sstas    if (ret == -1 || f == NULL) {
1991233294Sstas	krb5_clear_error_message(context);
1992233294Sstas	return;
1993178825Sdfr    }
1994178825Sdfr
1995233294Sstas    s = hx509_get_error_string(hx509ctx, hxret);
1996233294Sstas    if (s == NULL) {
1997233294Sstas	krb5_clear_error_message(context);
1998233294Sstas	free(f);
1999233294Sstas	return;
2000233294Sstas    }
2001233294Sstas    krb5_set_error_message(context, hxret, "%s: %s", f, s);
2002233294Sstas    free(s);
2003233294Sstas    free(f);
2004178825Sdfr}
2005178825Sdfr
2006178825Sdfrstatic int
2007233294Sstasparse_integer(krb5_context context, char **p, const char *file, int lineno,
2008178825Sdfr	      const char *name, heim_integer *integer)
2009178825Sdfr{
2010178825Sdfr    int ret;
2011178825Sdfr    char *p1;
2012178825Sdfr    p1 = strsep(p, " \t");
2013178825Sdfr    if (p1 == NULL) {
2014233294Sstas	krb5_set_error_message(context, EINVAL,
2015233294Sstas			       N_("moduli file %s missing %s on line %d", ""),
2016233294Sstas			       file, name, lineno);
2017178825Sdfr	return EINVAL;
2018178825Sdfr    }
2019178825Sdfr    ret = der_parse_hex_heim_integer(p1, integer);
2020178825Sdfr    if (ret) {
2021233294Sstas	krb5_set_error_message(context, ret,
2022233294Sstas			       N_("moduli file %s failed parsing %s "
2023233294Sstas				  "on line %d", ""),
2024233294Sstas			       file, name, lineno);
2025178825Sdfr	return ret;
2026178825Sdfr    }
2027178825Sdfr
2028178825Sdfr    return 0;
2029178825Sdfr}
2030178825Sdfr
2031178825Sdfrkrb5_error_code
2032233294Sstas_krb5_parse_moduli_line(krb5_context context,
2033178825Sdfr			const char *file,
2034178825Sdfr			int lineno,
2035178825Sdfr			char *p,
2036178825Sdfr			struct krb5_dh_moduli **m)
2037178825Sdfr{
2038178825Sdfr    struct krb5_dh_moduli *m1;
2039178825Sdfr    char *p1;
2040178825Sdfr    int ret;
2041178825Sdfr
2042178825Sdfr    *m = NULL;
2043178825Sdfr
2044178825Sdfr    m1 = calloc(1, sizeof(*m1));
2045178825Sdfr    if (m1 == NULL) {
2046233294Sstas	krb5_set_error_message(context, ENOMEM,
2047233294Sstas			       N_("malloc: out of memory", ""));
2048178825Sdfr	return ENOMEM;
2049178825Sdfr    }
2050178825Sdfr
2051178825Sdfr    while (isspace((unsigned char)*p))
2052178825Sdfr	p++;
2053233294Sstas    if (*p  == '#') {
2054233294Sstas        free(m1);
2055178825Sdfr	return 0;
2056233294Sstas    }
2057178825Sdfr    ret = EINVAL;
2058178825Sdfr
2059178825Sdfr    p1 = strsep(&p, " \t");
2060178825Sdfr    if (p1 == NULL) {
2061233294Sstas	krb5_set_error_message(context, ret,
2062233294Sstas			       N_("moduli file %s missing name on line %d", ""),
2063233294Sstas			       file, lineno);
2064178825Sdfr	goto out;
2065178825Sdfr    }
2066178825Sdfr    m1->name = strdup(p1);
2067233294Sstas    if (m1->name == NULL) {
2068178825Sdfr	ret = ENOMEM;
2069233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
2070178825Sdfr	goto out;
2071178825Sdfr    }
2072178825Sdfr
2073178825Sdfr    p1 = strsep(&p, " \t");
2074178825Sdfr    if (p1 == NULL) {
2075233294Sstas	krb5_set_error_message(context, ret,
2076233294Sstas			       N_("moduli file %s missing bits on line %d", ""),
2077233294Sstas			       file, lineno);
2078178825Sdfr	goto out;
2079178825Sdfr    }
2080178825Sdfr
2081178825Sdfr    m1->bits = atoi(p1);
2082178825Sdfr    if (m1->bits == 0) {
2083233294Sstas	krb5_set_error_message(context, ret,
2084233294Sstas			       N_("moduli file %s have un-parsable "
2085233294Sstas				  "bits on line %d", ""), file, lineno);
2086178825Sdfr	goto out;
2087178825Sdfr    }
2088233294Sstas
2089178825Sdfr    ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
2090178825Sdfr    if (ret)
2091178825Sdfr	goto out;
2092178825Sdfr    ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
2093178825Sdfr    if (ret)
2094178825Sdfr	goto out;
2095178825Sdfr    ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
2096178825Sdfr    if (ret)
2097178825Sdfr	goto out;
2098178825Sdfr
2099178825Sdfr    *m = m1;
2100178825Sdfr
2101178825Sdfr    return 0;
2102233294Sstas out:
2103178825Sdfr    free(m1->name);
2104178825Sdfr    der_free_heim_integer(&m1->p);
2105178825Sdfr    der_free_heim_integer(&m1->g);
2106178825Sdfr    der_free_heim_integer(&m1->q);
2107178825Sdfr    free(m1);
2108178825Sdfr    return ret;
2109178825Sdfr}
2110178825Sdfr
2111178825Sdfrvoid
2112178825Sdfr_krb5_free_moduli(struct krb5_dh_moduli **moduli)
2113178825Sdfr{
2114178825Sdfr    int i;
2115178825Sdfr    for (i = 0; moduli[i] != NULL; i++) {
2116178825Sdfr	free(moduli[i]->name);
2117178825Sdfr	der_free_heim_integer(&moduli[i]->p);
2118178825Sdfr	der_free_heim_integer(&moduli[i]->g);
2119178825Sdfr	der_free_heim_integer(&moduli[i]->q);
2120178825Sdfr	free(moduli[i]);
2121178825Sdfr    }
2122178825Sdfr    free(moduli);
2123178825Sdfr}
2124178825Sdfr
2125178825Sdfrstatic const char *default_moduli_RFC2412_MODP_group2 =
2126178825Sdfr    /* name */
2127178825Sdfr    "RFC2412-MODP-group2 "
2128178825Sdfr    /* bits */
2129178825Sdfr    "1024 "
2130178825Sdfr    /* p */
2131178825Sdfr    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2132178825Sdfr    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2133178825Sdfr    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2134178825Sdfr    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2135178825Sdfr    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2136178825Sdfr    "FFFFFFFF" "FFFFFFFF "
2137178825Sdfr    /* g */
2138178825Sdfr    "02 "
2139178825Sdfr    /* q */
2140178825Sdfr    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2141178825Sdfr    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2142178825Sdfr    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2143178825Sdfr    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2144178825Sdfr    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2145178825Sdfr    "FFFFFFFF" "FFFFFFFF";
2146178825Sdfr
2147178825Sdfrstatic const char *default_moduli_rfc3526_MODP_group14 =
2148178825Sdfr    /* name */
2149178825Sdfr    "rfc3526-MODP-group14 "
2150178825Sdfr    /* bits */
2151178825Sdfr    "1760 "
2152178825Sdfr    /* p */
2153178825Sdfr    "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2154178825Sdfr    "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2155178825Sdfr    "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2156178825Sdfr    "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2157178825Sdfr    "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
2158178825Sdfr    "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
2159178825Sdfr    "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
2160178825Sdfr    "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
2161178825Sdfr    "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
2162178825Sdfr    "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
2163178825Sdfr    "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
2164178825Sdfr    /* g */
2165178825Sdfr    "02 "
2166178825Sdfr    /* q */
2167178825Sdfr    "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2168178825Sdfr    "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2169178825Sdfr    "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2170178825Sdfr    "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2171178825Sdfr    "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
2172178825Sdfr    "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
2173178825Sdfr    "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
2174178825Sdfr    "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
2175178825Sdfr    "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
2176178825Sdfr    "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
2177178825Sdfr    "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
2178178825Sdfr
2179178825Sdfrkrb5_error_code
2180178825Sdfr_krb5_parse_moduli(krb5_context context, const char *file,
2181178825Sdfr		   struct krb5_dh_moduli ***moduli)
2182178825Sdfr{
2183178825Sdfr    /* name bits P G Q */
2184178825Sdfr    krb5_error_code ret;
2185178825Sdfr    struct krb5_dh_moduli **m = NULL, **m2;
2186178825Sdfr    char buf[4096];
2187178825Sdfr    FILE *f;
2188178825Sdfr    int lineno = 0, n = 0;
2189178825Sdfr
2190178825Sdfr    *moduli = NULL;
2191178825Sdfr
2192178825Sdfr    m = calloc(1, sizeof(m[0]) * 3);
2193178825Sdfr    if (m == NULL) {
2194233294Sstas	krb5_set_error_message(context, ENOMEM,
2195233294Sstas			       N_("malloc: out of memory", ""));
2196178825Sdfr	return ENOMEM;
2197178825Sdfr    }
2198178825Sdfr
2199178825Sdfr    strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
2200178825Sdfr    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[0]);
2201178825Sdfr    if (ret) {
2202178825Sdfr	_krb5_free_moduli(m);
2203178825Sdfr	return ret;
2204178825Sdfr    }
2205178825Sdfr    n++;
2206178825Sdfr
2207178825Sdfr    strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
2208178825Sdfr    ret = _krb5_parse_moduli_line(context, "builtin", 1, buf,  &m[1]);
2209178825Sdfr    if (ret) {
2210178825Sdfr	_krb5_free_moduli(m);
2211178825Sdfr	return ret;
2212178825Sdfr    }
2213178825Sdfr    n++;
2214178825Sdfr
2215178825Sdfr
2216178825Sdfr    if (file == NULL)
2217178825Sdfr	file = MODULI_FILE;
2218178825Sdfr
2219233294Sstas#ifdef KRB5_USE_PATH_TOKENS
2220233294Sstas    {
2221233294Sstas        char * exp_file;
2222233294Sstas
2223233294Sstas        if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
2224233294Sstas            f = fopen(exp_file, "r");
2225233294Sstas            krb5_xfree(exp_file);
2226233294Sstas        } else {
2227233294Sstas            f = NULL;
2228233294Sstas        }
2229233294Sstas    }
2230233294Sstas#else
2231178825Sdfr    f = fopen(file, "r");
2232233294Sstas#endif
2233233294Sstas
2234178825Sdfr    if (f == NULL) {
2235178825Sdfr	*moduli = m;
2236178825Sdfr	return 0;
2237178825Sdfr    }
2238233294Sstas    rk_cloexec_file(f);
2239178825Sdfr
2240178825Sdfr    while(fgets(buf, sizeof(buf), f) != NULL) {
2241178825Sdfr	struct krb5_dh_moduli *element;
2242178825Sdfr
2243178825Sdfr	buf[strcspn(buf, "\n")] = '\0';
2244178825Sdfr	lineno++;
2245178825Sdfr
2246178825Sdfr	m2 = realloc(m, (n + 2) * sizeof(m[0]));
2247178825Sdfr	if (m2 == NULL) {
2248178825Sdfr	    _krb5_free_moduli(m);
2249233294Sstas	    krb5_set_error_message(context, ENOMEM,
2250233294Sstas				   N_("malloc: out of memory", ""));
2251178825Sdfr	    return ENOMEM;
2252178825Sdfr	}
2253178825Sdfr	m = m2;
2254233294Sstas
2255178825Sdfr	m[n] = NULL;
2256178825Sdfr
2257178825Sdfr	ret = _krb5_parse_moduli_line(context, file, lineno, buf,  &element);
2258178825Sdfr	if (ret) {
2259178825Sdfr	    _krb5_free_moduli(m);
2260178825Sdfr	    return ret;
2261178825Sdfr	}
2262178825Sdfr	if (element == NULL)
2263178825Sdfr	    continue;
2264178825Sdfr
2265178825Sdfr	m[n] = element;
2266178825Sdfr	m[n + 1] = NULL;
2267178825Sdfr	n++;
2268178825Sdfr    }
2269178825Sdfr    *moduli = m;
2270178825Sdfr    return 0;
2271178825Sdfr}
2272178825Sdfr
2273178825Sdfrkrb5_error_code
2274178825Sdfr_krb5_dh_group_ok(krb5_context context, unsigned long bits,
2275178825Sdfr		  heim_integer *p, heim_integer *g, heim_integer *q,
2276178825Sdfr		  struct krb5_dh_moduli **moduli,
2277178825Sdfr		  char **name)
2278178825Sdfr{
2279178825Sdfr    int i;
2280178825Sdfr
2281178825Sdfr    if (name)
2282178825Sdfr	*name = NULL;
2283178825Sdfr
2284178825Sdfr    for (i = 0; moduli[i] != NULL; i++) {
2285178825Sdfr	if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
2286178825Sdfr	    der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
2287178825Sdfr	    (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0))
2288233294Sstas	    {
2289233294Sstas		if (bits && bits > moduli[i]->bits) {
2290233294Sstas		    krb5_set_error_message(context,
2291233294Sstas					   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2292233294Sstas					   N_("PKINIT: DH group parameter %s "
2293233294Sstas					      "no accepted, not enough bits "
2294233294Sstas					      "generated", ""),
2295233294Sstas					   moduli[i]->name);
2296233294Sstas		    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2297233294Sstas		}
2298233294Sstas		if (name)
2299233294Sstas		    *name = strdup(moduli[i]->name);
2300233294Sstas		return 0;
2301178825Sdfr	    }
2302178825Sdfr    }
2303233294Sstas    krb5_set_error_message(context,
2304233294Sstas			   KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
2305233294Sstas			   N_("PKINIT: DH group parameter no ok", ""));
2306178825Sdfr    return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
2307178825Sdfr}
2308233294Sstas#endif /* PKINIT */
2309178825Sdfr
2310233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
2311178825Sdfr_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2312178825Sdfr{
2313178825Sdfr#ifdef PKINIT
2314178825Sdfr    krb5_pk_init_ctx ctx;
2315178825Sdfr
2316178825Sdfr    if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
2317178825Sdfr	return;
2318178825Sdfr    ctx = opt->opt_private->pk_init_ctx;
2319233294Sstas    switch (ctx->keyex) {
2320233294Sstas    case USE_DH:
2321233294Sstas	if (ctx->u.dh)
2322233294Sstas	    DH_free(ctx->u.dh);
2323233294Sstas	break;
2324233294Sstas    case USE_RSA:
2325233294Sstas	break;
2326233294Sstas    case USE_ECDH:
2327233294Sstas#ifdef HAVE_OPENSSL
2328233294Sstas	if (ctx->u.eckey)
2329233294Sstas	    EC_KEY_free(ctx->u.eckey);
2330233294Sstas#endif
2331233294Sstas	break;
2332233294Sstas    }
2333178825Sdfr    if (ctx->id) {
2334178825Sdfr	hx509_verify_destroy_ctx(ctx->id->verify_ctx);
2335178825Sdfr	hx509_certs_free(&ctx->id->certs);
2336233294Sstas	hx509_cert_free(ctx->id->cert);
2337178825Sdfr	hx509_certs_free(&ctx->id->anchors);
2338178825Sdfr	hx509_certs_free(&ctx->id->certpool);
2339178825Sdfr
2340178825Sdfr	if (ctx->clientDHNonce) {
2341178825Sdfr	    krb5_free_data(NULL, ctx->clientDHNonce);
2342178825Sdfr	    ctx->clientDHNonce = NULL;
2343178825Sdfr	}
2344178825Sdfr	if (ctx->m)
2345178825Sdfr	    _krb5_free_moduli(ctx->m);
2346178825Sdfr	free(ctx->id);
2347178825Sdfr	ctx->id = NULL;
2348178825Sdfr    }
2349178825Sdfr    free(opt->opt_private->pk_init_ctx);
2350178825Sdfr    opt->opt_private->pk_init_ctx = NULL;
2351178825Sdfr#endif
2352178825Sdfr}
2353233294Sstas
2354233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2355178825Sdfrkrb5_get_init_creds_opt_set_pkinit(krb5_context context,
2356178825Sdfr				   krb5_get_init_creds_opt *opt,
2357178825Sdfr				   krb5_principal principal,
2358178825Sdfr				   const char *user_id,
2359178825Sdfr				   const char *x509_anchors,
2360178825Sdfr				   char * const * pool,
2361178825Sdfr				   char * const * pki_revoke,
2362178825Sdfr				   int flags,
2363178825Sdfr				   krb5_prompter_fct prompter,
2364178825Sdfr				   void *prompter_data,
2365178825Sdfr				   char *password)
2366178825Sdfr{
2367178825Sdfr#ifdef PKINIT
2368178825Sdfr    krb5_error_code ret;
2369178825Sdfr    char *anchors = NULL;
2370178825Sdfr
2371178825Sdfr    if (opt->opt_private == NULL) {
2372233294Sstas	krb5_set_error_message(context, EINVAL,
2373233294Sstas			       N_("PKINIT: on non extendable opt", ""));
2374178825Sdfr	return EINVAL;
2375178825Sdfr    }
2376178825Sdfr
2377233294Sstas    opt->opt_private->pk_init_ctx =
2378178825Sdfr	calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
2379178825Sdfr    if (opt->opt_private->pk_init_ctx == NULL) {
2380233294Sstas	krb5_set_error_message(context, ENOMEM,
2381233294Sstas			       N_("malloc: out of memory", ""));
2382178825Sdfr	return ENOMEM;
2383178825Sdfr    }
2384178825Sdfr    opt->opt_private->pk_init_ctx->require_binding = 0;
2385178825Sdfr    opt->opt_private->pk_init_ctx->require_eku = 1;
2386178825Sdfr    opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
2387178825Sdfr    opt->opt_private->pk_init_ctx->peer = NULL;
2388178825Sdfr
2389178825Sdfr    /* XXX implement krb5_appdefault_strings  */
2390178825Sdfr    if (pool == NULL)
2391178825Sdfr	pool = krb5_config_get_strings(context, NULL,
2392233294Sstas				       "appdefaults",
2393233294Sstas				       "pkinit_pool",
2394178825Sdfr				       NULL);
2395178825Sdfr
2396178825Sdfr    if (pki_revoke == NULL)
2397178825Sdfr	pki_revoke = krb5_config_get_strings(context, NULL,
2398233294Sstas					     "appdefaults",
2399233294Sstas					     "pkinit_revoke",
2400178825Sdfr					     NULL);
2401178825Sdfr
2402178825Sdfr    if (x509_anchors == NULL) {
2403178825Sdfr	krb5_appdefault_string(context, "kinit",
2404233294Sstas			       krb5_principal_get_realm(context, principal),
2405178825Sdfr			       "pkinit_anchors", NULL, &anchors);
2406178825Sdfr	x509_anchors = anchors;
2407178825Sdfr    }
2408178825Sdfr
2409233294Sstas    if (flags & 4)
2410233294Sstas	opt->opt_private->pk_init_ctx->anonymous = 1;
2411233294Sstas
2412178825Sdfr    ret = _krb5_pk_load_id(context,
2413178825Sdfr			   &opt->opt_private->pk_init_ctx->id,
2414178825Sdfr			   user_id,
2415178825Sdfr			   x509_anchors,
2416178825Sdfr			   pool,
2417178825Sdfr			   pki_revoke,
2418178825Sdfr			   prompter,
2419178825Sdfr			   prompter_data,
2420178825Sdfr			   password);
2421178825Sdfr    if (ret) {
2422178825Sdfr	free(opt->opt_private->pk_init_ctx);
2423178825Sdfr	opt->opt_private->pk_init_ctx = NULL;
2424178825Sdfr	return ret;
2425178825Sdfr    }
2426178825Sdfr
2427233294Sstas    if (opt->opt_private->pk_init_ctx->id->certs) {
2428233294Sstas	_krb5_pk_set_user_id(context,
2429233294Sstas			     principal,
2430233294Sstas			     opt->opt_private->pk_init_ctx,
2431233294Sstas			     opt->opt_private->pk_init_ctx->id->certs);
2432233294Sstas    } else
2433233294Sstas	opt->opt_private->pk_init_ctx->id->cert = NULL;
2434233294Sstas
2435178825Sdfr    if ((flags & 2) == 0) {
2436233294Sstas	hx509_context hx509ctx = context->hx509ctx;
2437233294Sstas	hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
2438178825Sdfr
2439233294Sstas	opt->opt_private->pk_init_ctx->keyex = USE_DH;
2440178825Sdfr
2441233294Sstas	/*
2442233294Sstas	 * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
2443233294Sstas	 */
2444233294Sstas	if (cert) {
2445233294Sstas	    AlgorithmIdentifier alg;
2446178825Sdfr
2447233294Sstas	    ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
2448233294Sstas	    if (ret == 0) {
2449233294Sstas		if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
2450233294Sstas		    opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
2451233294Sstas		free_AlgorithmIdentifier(&alg);
2452233294Sstas	    }
2453178825Sdfr	}
2454178825Sdfr
2455233294Sstas    } else {
2456233294Sstas	opt->opt_private->pk_init_ctx->keyex = USE_RSA;
2457178825Sdfr
2458233294Sstas	if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
2459233294Sstas	    krb5_set_error_message(context, EINVAL,
2460233294Sstas				   N_("No anonymous pkinit support in RSA mode", ""));
2461233294Sstas	    return EINVAL;
2462178825Sdfr	}
2463178825Sdfr    }
2464178825Sdfr
2465178825Sdfr    return 0;
2466178825Sdfr#else
2467233294Sstas    krb5_set_error_message(context, EINVAL,
2468233294Sstas			   N_("no support for PKINIT compiled in", ""));
2469178825Sdfr    return EINVAL;
2470178825Sdfr#endif
2471178825Sdfr}
2472178825Sdfr
2473233294Sstaskrb5_error_code KRB5_LIB_FUNCTION
2474233294Sstaskrb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
2475233294Sstas					      krb5_get_init_creds_opt *opt,
2476233294Sstas					      struct hx509_certs_data *certs)
2477233294Sstas{
2478233294Sstas#ifdef PKINIT
2479233294Sstas    if (opt->opt_private == NULL) {
2480233294Sstas	krb5_set_error_message(context, EINVAL,
2481233294Sstas			       N_("PKINIT: on non extendable opt", ""));
2482233294Sstas	return EINVAL;
2483233294Sstas    }
2484233294Sstas    if (opt->opt_private->pk_init_ctx == NULL) {
2485233294Sstas	krb5_set_error_message(context, EINVAL,
2486233294Sstas			       N_("PKINIT: on pkinit context", ""));
2487233294Sstas	return EINVAL;
2488233294Sstas    }
2489233294Sstas
2490233294Sstas    _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
2491233294Sstas
2492233294Sstas    return 0;
2493233294Sstas#else
2494233294Sstas    krb5_set_error_message(context, EINVAL,
2495233294Sstas			   N_("no support for PKINIT compiled in", ""));
2496233294Sstas    return EINVAL;
2497233294Sstas#endif
2498233294Sstas}
2499233294Sstas
2500233294Sstas#ifdef PKINIT
2501233294Sstas
2502233294Sstasstatic int
2503233294Sstasget_ms_san(hx509_context context, hx509_cert cert, char **upn)
2504233294Sstas{
2505233294Sstas    hx509_octet_string_list list;
2506233294Sstas    int ret;
2507233294Sstas
2508233294Sstas    *upn = NULL;
2509233294Sstas
2510233294Sstas    ret = hx509_cert_find_subjectAltName_otherName(context,
2511233294Sstas						   cert,
2512233294Sstas						   &asn1_oid_id_pkinit_ms_san,
2513233294Sstas						   &list);
2514233294Sstas    if (ret)
2515233294Sstas	return 0;
2516233294Sstas
2517233294Sstas    if (list.len > 0 && list.val[0].length > 0)
2518233294Sstas	ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
2519233294Sstas				upn, NULL);
2520233294Sstas    else
2521233294Sstas	ret = 1;
2522233294Sstas    hx509_free_octet_string_list(&list);
2523233294Sstas
2524233294Sstas    return ret;
2525233294Sstas}
2526233294Sstas
2527233294Sstasstatic int
2528233294Sstasfind_ms_san(hx509_context context, hx509_cert cert, void *ctx)
2529233294Sstas{
2530233294Sstas    char *upn;
2531233294Sstas    int ret;
2532233294Sstas
2533233294Sstas    ret = get_ms_san(context, cert, &upn);
2534233294Sstas    if (ret == 0)
2535233294Sstas	free(upn);
2536233294Sstas    return ret;
2537233294Sstas}
2538233294Sstas
2539233294Sstas
2540233294Sstas
2541233294Sstas#endif
2542233294Sstas
2543178825Sdfr/*
2544233294Sstas * Private since it need to be redesigned using krb5_get_init_creds()
2545178825Sdfr */
2546178825Sdfr
2547233294SstasKRB5_LIB_FUNCTION krb5_error_code  KRB5_LIB_CALL
2548233294Sstaskrb5_pk_enterprise_cert(krb5_context context,
2549233294Sstas			const char *user_id,
2550233294Sstas			krb5_const_realm realm,
2551233294Sstas			krb5_principal *principal,
2552233294Sstas			struct hx509_certs_data **res)
2553178825Sdfr{
2554233294Sstas#ifdef PKINIT
2555233294Sstas    krb5_error_code ret;
2556233294Sstas    hx509_certs certs, result;
2557233294Sstas    hx509_cert cert = NULL;
2558233294Sstas    hx509_query *q;
2559233294Sstas    char *name;
2560178825Sdfr
2561233294Sstas    *principal = NULL;
2562233294Sstas    if (res)
2563233294Sstas	*res = NULL;
2564233294Sstas
2565233294Sstas    if (user_id == NULL) {
2566233294Sstas	krb5_set_error_message(context, ENOENT, "no user id");
2567233294Sstas	return ENOENT;
2568178825Sdfr    }
2569178825Sdfr
2570233294Sstas    ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
2571233294Sstas    if (ret) {
2572233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
2573233294Sstas		      "Failed to init cert certs");
2574233294Sstas	goto out;
2575178825Sdfr    }
2576233294Sstas
2577233294Sstas    ret = hx509_query_alloc(context->hx509ctx, &q);
2578233294Sstas    if (ret) {
2579233294Sstas	krb5_set_error_message(context, ret, "out of memory");
2580233294Sstas	hx509_certs_free(&certs);
2581233294Sstas	goto out;
2582233294Sstas    }
2583233294Sstas
2584233294Sstas    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2585233294Sstas    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
2586233294Sstas    hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
2587233294Sstas    hx509_query_match_cmp_func(q, find_ms_san, NULL);
2588233294Sstas
2589233294Sstas    ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
2590233294Sstas    hx509_query_free(context->hx509ctx, q);
2591233294Sstas    hx509_certs_free(&certs);
2592233294Sstas    if (ret) {
2593233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
2594233294Sstas		      "Failed to find PKINIT certificate");
2595233294Sstas	return ret;
2596233294Sstas    }
2597233294Sstas
2598233294Sstas    ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
2599233294Sstas    hx509_certs_free(&result);
2600233294Sstas    if (ret) {
2601233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
2602233294Sstas		      "Failed to get one cert");
2603233294Sstas	goto out;
2604233294Sstas    }
2605233294Sstas
2606233294Sstas    ret = get_ms_san(context->hx509ctx, cert, &name);
2607233294Sstas    if (ret) {
2608233294Sstas	pk_copy_error(context, context->hx509ctx, ret,
2609233294Sstas		      "Failed to get MS SAN");
2610233294Sstas	goto out;
2611233294Sstas    }
2612233294Sstas
2613233294Sstas    ret = krb5_make_principal(context, principal, realm, name, NULL);
2614233294Sstas    free(name);
2615233294Sstas    if (ret)
2616233294Sstas	goto out;
2617233294Sstas
2618233294Sstas    krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
2619233294Sstas
2620233294Sstas    if (res) {
2621233294Sstas	ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
2622233294Sstas	if (ret)
2623233294Sstas	    goto out;
2624233294Sstas
2625233294Sstas	ret = hx509_certs_add(context->hx509ctx, *res, cert);
2626233294Sstas	if (ret) {
2627233294Sstas	    hx509_certs_free(res);
2628233294Sstas	    goto out;
2629233294Sstas	}
2630233294Sstas    }
2631233294Sstas
2632233294Sstas out:
2633233294Sstas    hx509_cert_free(cert);
2634233294Sstas
2635233294Sstas    return ret;
2636233294Sstas#else
2637233294Sstas    krb5_set_error_message(context, EINVAL,
2638233294Sstas			   N_("no support for PKINIT compiled in", ""));
2639233294Sstas    return EINVAL;
2640233294Sstas#endif
2641178825Sdfr}
2642