1178825Sdfr/*
2233294Sstas * Copyright (c) 2003 - 2008 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 "kdc_locl.h"
37178825Sdfr
38178825Sdfr#ifdef PKINIT
39178825Sdfr
40178825Sdfr#include <heim_asn1.h>
41178825Sdfr#include <rfc2459_asn1.h>
42178825Sdfr#include <cms_asn1.h>
43178825Sdfr#include <pkinit_asn1.h>
44178825Sdfr
45178825Sdfr#include <hx509.h>
46178825Sdfr#include "crypto-headers.h"
47178825Sdfr
48178825Sdfrstruct pk_client_params {
49233294Sstas    enum krb5_pk_type type;
50233294Sstas    enum { USE_RSA, USE_DH, USE_ECDH } keyex;
51233294Sstas    union {
52233294Sstas	struct {
53233294Sstas	    BIGNUM *public_key;
54233294Sstas	    DH *key;
55233294Sstas	} dh;
56233294Sstas#ifdef HAVE_OPENSSL
57233294Sstas	struct {
58233294Sstas	    EC_KEY *public_key;
59233294Sstas	    EC_KEY *key;
60233294Sstas	} ecdh;
61233294Sstas#endif
62233294Sstas    } u;
63178825Sdfr    hx509_cert cert;
64178825Sdfr    unsigned nonce;
65178825Sdfr    EncryptionKey reply_key;
66178825Sdfr    char *dh_group_name;
67178825Sdfr    hx509_peer_info peer;
68178825Sdfr    hx509_certs client_anchors;
69233294Sstas    hx509_verify_ctx verify_ctx;
70178825Sdfr};
71178825Sdfr
72178825Sdfrstruct pk_principal_mapping {
73178825Sdfr    unsigned int len;
74178825Sdfr    struct pk_allowed_princ {
75178825Sdfr	krb5_principal principal;
76178825Sdfr	char *subject;
77178825Sdfr    } *val;
78178825Sdfr};
79178825Sdfr
80178825Sdfrstatic struct krb5_pk_identity *kdc_identity;
81178825Sdfrstatic struct pk_principal_mapping principal_mappings;
82178825Sdfrstatic struct krb5_dh_moduli **moduli;
83178825Sdfr
84178825Sdfrstatic struct {
85178825Sdfr    krb5_data data;
86178825Sdfr    time_t expire;
87178825Sdfr    time_t next_update;
88178825Sdfr} ocsp;
89178825Sdfr
90178825Sdfr/*
91178825Sdfr *
92178825Sdfr */
93178825Sdfr
94178825Sdfrstatic krb5_error_code
95178825Sdfrpk_check_pkauthenticator_win2k(krb5_context context,
96178825Sdfr			       PKAuthenticator_Win2k *a,
97178825Sdfr			       const KDC_REQ *req)
98178825Sdfr{
99178825Sdfr    krb5_timestamp now;
100178825Sdfr
101178825Sdfr    krb5_timeofday (context, &now);
102178825Sdfr
103178825Sdfr    /* XXX cusec */
104178825Sdfr    if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
105233294Sstas	krb5_clear_error_message(context);
106178825Sdfr	return KRB5KRB_AP_ERR_SKEW;
107178825Sdfr    }
108178825Sdfr    return 0;
109178825Sdfr}
110178825Sdfr
111178825Sdfrstatic krb5_error_code
112178825Sdfrpk_check_pkauthenticator(krb5_context context,
113178825Sdfr			 PKAuthenticator *a,
114178825Sdfr			 const KDC_REQ *req)
115178825Sdfr{
116178825Sdfr    u_char *buf = NULL;
117178825Sdfr    size_t buf_size;
118178825Sdfr    krb5_error_code ret;
119233294Sstas    size_t len = 0;
120178825Sdfr    krb5_timestamp now;
121178825Sdfr    Checksum checksum;
122178825Sdfr
123178825Sdfr    krb5_timeofday (context, &now);
124178825Sdfr
125178825Sdfr    /* XXX cusec */
126178825Sdfr    if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
127233294Sstas	krb5_clear_error_message(context);
128178825Sdfr	return KRB5KRB_AP_ERR_SKEW;
129178825Sdfr    }
130178825Sdfr
131178825Sdfr    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
132178825Sdfr    if (ret) {
133233294Sstas	krb5_clear_error_message(context);
134178825Sdfr	return ret;
135178825Sdfr    }
136178825Sdfr    if (buf_size != len)
137178825Sdfr	krb5_abortx(context, "Internal error in ASN.1 encoder");
138178825Sdfr
139178825Sdfr    ret = krb5_create_checksum(context,
140178825Sdfr			       NULL,
141178825Sdfr			       0,
142178825Sdfr			       CKSUMTYPE_SHA1,
143178825Sdfr			       buf,
144178825Sdfr			       len,
145178825Sdfr			       &checksum);
146178825Sdfr    free(buf);
147178825Sdfr    if (ret) {
148233294Sstas	krb5_clear_error_message(context);
149178825Sdfr	return ret;
150178825Sdfr    }
151233294Sstas
152178825Sdfr    if (a->paChecksum == NULL) {
153233294Sstas	krb5_clear_error_message(context);
154178825Sdfr	ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
155178825Sdfr	goto out;
156178825Sdfr    }
157178825Sdfr
158178825Sdfr    if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
159233294Sstas	krb5_clear_error_message(context);
160178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
161178825Sdfr    }
162178825Sdfr
163178825Sdfrout:
164178825Sdfr    free_Checksum(&checksum);
165178825Sdfr
166178825Sdfr    return ret;
167178825Sdfr}
168178825Sdfr
169178825Sdfrvoid
170233294Sstas_kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
171178825Sdfr{
172233294Sstas    if (cp == NULL)
173233294Sstas        return;
174233294Sstas    if (cp->cert)
175233294Sstas	hx509_cert_free(cp->cert);
176233294Sstas    if (cp->verify_ctx)
177233294Sstas	hx509_verify_destroy_ctx(cp->verify_ctx);
178233294Sstas    if (cp->keyex == USE_DH) {
179233294Sstas	if (cp->u.dh.key)
180233294Sstas	    DH_free(cp->u.dh.key);
181233294Sstas	if (cp->u.dh.public_key)
182233294Sstas	    BN_free(cp->u.dh.public_key);
183233294Sstas    }
184233294Sstas#ifdef HAVE_OPENSSL
185233294Sstas    if (cp->keyex == USE_ECDH) {
186233294Sstas	if (cp->u.ecdh.key)
187233294Sstas	    EC_KEY_free(cp->u.ecdh.key);
188233294Sstas	if (cp->u.ecdh.public_key)
189233294Sstas	    EC_KEY_free(cp->u.ecdh.public_key);
190233294Sstas    }
191233294Sstas#endif
192233294Sstas    krb5_free_keyblock_contents(context, &cp->reply_key);
193233294Sstas    if (cp->dh_group_name)
194233294Sstas	free(cp->dh_group_name);
195233294Sstas    if (cp->peer)
196233294Sstas	hx509_peer_info_free(cp->peer);
197233294Sstas    if (cp->client_anchors)
198233294Sstas	hx509_certs_free(&cp->client_anchors);
199233294Sstas    memset(cp, 0, sizeof(*cp));
200233294Sstas    free(cp);
201178825Sdfr}
202178825Sdfr
203178825Sdfrstatic krb5_error_code
204233294Sstasgenerate_dh_keyblock(krb5_context context,
205233294Sstas		     pk_client_params *client_params,
206233294Sstas                     krb5_enctype enctype)
207178825Sdfr{
208178825Sdfr    unsigned char *dh_gen_key = NULL;
209178825Sdfr    krb5_keyblock key;
210178825Sdfr    krb5_error_code ret;
211178825Sdfr    size_t dh_gen_keylen, size;
212178825Sdfr
213178825Sdfr    memset(&key, 0, sizeof(key));
214178825Sdfr
215233294Sstas    if (client_params->keyex == USE_DH) {
216178825Sdfr
217233294Sstas	if (client_params->u.dh.public_key == NULL) {
218233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
219233294Sstas	    krb5_set_error_message(context, ret, "public_key");
220233294Sstas	    goto out;
221233294Sstas	}
222178825Sdfr
223233294Sstas	if (!DH_generate_key(client_params->u.dh.key)) {
224233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
225233294Sstas	    krb5_set_error_message(context, ret,
226233294Sstas				   "Can't generate Diffie-Hellman keys");
227233294Sstas	    goto out;
228233294Sstas	}
229178825Sdfr
230233294Sstas	size = DH_size(client_params->u.dh.key);
231233294Sstas
232233294Sstas	dh_gen_key = malloc(size);
233233294Sstas	if (dh_gen_key == NULL) {
234233294Sstas	    ret = ENOMEM;
235233294Sstas	    krb5_set_error_message(context, ret, "malloc: out of memory");
236233294Sstas	    goto out;
237233294Sstas	}
238233294Sstas
239233294Sstas	dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
240233294Sstas	if (dh_gen_keylen == (size_t)-1) {
241233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
242233294Sstas	    krb5_set_error_message(context, ret,
243233294Sstas				   "Can't compute Diffie-Hellman key");
244233294Sstas	    goto out;
245233294Sstas	}
246233294Sstas	if (dh_gen_keylen < size) {
247233294Sstas	    size -= dh_gen_keylen;
248233294Sstas	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
249233294Sstas	    memset(dh_gen_key, 0, size);
250233294Sstas	}
251233294Sstas
252233294Sstas	ret = 0;
253233294Sstas#ifdef HAVE_OPENSSL
254233294Sstas    } else if (client_params->keyex == USE_ECDH) {
255233294Sstas
256233294Sstas	if (client_params->u.ecdh.public_key == NULL) {
257233294Sstas	    ret = KRB5KRB_ERR_GENERIC;
258233294Sstas	    krb5_set_error_message(context, ret, "public_key");
259233294Sstas	    goto out;
260233294Sstas	}
261233294Sstas
262233294Sstas	client_params->u.ecdh.key = EC_KEY_new();
263233294Sstas	if (client_params->u.ecdh.key == NULL) {
264233294Sstas	    ret = ENOMEM;
265233294Sstas	    goto out;
266233294Sstas	}
267233294Sstas	EC_KEY_set_group(client_params->u.ecdh.key,
268233294Sstas			 EC_KEY_get0_group(client_params->u.ecdh.public_key));
269233294Sstas
270233294Sstas	if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) {
271233294Sstas	    ret = ENOMEM;
272233294Sstas	    goto out;
273233294Sstas	}
274233294Sstas
275233294Sstas	size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8;
276233294Sstas	dh_gen_key = malloc(size);
277233294Sstas	if (dh_gen_key == NULL) {
278233294Sstas	    ret = ENOMEM;
279233294Sstas	    krb5_set_error_message(context, ret,
280233294Sstas				   N_("malloc: out of memory", ""));
281233294Sstas	    goto out;
282233294Sstas	}
283233294Sstas
284233294Sstas	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
285233294Sstas					 EC_KEY_get0_public_key(client_params->u.ecdh.public_key),
286233294Sstas					 client_params->u.ecdh.key, NULL);
287233294Sstas
288233294Sstas#endif /* HAVE_OPENSSL */
289233294Sstas    } else {
290178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
291233294Sstas	krb5_set_error_message(context, ret,
292233294Sstas			       "Diffie-Hellman not selected keys");
293178825Sdfr	goto out;
294178825Sdfr    }
295178825Sdfr
296178825Sdfr    ret = _krb5_pk_octetstring2key(context,
297178825Sdfr				   enctype,
298178825Sdfr				   dh_gen_key, dh_gen_keylen,
299178825Sdfr				   NULL, NULL,
300233294Sstas				   &client_params->reply_key);
301178825Sdfr
302178825Sdfr out:
303178825Sdfr    if (dh_gen_key)
304178825Sdfr	free(dh_gen_key);
305178825Sdfr    if (key.keyvalue.data)
306178825Sdfr	krb5_free_keyblock_contents(context, &key);
307178825Sdfr
308178825Sdfr    return ret;
309178825Sdfr}
310178825Sdfr
311178825Sdfrstatic BIGNUM *
312178825Sdfrinteger_to_BN(krb5_context context, const char *field, heim_integer *f)
313178825Sdfr{
314178825Sdfr    BIGNUM *bn;
315178825Sdfr
316178825Sdfr    bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
317178825Sdfr    if (bn == NULL) {
318233294Sstas	krb5_set_error_message(context, KRB5_BADMSGTYPE,
319233294Sstas			       "PKINIT: parsing BN failed %s", field);
320178825Sdfr	return NULL;
321178825Sdfr    }
322178825Sdfr    BN_set_negative(bn, f->negative);
323178825Sdfr    return bn;
324178825Sdfr}
325178825Sdfr
326178825Sdfrstatic krb5_error_code
327178825Sdfrget_dh_param(krb5_context context,
328178825Sdfr	     krb5_kdc_configuration *config,
329178825Sdfr	     SubjectPublicKeyInfo *dh_key_info,
330178825Sdfr	     pk_client_params *client_params)
331178825Sdfr{
332178825Sdfr    DomainParameters dhparam;
333178825Sdfr    DH *dh = NULL;
334178825Sdfr    krb5_error_code ret;
335178825Sdfr
336178825Sdfr    memset(&dhparam, 0, sizeof(dhparam));
337178825Sdfr
338233294Sstas    if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
339233294Sstas	ret = KRB5_BADMSGTYPE;
340233294Sstas	krb5_set_error_message(context, ret,
341233294Sstas			       "PKINIT: subjectPublicKey not aligned "
342233294Sstas			       "to 8 bit boundary");
343233294Sstas	goto out;
344178825Sdfr    }
345178825Sdfr
346178825Sdfr    if (dh_key_info->algorithm.parameters == NULL) {
347233294Sstas	krb5_set_error_message(context, KRB5_BADMSGTYPE,
348233294Sstas			       "PKINIT missing algorithm parameter "
349178825Sdfr			      "in clientPublicValue");
350178825Sdfr	return KRB5_BADMSGTYPE;
351178825Sdfr    }
352178825Sdfr
353178825Sdfr    ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
354178825Sdfr				  dh_key_info->algorithm.parameters->length,
355178825Sdfr				  &dhparam,
356178825Sdfr				  NULL);
357178825Sdfr    if (ret) {
358233294Sstas	krb5_set_error_message(context, ret, "Can't decode algorithm "
359233294Sstas			       "parameters in clientPublicValue");
360178825Sdfr	goto out;
361178825Sdfr    }
362178825Sdfr
363233294Sstas    ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
364178825Sdfr			    &dhparam.p, &dhparam.g, &dhparam.q, moduli,
365178825Sdfr			    &client_params->dh_group_name);
366178825Sdfr    if (ret) {
367178825Sdfr	/* XXX send back proposal of better group */
368178825Sdfr	goto out;
369178825Sdfr    }
370178825Sdfr
371178825Sdfr    dh = DH_new();
372178825Sdfr    if (dh == NULL) {
373178825Sdfr	ret = ENOMEM;
374233294Sstas	krb5_set_error_message(context, ret, "Cannot create DH structure");
375178825Sdfr	goto out;
376178825Sdfr    }
377178825Sdfr    ret = KRB5_BADMSGTYPE;
378178825Sdfr    dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
379178825Sdfr    if (dh->p == NULL)
380178825Sdfr	goto out;
381178825Sdfr    dh->g = integer_to_BN(context, "DH base", &dhparam.g);
382178825Sdfr    if (dh->g == NULL)
383178825Sdfr	goto out;
384178825Sdfr    dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
385178825Sdfr    if (dh->g == NULL)
386178825Sdfr	goto out;
387178825Sdfr
388178825Sdfr    {
389178825Sdfr	heim_integer glue;
390178825Sdfr	size_t size;
391178825Sdfr
392178825Sdfr	ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
393178825Sdfr				 dh_key_info->subjectPublicKey.length / 8,
394178825Sdfr				 &glue,
395178825Sdfr				 &size);
396178825Sdfr	if (ret) {
397233294Sstas	    krb5_clear_error_message(context);
398178825Sdfr	    return ret;
399178825Sdfr	}
400178825Sdfr
401233294Sstas	client_params->u.dh.public_key = integer_to_BN(context,
402233294Sstas						       "subjectPublicKey",
403233294Sstas						       &glue);
404178825Sdfr	der_free_heim_integer(&glue);
405233294Sstas	if (client_params->u.dh.public_key == NULL) {
406233294Sstas	    ret = KRB5_BADMSGTYPE;
407178825Sdfr	    goto out;
408233294Sstas	}
409178825Sdfr    }
410178825Sdfr
411233294Sstas    client_params->u.dh.key = dh;
412178825Sdfr    dh = NULL;
413178825Sdfr    ret = 0;
414233294Sstas
415178825Sdfr out:
416178825Sdfr    if (dh)
417178825Sdfr	DH_free(dh);
418178825Sdfr    free_DomainParameters(&dhparam);
419178825Sdfr    return ret;
420178825Sdfr}
421178825Sdfr
422233294Sstas#ifdef HAVE_OPENSSL
423233294Sstas
424233294Sstasstatic krb5_error_code
425233294Sstasget_ecdh_param(krb5_context context,
426233294Sstas	       krb5_kdc_configuration *config,
427233294Sstas	       SubjectPublicKeyInfo *dh_key_info,
428233294Sstas	       pk_client_params *client_params)
429233294Sstas{
430233294Sstas    ECParameters ecp;
431233294Sstas    EC_KEY *public = NULL;
432233294Sstas    krb5_error_code ret;
433233294Sstas    const unsigned char *p;
434233294Sstas    size_t len;
435233294Sstas    int nid;
436233294Sstas
437233294Sstas    if (dh_key_info->algorithm.parameters == NULL) {
438233294Sstas	krb5_set_error_message(context, KRB5_BADMSGTYPE,
439233294Sstas			       "PKINIT missing algorithm parameter "
440233294Sstas			       "in clientPublicValue");
441233294Sstas	return KRB5_BADMSGTYPE;
442233294Sstas    }
443233294Sstas
444233294Sstas    memset(&ecp, 0, sizeof(ecp));
445233294Sstas
446233294Sstas    ret = decode_ECParameters(dh_key_info->algorithm.parameters->data,
447233294Sstas			      dh_key_info->algorithm.parameters->length, &ecp, &len);
448233294Sstas    if (ret)
449233294Sstas	goto out;
450233294Sstas
451233294Sstas    if (ecp.element != choice_ECParameters_namedCurve) {
452233294Sstas	ret = KRB5_BADMSGTYPE;
453233294Sstas	goto out;
454233294Sstas    }
455233294Sstas
456233294Sstas    if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0)
457233294Sstas	nid = NID_X9_62_prime256v1;
458233294Sstas    else {
459233294Sstas	ret = KRB5_BADMSGTYPE;
460233294Sstas	goto out;
461233294Sstas    }
462233294Sstas
463233294Sstas    /* XXX verify group is ok */
464233294Sstas
465233294Sstas    public = EC_KEY_new_by_curve_name(nid);
466233294Sstas
467233294Sstas    p = dh_key_info->subjectPublicKey.data;
468233294Sstas    len = dh_key_info->subjectPublicKey.length / 8;
469233294Sstas    if (o2i_ECPublicKey(&public, &p, len) == NULL) {
470233294Sstas	ret = KRB5_BADMSGTYPE;
471233294Sstas	krb5_set_error_message(context, ret,
472233294Sstas			       "PKINIT failed to decode ECDH key");
473233294Sstas	goto out;
474233294Sstas    }
475233294Sstas    client_params->u.ecdh.public_key = public;
476233294Sstas    public = NULL;
477233294Sstas
478233294Sstas out:
479233294Sstas    if (public)
480233294Sstas	EC_KEY_free(public);
481233294Sstas    free_ECParameters(&ecp);
482233294Sstas    return ret;
483233294Sstas}
484233294Sstas
485233294Sstas#endif /* HAVE_OPENSSL */
486233294Sstas
487178825Sdfrkrb5_error_code
488178825Sdfr_kdc_pk_rd_padata(krb5_context context,
489178825Sdfr		  krb5_kdc_configuration *config,
490178825Sdfr		  const KDC_REQ *req,
491178825Sdfr		  const PA_DATA *pa,
492233294Sstas		  hdb_entry_ex *client,
493178825Sdfr		  pk_client_params **ret_params)
494178825Sdfr{
495233294Sstas    pk_client_params *cp;
496178825Sdfr    krb5_error_code ret;
497178825Sdfr    heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
498178825Sdfr    krb5_data eContent = { 0, NULL };
499178825Sdfr    krb5_data signed_content = { 0, NULL };
500178825Sdfr    const char *type = "unknown type";
501233294Sstas    hx509_certs trust_anchors;
502178825Sdfr    int have_data = 0;
503233294Sstas    const HDB_Ext_PKINIT_cert *pc;
504178825Sdfr
505178825Sdfr    *ret_params = NULL;
506233294Sstas
507178825Sdfr    if (!config->enable_pkinit) {
508178825Sdfr	kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
509233294Sstas	krb5_clear_error_message(context);
510178825Sdfr	return 0;
511178825Sdfr    }
512178825Sdfr
513233294Sstas    cp = calloc(1, sizeof(*cp));
514233294Sstas    if (cp == NULL) {
515233294Sstas	krb5_clear_error_message(context);
516178825Sdfr	ret = ENOMEM;
517178825Sdfr	goto out;
518178825Sdfr    }
519178825Sdfr
520233294Sstas    ret = hx509_certs_init(context->hx509ctx,
521233294Sstas			   "MEMORY:trust-anchors",
522233294Sstas			   0, NULL, &trust_anchors);
523233294Sstas    if (ret) {
524233294Sstas	krb5_set_error_message(context, ret, "failed to create trust anchors");
525233294Sstas	goto out;
526233294Sstas    }
527233294Sstas
528233294Sstas    ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
529233294Sstas			    kdc_identity->anchors);
530233294Sstas    if (ret) {
531233294Sstas	hx509_certs_free(&trust_anchors);
532233294Sstas	krb5_set_error_message(context, ret, "failed to create verify context");
533233294Sstas	goto out;
534233294Sstas    }
535233294Sstas
536233294Sstas    /* Add any registered certificates for this client as trust anchors */
537233294Sstas    ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
538233294Sstas    if (ret == 0 && pc != NULL) {
539233294Sstas	hx509_cert cert;
540233294Sstas	unsigned int i;
541233294Sstas
542233294Sstas	for (i = 0; i < pc->len; i++) {
543233294Sstas	    ret = hx509_cert_init_data(context->hx509ctx,
544233294Sstas				       pc->val[i].cert.data,
545233294Sstas				       pc->val[i].cert.length,
546233294Sstas				       &cert);
547233294Sstas	    if (ret)
548233294Sstas		continue;
549233294Sstas	    hx509_certs_add(context->hx509ctx, trust_anchors, cert);
550233294Sstas	    hx509_cert_free(cert);
551233294Sstas	}
552233294Sstas    }
553233294Sstas
554233294Sstas    ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
555233294Sstas    if (ret) {
556233294Sstas	hx509_certs_free(&trust_anchors);
557233294Sstas	krb5_set_error_message(context, ret, "failed to create verify context");
558233294Sstas	goto out;
559233294Sstas    }
560233294Sstas
561233294Sstas    hx509_verify_set_time(cp->verify_ctx, kdc_time);
562233294Sstas    hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
563233294Sstas    hx509_certs_free(&trust_anchors);
564233294Sstas
565233294Sstas    if (config->pkinit_allow_proxy_certs)
566233294Sstas	hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
567233294Sstas
568178825Sdfr    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
569178825Sdfr	PA_PK_AS_REQ_Win2k r;
570178825Sdfr
571178825Sdfr	type = "PK-INIT-Win2k";
572178825Sdfr
573233294Sstas	if (req->req_body.kdc_options.request_anonymous) {
574233294Sstas	    ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
575233294Sstas	    krb5_set_error_message(context, ret,
576233294Sstas				   "Anon not supported in RSA mode");
577233294Sstas	    goto out;
578233294Sstas	}
579233294Sstas
580178825Sdfr	ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
581178825Sdfr					pa->padata_value.length,
582178825Sdfr					&r,
583178825Sdfr					NULL);
584178825Sdfr	if (ret) {
585233294Sstas	    krb5_set_error_message(context, ret, "Can't decode "
586233294Sstas				   "PK-AS-REQ-Win2k: %d", ret);
587178825Sdfr	    goto out;
588178825Sdfr	}
589233294Sstas
590178825Sdfr	ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
591178825Sdfr					   &contentInfoOid,
592178825Sdfr					   &signed_content,
593178825Sdfr					   &have_data);
594178825Sdfr	free_PA_PK_AS_REQ_Win2k(&r);
595178825Sdfr	if (ret) {
596233294Sstas	    krb5_set_error_message(context, ret,
597233294Sstas				   "Can't unwrap ContentInfo(win): %d", ret);
598178825Sdfr	    goto out;
599178825Sdfr	}
600178825Sdfr
601178825Sdfr    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
602178825Sdfr	PA_PK_AS_REQ r;
603178825Sdfr
604178825Sdfr	type = "PK-INIT-IETF";
605178825Sdfr
606178825Sdfr	ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
607178825Sdfr				  pa->padata_value.length,
608178825Sdfr				  &r,
609178825Sdfr				  NULL);
610178825Sdfr	if (ret) {
611233294Sstas	    krb5_set_error_message(context, ret,
612233294Sstas				   "Can't decode PK-AS-REQ: %d", ret);
613178825Sdfr	    goto out;
614178825Sdfr	}
615233294Sstas
616178825Sdfr	/* XXX look at r.kdcPkId */
617178825Sdfr	if (r.trustedCertifiers) {
618178825Sdfr	    ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
619233294Sstas	    unsigned int i, maxedi;
620178825Sdfr
621233294Sstas	    ret = hx509_certs_init(context->hx509ctx,
622178825Sdfr				   "MEMORY:client-anchors",
623178825Sdfr				   0, NULL,
624233294Sstas				   &cp->client_anchors);
625178825Sdfr	    if (ret) {
626233294Sstas		krb5_set_error_message(context, ret,
627233294Sstas				       "Can't allocate client anchors: %d",
628233294Sstas				       ret);
629178825Sdfr		goto out;
630178825Sdfr
631178825Sdfr	    }
632233294Sstas	    /*
633233294Sstas	     * If the client sent more then 10 EDI, don't bother
634233294Sstas	     * looking more then 10 of performance reasons.
635233294Sstas	     */
636233294Sstas	    maxedi = edi->len;
637233294Sstas	    if (maxedi > 10)
638233294Sstas		maxedi = 10;
639233294Sstas	    for (i = 0; i < maxedi; i++) {
640178825Sdfr		IssuerAndSerialNumber iasn;
641178825Sdfr		hx509_query *q;
642178825Sdfr		hx509_cert cert;
643178825Sdfr		size_t size;
644178825Sdfr
645178825Sdfr		if (edi->val[i].issuerAndSerialNumber == NULL)
646178825Sdfr		    continue;
647178825Sdfr
648233294Sstas		ret = hx509_query_alloc(context->hx509ctx, &q);
649178825Sdfr		if (ret) {
650233294Sstas		    krb5_set_error_message(context, ret,
651178825Sdfr					  "Failed to allocate hx509_query");
652178825Sdfr		    goto out;
653178825Sdfr		}
654233294Sstas
655178825Sdfr		ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
656178825Sdfr						   edi->val[i].issuerAndSerialNumber->length,
657178825Sdfr						   &iasn,
658178825Sdfr						   &size);
659178825Sdfr		if (ret) {
660233294Sstas		    hx509_query_free(context->hx509ctx, q);
661178825Sdfr		    continue;
662178825Sdfr		}
663178825Sdfr		ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
664178825Sdfr		free_IssuerAndSerialNumber(&iasn);
665233294Sstas		if (ret) {
666233294Sstas		    hx509_query_free(context->hx509ctx, q);
667178825Sdfr		    continue;
668233294Sstas		}
669178825Sdfr
670233294Sstas		ret = hx509_certs_find(context->hx509ctx,
671178825Sdfr				       kdc_identity->certs,
672178825Sdfr				       q,
673178825Sdfr				       &cert);
674233294Sstas		hx509_query_free(context->hx509ctx, q);
675178825Sdfr		if (ret)
676178825Sdfr		    continue;
677233294Sstas		hx509_certs_add(context->hx509ctx,
678233294Sstas				cp->client_anchors, cert);
679178825Sdfr		hx509_cert_free(cert);
680178825Sdfr	    }
681178825Sdfr	}
682178825Sdfr
683178825Sdfr	ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
684178825Sdfr					   &contentInfoOid,
685178825Sdfr					   &signed_content,
686178825Sdfr					   &have_data);
687178825Sdfr	free_PA_PK_AS_REQ(&r);
688178825Sdfr	if (ret) {
689233294Sstas	    krb5_set_error_message(context, ret,
690233294Sstas				   "Can't unwrap ContentInfo: %d", ret);
691178825Sdfr	    goto out;
692178825Sdfr	}
693178825Sdfr
694233294Sstas    } else {
695233294Sstas	krb5_clear_error_message(context);
696178825Sdfr	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
697178825Sdfr	goto out;
698178825Sdfr    }
699178825Sdfr
700233294Sstas    ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
701178825Sdfr    if (ret != 0) {
702178825Sdfr	ret = KRB5KRB_ERR_GENERIC;
703233294Sstas	krb5_set_error_message(context, ret,
704233294Sstas			       "PK-AS-REQ-Win2k invalid content type oid");
705178825Sdfr	goto out;
706178825Sdfr    }
707233294Sstas
708178825Sdfr    if (!have_data) {
709233294Sstas	ret = KRB5KRB_ERR_GENERIC;
710233294Sstas	krb5_set_error_message(context, ret,
711178825Sdfr			      "PK-AS-REQ-Win2k no signed auth pack");
712178825Sdfr	goto out;
713178825Sdfr    }
714178825Sdfr
715178825Sdfr    {
716178825Sdfr	hx509_certs signer_certs;
717233294Sstas	int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
718178825Sdfr
719233294Sstas	if (req->req_body.kdc_options.request_anonymous)
720233294Sstas	    flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
721233294Sstas
722233294Sstas	ret = hx509_cms_verify_signed(context->hx509ctx,
723233294Sstas				      cp->verify_ctx,
724233294Sstas				      flags,
725178825Sdfr				      signed_content.data,
726178825Sdfr				      signed_content.length,
727178825Sdfr				      NULL,
728178825Sdfr				      kdc_identity->certpool,
729178825Sdfr				      &eContentType,
730178825Sdfr				      &eContent,
731178825Sdfr				      &signer_certs);
732178825Sdfr	if (ret) {
733233294Sstas	    char *s = hx509_get_error_string(context->hx509ctx, ret);
734178825Sdfr	    krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
735178825Sdfr		       s, ret);
736178825Sdfr	    free(s);
737178825Sdfr	    goto out;
738178825Sdfr	}
739178825Sdfr
740233294Sstas	if (signer_certs) {
741233294Sstas	    ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
742233294Sstas				     &cp->cert);
743233294Sstas	    hx509_certs_free(&signer_certs);
744233294Sstas	}
745178825Sdfr	if (ret)
746178825Sdfr	    goto out;
747178825Sdfr    }
748178825Sdfr
749178825Sdfr    /* Signature is correct, now verify the signed message */
750233294Sstas    if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
751233294Sstas	der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
752178825Sdfr    {
753178825Sdfr	ret = KRB5_BADMSGTYPE;
754233294Sstas	krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
755178825Sdfr	goto out;
756178825Sdfr    }
757178825Sdfr
758178825Sdfr    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
759178825Sdfr	AuthPack_Win2k ap;
760178825Sdfr
761178825Sdfr	ret = decode_AuthPack_Win2k(eContent.data,
762178825Sdfr				    eContent.length,
763178825Sdfr				    &ap,
764178825Sdfr				    NULL);
765178825Sdfr	if (ret) {
766233294Sstas	    krb5_set_error_message(context, ret,
767233294Sstas				   "Can't decode AuthPack: %d", ret);
768178825Sdfr	    goto out;
769178825Sdfr	}
770233294Sstas
771233294Sstas	ret = pk_check_pkauthenticator_win2k(context,
772178825Sdfr					     &ap.pkAuthenticator,
773178825Sdfr					     req);
774178825Sdfr	if (ret) {
775178825Sdfr	    free_AuthPack_Win2k(&ap);
776178825Sdfr	    goto out;
777178825Sdfr	}
778178825Sdfr
779233294Sstas	cp->type = PKINIT_WIN2K;
780233294Sstas	cp->nonce = ap.pkAuthenticator.nonce;
781178825Sdfr
782178825Sdfr	if (ap.clientPublicValue) {
783178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
784233294Sstas	    krb5_set_error_message(context, ret,
785233294Sstas				   "DH not supported for windows");
786178825Sdfr	    goto out;
787178825Sdfr	}
788178825Sdfr	free_AuthPack_Win2k(&ap);
789178825Sdfr
790178825Sdfr    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
791178825Sdfr	AuthPack ap;
792178825Sdfr
793178825Sdfr	ret = decode_AuthPack(eContent.data,
794178825Sdfr			      eContent.length,
795178825Sdfr			      &ap,
796178825Sdfr			      NULL);
797178825Sdfr	if (ret) {
798233294Sstas	    krb5_set_error_message(context, ret,
799233294Sstas				   "Can't decode AuthPack: %d", ret);
800178825Sdfr	    free_AuthPack(&ap);
801178825Sdfr	    goto out;
802178825Sdfr	}
803233294Sstas
804233294Sstas	if (req->req_body.kdc_options.request_anonymous &&
805233294Sstas	    ap.clientPublicValue == NULL) {
806233294Sstas	    free_AuthPack(&ap);
807233294Sstas	    ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
808233294Sstas	    krb5_set_error_message(context, ret,
809233294Sstas				   "Anon not supported in RSA mode");
810233294Sstas	    goto out;
811233294Sstas	}
812233294Sstas
813233294Sstas	ret = pk_check_pkauthenticator(context,
814178825Sdfr				       &ap.pkAuthenticator,
815178825Sdfr				       req);
816178825Sdfr	if (ret) {
817178825Sdfr	    free_AuthPack(&ap);
818178825Sdfr	    goto out;
819178825Sdfr	}
820178825Sdfr
821233294Sstas	cp->type = PKINIT_27;
822233294Sstas	cp->nonce = ap.pkAuthenticator.nonce;
823178825Sdfr
824178825Sdfr	if (ap.clientPublicValue) {
825233294Sstas	    if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
826233294Sstas		cp->keyex = USE_DH;
827233294Sstas		ret = get_dh_param(context, config,
828233294Sstas				   ap.clientPublicValue, cp);
829233294Sstas#ifdef HAVE_OPENSSL
830233294Sstas	    } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
831233294Sstas		cp->keyex = USE_ECDH;
832233294Sstas		ret = get_ecdh_param(context, config,
833233294Sstas				     ap.clientPublicValue, cp);
834233294Sstas#endif /* HAVE_OPENSSL */
835233294Sstas	    } else {
836233294Sstas		ret = KRB5_BADMSGTYPE;
837233294Sstas		krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
838233294Sstas	    }
839178825Sdfr	    if (ret) {
840178825Sdfr		free_AuthPack(&ap);
841178825Sdfr		goto out;
842178825Sdfr	    }
843233294Sstas	} else
844233294Sstas	    cp->keyex = USE_RSA;
845233294Sstas
846233294Sstas	ret = hx509_peer_info_alloc(context->hx509ctx,
847233294Sstas					&cp->peer);
848233294Sstas	if (ret) {
849233294Sstas	    free_AuthPack(&ap);
850233294Sstas	    goto out;
851178825Sdfr	}
852178825Sdfr
853178825Sdfr	if (ap.supportedCMSTypes) {
854233294Sstas	    ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
855233294Sstas					       cp->peer,
856178825Sdfr					       ap.supportedCMSTypes->val,
857178825Sdfr					       ap.supportedCMSTypes->len);
858178825Sdfr	    if (ret) {
859178825Sdfr		free_AuthPack(&ap);
860178825Sdfr		goto out;
861178825Sdfr	    }
862233294Sstas	} else {
863233294Sstas	    /* assume old client */
864233294Sstas	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
865233294Sstas					hx509_crypto_des_rsdi_ede3_cbc());
866233294Sstas	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
867233294Sstas					hx509_signature_rsa_with_sha1());
868233294Sstas	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
869233294Sstas					hx509_signature_sha1());
870178825Sdfr	}
871178825Sdfr	free_AuthPack(&ap);
872178825Sdfr    } else
873178825Sdfr	krb5_abortx(context, "internal pkinit error");
874178825Sdfr
875178825Sdfr    kdc_log(context, config, 0, "PK-INIT request of type %s", type);
876178825Sdfr
877178825Sdfrout:
878178825Sdfr    if (ret)
879178825Sdfr	krb5_warn(context, ret, "PKINIT");
880178825Sdfr
881178825Sdfr    if (signed_content.data)
882178825Sdfr	free(signed_content.data);
883178825Sdfr    krb5_data_free(&eContent);
884178825Sdfr    der_free_oid(&eContentType);
885178825Sdfr    der_free_oid(&contentInfoOid);
886233294Sstas    if (ret) {
887233294Sstas        _kdc_pk_free_client_param(context, cp);
888233294Sstas    } else
889233294Sstas	*ret_params = cp;
890178825Sdfr    return ret;
891178825Sdfr}
892178825Sdfr
893178825Sdfr/*
894178825Sdfr *
895178825Sdfr */
896178825Sdfr
897178825Sdfrstatic krb5_error_code
898178825SdfrBN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
899178825Sdfr{
900178825Sdfr    integer->length = BN_num_bytes(bn);
901178825Sdfr    integer->data = malloc(integer->length);
902178825Sdfr    if (integer->data == NULL) {
903233294Sstas	krb5_clear_error_message(context);
904178825Sdfr	return ENOMEM;
905178825Sdfr    }
906178825Sdfr    BN_bn2bin(bn, integer->data);
907178825Sdfr    integer->negative = BN_is_negative(bn);
908178825Sdfr    return 0;
909178825Sdfr}
910178825Sdfr
911178825Sdfrstatic krb5_error_code
912178825Sdfrpk_mk_pa_reply_enckey(krb5_context context,
913178825Sdfr		      krb5_kdc_configuration *config,
914233294Sstas		      pk_client_params *cp,
915178825Sdfr		      const KDC_REQ *req,
916178825Sdfr		      const krb5_data *req_buffer,
917178825Sdfr		      krb5_keyblock *reply_key,
918233294Sstas		      ContentInfo *content_info,
919233294Sstas		      hx509_cert *kdc_cert)
920178825Sdfr{
921233294Sstas    const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
922178825Sdfr    krb5_error_code ret;
923178825Sdfr    krb5_data buf, signed_data;
924233294Sstas    size_t size = 0;
925178825Sdfr    int do_win2k = 0;
926178825Sdfr
927178825Sdfr    krb5_data_zero(&buf);
928178825Sdfr    krb5_data_zero(&signed_data);
929178825Sdfr
930233294Sstas    *kdc_cert = NULL;
931233294Sstas
932178825Sdfr    /*
933178825Sdfr     * If the message client is a win2k-type but it send pa data
934178825Sdfr     * 09-binding it expects a IETF (checksum) reply so there can be
935178825Sdfr     * no replay attacks.
936178825Sdfr     */
937178825Sdfr
938233294Sstas    switch (cp->type) {
939233294Sstas    case PKINIT_WIN2K: {
940178825Sdfr	int i = 0;
941178825Sdfr	if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
942178825Sdfr	    && config->pkinit_require_binding == 0)
943178825Sdfr	{
944178825Sdfr	    do_win2k = 1;
945178825Sdfr	}
946233294Sstas	sdAlg = &asn1_oid_id_pkcs7_data;
947233294Sstas	evAlg = &asn1_oid_id_pkcs7_data;
948233294Sstas	envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
949178825Sdfr	break;
950178825Sdfr    }
951233294Sstas    case PKINIT_27:
952233294Sstas	sdAlg = &asn1_oid_id_pkrkeydata;
953233294Sstas	evAlg = &asn1_oid_id_pkcs7_signedData;
954178825Sdfr	break;
955178825Sdfr    default:
956178825Sdfr	krb5_abortx(context, "internal pkinit error");
957233294Sstas    }
958178825Sdfr
959178825Sdfr    if (do_win2k) {
960178825Sdfr	ReplyKeyPack_Win2k kp;
961178825Sdfr	memset(&kp, 0, sizeof(kp));
962178825Sdfr
963178825Sdfr	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
964178825Sdfr	if (ret) {
965233294Sstas	    krb5_clear_error_message(context);
966178825Sdfr	    goto out;
967178825Sdfr	}
968233294Sstas	kp.nonce = cp->nonce;
969233294Sstas
970233294Sstas	ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
971178825Sdfr			   buf.data, buf.length,
972178825Sdfr			   &kp, &size,ret);
973178825Sdfr	free_ReplyKeyPack_Win2k(&kp);
974178825Sdfr    } else {
975178825Sdfr	krb5_crypto ascrypto;
976178825Sdfr	ReplyKeyPack kp;
977178825Sdfr	memset(&kp, 0, sizeof(kp));
978178825Sdfr
979178825Sdfr	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
980178825Sdfr	if (ret) {
981233294Sstas	    krb5_clear_error_message(context);
982178825Sdfr	    goto out;
983178825Sdfr	}
984178825Sdfr
985178825Sdfr	ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
986178825Sdfr	if (ret) {
987233294Sstas	    krb5_clear_error_message(context);
988178825Sdfr	    goto out;
989178825Sdfr	}
990178825Sdfr
991178825Sdfr	ret = krb5_create_checksum(context, ascrypto, 6, 0,
992178825Sdfr				   req_buffer->data, req_buffer->length,
993178825Sdfr				   &kp.asChecksum);
994178825Sdfr	if (ret) {
995233294Sstas	    krb5_clear_error_message(context);
996178825Sdfr	    goto out;
997178825Sdfr	}
998233294Sstas
999178825Sdfr	ret = krb5_crypto_destroy(context, ascrypto);
1000178825Sdfr	if (ret) {
1001233294Sstas	    krb5_clear_error_message(context);
1002178825Sdfr	    goto out;
1003178825Sdfr	}
1004178825Sdfr	ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1005178825Sdfr	free_ReplyKeyPack(&kp);
1006178825Sdfr    }
1007178825Sdfr    if (ret) {
1008233294Sstas	krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1009233294Sstas			       "failed (%d)", ret);
1010178825Sdfr	goto out;
1011178825Sdfr    }
1012178825Sdfr    if (buf.length != size)
1013178825Sdfr	krb5_abortx(context, "Internal ASN.1 encoder error");
1014178825Sdfr
1015178825Sdfr    {
1016178825Sdfr	hx509_query *q;
1017178825Sdfr	hx509_cert cert;
1018233294Sstas
1019233294Sstas	ret = hx509_query_alloc(context->hx509ctx, &q);
1020178825Sdfr	if (ret)
1021178825Sdfr	    goto out;
1022233294Sstas
1023178825Sdfr	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1024233294Sstas	if (config->pkinit_kdc_friendly_name)
1025233294Sstas	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1026233294Sstas
1027233294Sstas	ret = hx509_certs_find(context->hx509ctx,
1028233294Sstas			       kdc_identity->certs,
1029233294Sstas			       q,
1030178825Sdfr			       &cert);
1031233294Sstas	hx509_query_free(context->hx509ctx, q);
1032178825Sdfr	if (ret)
1033178825Sdfr	    goto out;
1034233294Sstas
1035233294Sstas	ret = hx509_cms_create_signed_1(context->hx509ctx,
1036178825Sdfr					0,
1037178825Sdfr					sdAlg,
1038178825Sdfr					buf.data,
1039178825Sdfr					buf.length,
1040178825Sdfr					NULL,
1041178825Sdfr					cert,
1042233294Sstas					cp->peer,
1043233294Sstas					cp->client_anchors,
1044178825Sdfr					kdc_identity->certpool,
1045178825Sdfr					&signed_data);
1046233294Sstas	*kdc_cert = cert;
1047178825Sdfr    }
1048178825Sdfr
1049178825Sdfr    krb5_data_free(&buf);
1050233294Sstas    if (ret)
1051178825Sdfr	goto out;
1052178825Sdfr
1053233294Sstas    if (cp->type == PKINIT_WIN2K) {
1054233294Sstas	ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1055178825Sdfr					 &signed_data,
1056178825Sdfr					 &buf);
1057178825Sdfr	if (ret)
1058178825Sdfr	    goto out;
1059178825Sdfr	krb5_data_free(&signed_data);
1060178825Sdfr	signed_data = buf;
1061178825Sdfr    }
1062178825Sdfr
1063233294Sstas    ret = hx509_cms_envelope_1(context->hx509ctx,
1064233294Sstas			       HX509_CMS_EV_NO_KU_CHECK,
1065233294Sstas			       cp->cert,
1066233294Sstas			       signed_data.data, signed_data.length,
1067178825Sdfr			       envelopedAlg,
1068233294Sstas			       evAlg, &buf);
1069178825Sdfr    if (ret)
1070178825Sdfr	goto out;
1071233294Sstas
1072178825Sdfr    ret = _krb5_pk_mk_ContentInfo(context,
1073178825Sdfr				  &buf,
1074233294Sstas				  &asn1_oid_id_pkcs7_envelopedData,
1075178825Sdfr				  content_info);
1076178825Sdfrout:
1077233294Sstas    if (ret && *kdc_cert) {
1078233294Sstas        hx509_cert_free(*kdc_cert);
1079233294Sstas	*kdc_cert = NULL;
1080233294Sstas    }
1081233294Sstas
1082178825Sdfr    krb5_data_free(&buf);
1083178825Sdfr    krb5_data_free(&signed_data);
1084178825Sdfr    return ret;
1085178825Sdfr}
1086178825Sdfr
1087178825Sdfr/*
1088178825Sdfr *
1089178825Sdfr */
1090178825Sdfr
1091178825Sdfrstatic krb5_error_code
1092178825Sdfrpk_mk_pa_reply_dh(krb5_context context,
1093233294Sstas		  krb5_kdc_configuration *config,
1094233294Sstas      		  pk_client_params *cp,
1095178825Sdfr		  ContentInfo *content_info,
1096178825Sdfr		  hx509_cert *kdc_cert)
1097178825Sdfr{
1098178825Sdfr    KDCDHKeyInfo dh_info;
1099178825Sdfr    krb5_data signed_data, buf;
1100178825Sdfr    ContentInfo contentinfo;
1101178825Sdfr    krb5_error_code ret;
1102233294Sstas    hx509_cert cert;
1103233294Sstas    hx509_query *q;
1104233294Sstas    size_t size = 0;
1105178825Sdfr
1106178825Sdfr    memset(&contentinfo, 0, sizeof(contentinfo));
1107178825Sdfr    memset(&dh_info, 0, sizeof(dh_info));
1108233294Sstas    krb5_data_zero(&signed_data);
1109178825Sdfr    krb5_data_zero(&buf);
1110178825Sdfr
1111178825Sdfr    *kdc_cert = NULL;
1112178825Sdfr
1113233294Sstas    if (cp->keyex == USE_DH) {
1114233294Sstas	DH *kdc_dh = cp->u.dh.key;
1115233294Sstas	heim_integer i;
1116178825Sdfr
1117233294Sstas	ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1118233294Sstas	if (ret)
1119233294Sstas	    return ret;
1120178825Sdfr
1121233294Sstas	ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1122233294Sstas	der_free_heim_integer(&i);
1123233294Sstas	if (ret) {
1124233294Sstas	    krb5_set_error_message(context, ret, "ASN.1 encoding of "
1125233294Sstas				   "DHPublicKey failed (%d)", ret);
1126233294Sstas	    return ret;
1127233294Sstas	}
1128233294Sstas	if (buf.length != size)
1129233294Sstas	    krb5_abortx(context, "Internal ASN.1 encoder error");
1130178825Sdfr
1131233294Sstas	dh_info.subjectPublicKey.length = buf.length * 8;
1132233294Sstas	dh_info.subjectPublicKey.data = buf.data;
1133233294Sstas	krb5_data_zero(&buf);
1134233294Sstas#ifdef HAVE_OPENSSL
1135233294Sstas    } else if (cp->keyex == USE_ECDH) {
1136233294Sstas	unsigned char *p;
1137233294Sstas	int len;
1138233294Sstas
1139233294Sstas	len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1140233294Sstas	if (len <= 0)
1141233294Sstas	    abort();
1142233294Sstas
1143233294Sstas	p = malloc(len);
1144233294Sstas	if (p == NULL)
1145233294Sstas	    abort();
1146233294Sstas
1147233294Sstas	dh_info.subjectPublicKey.length = len * 8;
1148233294Sstas	dh_info.subjectPublicKey.data = p;
1149233294Sstas
1150233294Sstas	len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1151233294Sstas	if (len <= 0)
1152233294Sstas	    abort();
1153233294Sstas#endif
1154233294Sstas    } else
1155233294Sstas	krb5_abortx(context, "no keyex selected ?");
1156233294Sstas
1157233294Sstas
1158233294Sstas    dh_info.nonce = cp->nonce;
1159233294Sstas
1160233294Sstas    ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1161178825Sdfr		       ret);
1162178825Sdfr    if (ret) {
1163233294Sstas	krb5_set_error_message(context, ret, "ASN.1 encoding of "
1164233294Sstas			       "KdcDHKeyInfo failed (%d)", ret);
1165178825Sdfr	goto out;
1166178825Sdfr    }
1167178825Sdfr    if (buf.length != size)
1168178825Sdfr	krb5_abortx(context, "Internal ASN.1 encoder error");
1169178825Sdfr
1170233294Sstas    /*
1171178825Sdfr     * Create the SignedData structure and sign the KdcDHKeyInfo
1172178825Sdfr     * filled in above
1173178825Sdfr     */
1174178825Sdfr
1175233294Sstas    ret = hx509_query_alloc(context->hx509ctx, &q);
1176178825Sdfr    if (ret)
1177178825Sdfr	goto out;
1178178825Sdfr
1179233294Sstas    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1180233294Sstas    if (config->pkinit_kdc_friendly_name)
1181233294Sstas	hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1182233294Sstas
1183233294Sstas    ret = hx509_certs_find(context->hx509ctx,
1184233294Sstas			   kdc_identity->certs,
1185233294Sstas			   q,
1186233294Sstas			   &cert);
1187233294Sstas    hx509_query_free(context->hx509ctx, q);
1188233294Sstas    if (ret)
1189233294Sstas	goto out;
1190233294Sstas
1191233294Sstas    ret = hx509_cms_create_signed_1(context->hx509ctx,
1192233294Sstas				    0,
1193233294Sstas				    &asn1_oid_id_pkdhkeydata,
1194233294Sstas				    buf.data,
1195233294Sstas				    buf.length,
1196233294Sstas				    NULL,
1197233294Sstas				    cert,
1198233294Sstas				    cp->peer,
1199233294Sstas				    cp->client_anchors,
1200233294Sstas				    kdc_identity->certpool,
1201233294Sstas				    &signed_data);
1202233294Sstas    if (ret) {
1203233294Sstas	kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1204233294Sstas	goto out;
1205233294Sstas    }
1206233294Sstas    *kdc_cert = cert;
1207233294Sstas
1208178825Sdfr    ret = _krb5_pk_mk_ContentInfo(context,
1209178825Sdfr				  &signed_data,
1210233294Sstas				  &asn1_oid_id_pkcs7_signedData,
1211178825Sdfr				  content_info);
1212178825Sdfr    if (ret)
1213178825Sdfr	goto out;
1214178825Sdfr
1215178825Sdfr out:
1216178825Sdfr    if (ret && *kdc_cert) {
1217178825Sdfr	hx509_cert_free(*kdc_cert);
1218178825Sdfr	*kdc_cert = NULL;
1219178825Sdfr    }
1220178825Sdfr
1221178825Sdfr    krb5_data_free(&buf);
1222178825Sdfr    krb5_data_free(&signed_data);
1223178825Sdfr    free_KDCDHKeyInfo(&dh_info);
1224178825Sdfr
1225178825Sdfr    return ret;
1226178825Sdfr}
1227178825Sdfr
1228178825Sdfr/*
1229178825Sdfr *
1230178825Sdfr */
1231178825Sdfr
1232178825Sdfrkrb5_error_code
1233178825Sdfr_kdc_pk_mk_pa_reply(krb5_context context,
1234178825Sdfr		    krb5_kdc_configuration *config,
1235233294Sstas		    pk_client_params *cp,
1236178825Sdfr		    const hdb_entry_ex *client,
1237233294Sstas		    krb5_enctype sessionetype,
1238178825Sdfr		    const KDC_REQ *req,
1239178825Sdfr		    const krb5_data *req_buffer,
1240178825Sdfr		    krb5_keyblock **reply_key,
1241233294Sstas		    krb5_keyblock *sessionkey,
1242178825Sdfr		    METHOD_DATA *md)
1243178825Sdfr{
1244178825Sdfr    krb5_error_code ret;
1245233294Sstas    void *buf = NULL;
1246233294Sstas    size_t len = 0, size = 0;
1247178825Sdfr    krb5_enctype enctype;
1248178825Sdfr    int pa_type;
1249178825Sdfr    hx509_cert kdc_cert = NULL;
1250233294Sstas    size_t i;
1251178825Sdfr
1252178825Sdfr    if (!config->enable_pkinit) {
1253233294Sstas	krb5_clear_error_message(context);
1254178825Sdfr	return 0;
1255178825Sdfr    }
1256178825Sdfr
1257178825Sdfr    if (req->req_body.etype.len > 0) {
1258178825Sdfr	for (i = 0; i < req->req_body.etype.len; i++)
1259178825Sdfr	    if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1260178825Sdfr		break;
1261178825Sdfr	if (req->req_body.etype.len <= i) {
1262178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1263233294Sstas	    krb5_set_error_message(context, ret,
1264233294Sstas				   "No valid enctype available from client");
1265178825Sdfr	    goto out;
1266233294Sstas	}
1267178825Sdfr	enctype = req->req_body.etype.val[i];
1268178825Sdfr    } else
1269178825Sdfr	enctype = ETYPE_DES3_CBC_SHA1;
1270178825Sdfr
1271233294Sstas    if (cp->type == PKINIT_27) {
1272178825Sdfr	PA_PK_AS_REP rep;
1273178825Sdfr	const char *type, *other = "";
1274178825Sdfr
1275178825Sdfr	memset(&rep, 0, sizeof(rep));
1276178825Sdfr
1277178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REP;
1278178825Sdfr
1279233294Sstas	if (cp->keyex == USE_RSA) {
1280178825Sdfr	    ContentInfo info;
1281178825Sdfr
1282178825Sdfr	    type = "enckey";
1283178825Sdfr
1284178825Sdfr	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1285178825Sdfr
1286233294Sstas	    ret = krb5_generate_random_keyblock(context, enctype,
1287233294Sstas						&cp->reply_key);
1288178825Sdfr	    if (ret) {
1289178825Sdfr		free_PA_PK_AS_REP(&rep);
1290178825Sdfr		goto out;
1291178825Sdfr	    }
1292178825Sdfr	    ret = pk_mk_pa_reply_enckey(context,
1293178825Sdfr					config,
1294233294Sstas					cp,
1295178825Sdfr					req,
1296178825Sdfr					req_buffer,
1297233294Sstas					&cp->reply_key,
1298233294Sstas					&info,
1299233294Sstas					&kdc_cert);
1300178825Sdfr	    if (ret) {
1301178825Sdfr		free_PA_PK_AS_REP(&rep);
1302178825Sdfr		goto out;
1303178825Sdfr	    }
1304233294Sstas	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1305233294Sstas			       rep.u.encKeyPack.length, &info, &size,
1306178825Sdfr			       ret);
1307178825Sdfr	    free_ContentInfo(&info);
1308178825Sdfr	    if (ret) {
1309233294Sstas		krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1310233294Sstas				       "failed %d", ret);
1311178825Sdfr		free_PA_PK_AS_REP(&rep);
1312178825Sdfr		goto out;
1313178825Sdfr	    }
1314178825Sdfr	    if (rep.u.encKeyPack.length != size)
1315178825Sdfr		krb5_abortx(context, "Internal ASN.1 encoder error");
1316178825Sdfr
1317233294Sstas	    ret = krb5_generate_random_keyblock(context, sessionetype,
1318233294Sstas						sessionkey);
1319233294Sstas	    if (ret) {
1320233294Sstas		free_PA_PK_AS_REP(&rep);
1321233294Sstas		goto out;
1322233294Sstas	    }
1323233294Sstas
1324178825Sdfr	} else {
1325178825Sdfr	    ContentInfo info;
1326178825Sdfr
1327233294Sstas	    switch (cp->keyex) {
1328233294Sstas	    case USE_DH: type = "dh"; break;
1329233294Sstas#ifdef HAVE_OPENSSL
1330233294Sstas	    case USE_ECDH: type = "ecdh"; break;
1331233294Sstas#endif
1332233294Sstas	    default: krb5_abortx(context, "unknown keyex"); break;
1333233294Sstas	    }
1334178825Sdfr
1335233294Sstas	    if (cp->dh_group_name)
1336233294Sstas		other = cp->dh_group_name;
1337233294Sstas
1338178825Sdfr	    rep.element = choice_PA_PK_AS_REP_dhInfo;
1339178825Sdfr
1340233294Sstas	    ret = generate_dh_keyblock(context, cp, enctype);
1341178825Sdfr	    if (ret)
1342178825Sdfr		return ret;
1343178825Sdfr
1344233294Sstas	    ret = pk_mk_pa_reply_dh(context, config,
1345233294Sstas				    cp,
1346178825Sdfr				    &info,
1347178825Sdfr				    &kdc_cert);
1348233294Sstas	    if (ret) {
1349233294Sstas		free_PA_PK_AS_REP(&rep);
1350233294Sstas		krb5_set_error_message(context, ret,
1351233294Sstas				       "create pa-reply-dh "
1352233294Sstas				       "failed %d", ret);
1353233294Sstas		goto out;
1354233294Sstas	    }
1355178825Sdfr
1356178825Sdfr	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1357178825Sdfr			       rep.u.dhInfo.dhSignedData.length, &info, &size,
1358178825Sdfr			       ret);
1359178825Sdfr	    free_ContentInfo(&info);
1360178825Sdfr	    if (ret) {
1361233294Sstas		krb5_set_error_message(context, ret,
1362233294Sstas				       "encoding of Key ContentInfo "
1363233294Sstas				       "failed %d", ret);
1364178825Sdfr		free_PA_PK_AS_REP(&rep);
1365178825Sdfr		goto out;
1366178825Sdfr	    }
1367178825Sdfr	    if (rep.u.encKeyPack.length != size)
1368178825Sdfr		krb5_abortx(context, "Internal ASN.1 encoder error");
1369178825Sdfr
1370233294Sstas	    /* XXX KRB-FX-CF2 */
1371233294Sstas	    ret = krb5_generate_random_keyblock(context, sessionetype,
1372233294Sstas						sessionkey);
1373233294Sstas	    if (ret) {
1374233294Sstas		free_PA_PK_AS_REP(&rep);
1375233294Sstas		goto out;
1376233294Sstas	    }
1377233294Sstas
1378233294Sstas	    /* XXX Add PA-PKINIT-KX */
1379233294Sstas
1380178825Sdfr	}
1381233294Sstas
1382233294Sstas#define use_btmm_with_enckey 0
1383233294Sstas	if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1384233294Sstas	    PA_PK_AS_REP_BTMM btmm;
1385233294Sstas	    heim_any any;
1386233294Sstas
1387233294Sstas	    any.data = rep.u.encKeyPack.data;
1388233294Sstas	    any.length = rep.u.encKeyPack.length;
1389233294Sstas
1390233294Sstas	    btmm.dhSignedData = NULL;
1391233294Sstas	    btmm.encKeyPack = &any;
1392233294Sstas
1393233294Sstas	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1394233294Sstas	} else {
1395233294Sstas	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1396178825Sdfr	}
1397178825Sdfr
1398178825Sdfr	free_PA_PK_AS_REP(&rep);
1399178825Sdfr	if (ret) {
1400233294Sstas	    krb5_set_error_message(context, ret,
1401233294Sstas				   "encode PA-PK-AS-REP failed %d", ret);
1402178825Sdfr	    goto out;
1403178825Sdfr	}
1404178825Sdfr	if (len != size)
1405178825Sdfr	    krb5_abortx(context, "Internal ASN.1 encoder error");
1406178825Sdfr
1407178825Sdfr	kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1408178825Sdfr
1409233294Sstas    } else if (cp->type == PKINIT_WIN2K) {
1410178825Sdfr	PA_PK_AS_REP_Win2k rep;
1411178825Sdfr	ContentInfo info;
1412178825Sdfr
1413233294Sstas	if (cp->keyex != USE_RSA) {
1414178825Sdfr	    ret = KRB5KRB_ERR_GENERIC;
1415233294Sstas	    krb5_set_error_message(context, ret,
1416233294Sstas				   "Windows PK-INIT doesn't support DH");
1417178825Sdfr	    goto out;
1418178825Sdfr	}
1419178825Sdfr
1420178825Sdfr	memset(&rep, 0, sizeof(rep));
1421178825Sdfr
1422178825Sdfr	pa_type = KRB5_PADATA_PK_AS_REP_19;
1423233294Sstas	rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1424178825Sdfr
1425233294Sstas	ret = krb5_generate_random_keyblock(context, enctype,
1426233294Sstas					    &cp->reply_key);
1427178825Sdfr	if (ret) {
1428178825Sdfr	    free_PA_PK_AS_REP_Win2k(&rep);
1429178825Sdfr	    goto out;
1430178825Sdfr	}
1431178825Sdfr	ret = pk_mk_pa_reply_enckey(context,
1432178825Sdfr				    config,
1433233294Sstas				    cp,
1434178825Sdfr				    req,
1435178825Sdfr				    req_buffer,
1436233294Sstas				    &cp->reply_key,
1437233294Sstas				    &info,
1438233294Sstas				    &kdc_cert);
1439178825Sdfr	if (ret) {
1440178825Sdfr	    free_PA_PK_AS_REP_Win2k(&rep);
1441178825Sdfr	    goto out;
1442178825Sdfr	}
1443233294Sstas	ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1444233294Sstas			   rep.u.encKeyPack.length, &info, &size,
1445178825Sdfr			   ret);
1446178825Sdfr	free_ContentInfo(&info);
1447178825Sdfr	if (ret) {
1448233294Sstas	    krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1449178825Sdfr				  "failed %d", ret);
1450178825Sdfr	    free_PA_PK_AS_REP_Win2k(&rep);
1451178825Sdfr	    goto out;
1452178825Sdfr	}
1453178825Sdfr	if (rep.u.encKeyPack.length != size)
1454178825Sdfr	    krb5_abortx(context, "Internal ASN.1 encoder error");
1455178825Sdfr
1456178825Sdfr	ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1457178825Sdfr	free_PA_PK_AS_REP_Win2k(&rep);
1458178825Sdfr	if (ret) {
1459233294Sstas	    krb5_set_error_message(context, ret,
1460178825Sdfr				  "encode PA-PK-AS-REP-Win2k failed %d", ret);
1461178825Sdfr	    goto out;
1462178825Sdfr	}
1463178825Sdfr	if (len != size)
1464178825Sdfr	    krb5_abortx(context, "Internal ASN.1 encoder error");
1465178825Sdfr
1466233294Sstas	ret = krb5_generate_random_keyblock(context, sessionetype,
1467233294Sstas					    sessionkey);
1468233294Sstas	if (ret) {
1469233294Sstas	    free(buf);
1470233294Sstas	    goto out;
1471233294Sstas	}
1472233294Sstas
1473178825Sdfr    } else
1474178825Sdfr	krb5_abortx(context, "PK-INIT internal error");
1475178825Sdfr
1476178825Sdfr
1477178825Sdfr    ret = krb5_padata_add(context, md, pa_type, buf, len);
1478178825Sdfr    if (ret) {
1479233294Sstas	krb5_set_error_message(context, ret,
1480233294Sstas			       "Failed adding PA-PK-AS-REP %d", ret);
1481178825Sdfr	free(buf);
1482178825Sdfr	goto out;
1483178825Sdfr    }
1484178825Sdfr
1485178825Sdfr    if (config->pkinit_kdc_ocsp_file) {
1486178825Sdfr
1487178825Sdfr	if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1488178825Sdfr	    struct stat sb;
1489178825Sdfr	    int fd;
1490178825Sdfr
1491178825Sdfr	    krb5_data_free(&ocsp.data);
1492178825Sdfr
1493178825Sdfr	    ocsp.expire = 0;
1494178825Sdfr	    ocsp.next_update = kdc_time + 60 * 5;
1495178825Sdfr
1496178825Sdfr	    fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1497178825Sdfr	    if (fd < 0) {
1498233294Sstas		kdc_log(context, config, 0,
1499178825Sdfr			"PK-INIT failed to open ocsp data file %d", errno);
1500178825Sdfr		goto out_ocsp;
1501178825Sdfr	    }
1502178825Sdfr	    ret = fstat(fd, &sb);
1503178825Sdfr	    if (ret) {
1504178825Sdfr		ret = errno;
1505178825Sdfr		close(fd);
1506233294Sstas		kdc_log(context, config, 0,
1507178825Sdfr			"PK-INIT failed to stat ocsp data %d", ret);
1508178825Sdfr		goto out_ocsp;
1509178825Sdfr	    }
1510233294Sstas
1511178825Sdfr	    ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1512178825Sdfr	    if (ret) {
1513178825Sdfr		close(fd);
1514233294Sstas		kdc_log(context, config, 0,
1515178825Sdfr			"PK-INIT failed to stat ocsp data %d", ret);
1516178825Sdfr		goto out_ocsp;
1517178825Sdfr	    }
1518178825Sdfr	    ocsp.data.length = sb.st_size;
1519178825Sdfr	    ret = read(fd, ocsp.data.data, sb.st_size);
1520178825Sdfr	    close(fd);
1521178825Sdfr	    if (ret != sb.st_size) {
1522233294Sstas		kdc_log(context, config, 0,
1523178825Sdfr			"PK-INIT failed to read ocsp data %d", errno);
1524178825Sdfr		goto out_ocsp;
1525178825Sdfr	    }
1526178825Sdfr
1527233294Sstas	    ret = hx509_ocsp_verify(context->hx509ctx,
1528178825Sdfr				    kdc_time,
1529178825Sdfr				    kdc_cert,
1530178825Sdfr				    0,
1531178825Sdfr				    ocsp.data.data, ocsp.data.length,
1532178825Sdfr				    &ocsp.expire);
1533178825Sdfr	    if (ret) {
1534233294Sstas		kdc_log(context, config, 0,
1535178825Sdfr			"PK-INIT failed to verify ocsp data %d", ret);
1536178825Sdfr		krb5_data_free(&ocsp.data);
1537178825Sdfr		ocsp.expire = 0;
1538178825Sdfr	    } else if (ocsp.expire > 180) {
1539178825Sdfr		ocsp.expire -= 180; /* refetch the ocsp before it expire */
1540178825Sdfr		ocsp.next_update = ocsp.expire;
1541178825Sdfr	    } else {
1542178825Sdfr		ocsp.next_update = kdc_time;
1543178825Sdfr	    }
1544178825Sdfr	out_ocsp:
1545178825Sdfr	    ret = 0;
1546178825Sdfr	}
1547178825Sdfr
1548178825Sdfr	if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1549178825Sdfr
1550233294Sstas	    ret = krb5_padata_add(context, md,
1551178825Sdfr				  KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1552178825Sdfr				  ocsp.data.data, ocsp.data.length);
1553178825Sdfr	    if (ret) {
1554233294Sstas		krb5_set_error_message(context, ret,
1555233294Sstas				       "Failed adding OCSP response %d", ret);
1556178825Sdfr		goto out;
1557178825Sdfr	    }
1558178825Sdfr	}
1559178825Sdfr    }
1560178825Sdfr
1561178825Sdfrout:
1562178825Sdfr    if (kdc_cert)
1563178825Sdfr	hx509_cert_free(kdc_cert);
1564178825Sdfr
1565178825Sdfr    if (ret == 0)
1566233294Sstas	*reply_key = &cp->reply_key;
1567178825Sdfr    return ret;
1568178825Sdfr}
1569178825Sdfr
1570178825Sdfrstatic int
1571233294Sstasmatch_rfc_san(krb5_context context,
1572178825Sdfr	      krb5_kdc_configuration *config,
1573178825Sdfr	      hx509_context hx509ctx,
1574233294Sstas	      hx509_cert client_cert,
1575178825Sdfr	      krb5_const_principal match)
1576178825Sdfr{
1577178825Sdfr    hx509_octet_string_list list;
1578233294Sstas    int ret, found = 0;
1579233294Sstas    size_t i;
1580178825Sdfr
1581178825Sdfr    memset(&list, 0 , sizeof(list));
1582178825Sdfr
1583178825Sdfr    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1584178825Sdfr						   client_cert,
1585233294Sstas						   &asn1_oid_id_pkinit_san,
1586178825Sdfr						   &list);
1587178825Sdfr    if (ret)
1588178825Sdfr	goto out;
1589178825Sdfr
1590178825Sdfr    for (i = 0; !found && i < list.len; i++) {
1591178825Sdfr	krb5_principal_data principal;
1592178825Sdfr	KRB5PrincipalName kn;
1593178825Sdfr	size_t size;
1594178825Sdfr
1595233294Sstas	ret = decode_KRB5PrincipalName(list.val[i].data,
1596178825Sdfr				       list.val[i].length,
1597178825Sdfr				       &kn, &size);
1598178825Sdfr	if (ret) {
1599233294Sstas	    const char *msg = krb5_get_error_message(context, ret);
1600178825Sdfr	    kdc_log(context, config, 0,
1601233294Sstas		    "Decoding kerberos name in certificate failed: %s", msg);
1602233294Sstas	    krb5_free_error_message(context, msg);
1603178825Sdfr	    break;
1604178825Sdfr	}
1605178825Sdfr	if (size != list.val[i].length) {
1606178825Sdfr	    kdc_log(context, config, 0,
1607178825Sdfr		    "Decoding kerberos name have extra bits on the end");
1608178825Sdfr	    return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1609178825Sdfr	}
1610178825Sdfr
1611178825Sdfr	principal.name = kn.principalName;
1612178825Sdfr	principal.realm = kn.realm;
1613178825Sdfr
1614178825Sdfr	if (krb5_principal_compare(context, &principal, match) == TRUE)
1615178825Sdfr	    found = 1;
1616178825Sdfr	free_KRB5PrincipalName(&kn);
1617178825Sdfr    }
1618178825Sdfr
1619178825Sdfrout:
1620233294Sstas    hx509_free_octet_string_list(&list);
1621178825Sdfr    if (ret)
1622178825Sdfr	return ret;
1623178825Sdfr
1624178825Sdfr    if (!found)
1625178825Sdfr	return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1626178825Sdfr
1627178825Sdfr    return 0;
1628178825Sdfr}
1629178825Sdfr
1630178825Sdfrstatic int
1631233294Sstasmatch_ms_upn_san(krb5_context context,
1632178825Sdfr		 krb5_kdc_configuration *config,
1633178825Sdfr		 hx509_context hx509ctx,
1634233294Sstas		 hx509_cert client_cert,
1635233294Sstas		 HDB *clientdb,
1636233294Sstas		 hdb_entry_ex *client)
1637178825Sdfr{
1638178825Sdfr    hx509_octet_string_list list;
1639178825Sdfr    krb5_principal principal = NULL;
1640233294Sstas    int ret;
1641178825Sdfr    MS_UPN_SAN upn;
1642178825Sdfr    size_t size;
1643178825Sdfr
1644178825Sdfr    memset(&list, 0 , sizeof(list));
1645178825Sdfr
1646178825Sdfr    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1647178825Sdfr						   client_cert,
1648233294Sstas						   &asn1_oid_id_pkinit_ms_san,
1649178825Sdfr						   &list);
1650178825Sdfr    if (ret)
1651178825Sdfr	goto out;
1652178825Sdfr
1653178825Sdfr    if (list.len != 1) {
1654178825Sdfr	kdc_log(context, config, 0,
1655178825Sdfr		"More then one PK-INIT MS UPN SAN");
1656178825Sdfr	goto out;
1657178825Sdfr    }
1658178825Sdfr
1659178825Sdfr    ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1660178825Sdfr    if (ret) {
1661178825Sdfr	kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1662178825Sdfr	goto out;
1663178825Sdfr    }
1664233294Sstas    if (size != list.val[0].length) {
1665233294Sstas	free_MS_UPN_SAN(&upn);
1666233294Sstas	kdc_log(context, config, 0, "Trailing data in ");
1667233294Sstas	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1668233294Sstas	goto out;
1669233294Sstas    }
1670178825Sdfr
1671178825Sdfr    kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1672178825Sdfr
1673178825Sdfr    ret = krb5_parse_name(context, upn, &principal);
1674178825Sdfr    free_MS_UPN_SAN(&upn);
1675178825Sdfr    if (ret) {
1676178825Sdfr	kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1677178825Sdfr	goto out;
1678178825Sdfr    }
1679178825Sdfr
1680233294Sstas    if (clientdb->hdb_check_pkinit_ms_upn_match) {
1681233294Sstas	ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1682233294Sstas    } else {
1683178825Sdfr
1684233294Sstas	/*
1685233294Sstas	 * This is very wrong, but will do for a fallback
1686233294Sstas	 */
1687233294Sstas	strupr(principal->realm);
1688178825Sdfr
1689233294Sstas	if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1690233294Sstas	    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1691233294Sstas    }
1692233294Sstas
1693178825Sdfrout:
1694178825Sdfr    if (principal)
1695178825Sdfr	krb5_free_principal(context, principal);
1696233294Sstas    hx509_free_octet_string_list(&list);
1697178825Sdfr
1698233294Sstas    return ret;
1699178825Sdfr}
1700178825Sdfr
1701178825Sdfrkrb5_error_code
1702178825Sdfr_kdc_pk_check_client(krb5_context context,
1703178825Sdfr		     krb5_kdc_configuration *config,
1704233294Sstas		     HDB *clientdb,
1705233294Sstas		     hdb_entry_ex *client,
1706233294Sstas		     pk_client_params *cp,
1707178825Sdfr		     char **subject_name)
1708178825Sdfr{
1709178825Sdfr    const HDB_Ext_PKINIT_acl *acl;
1710233294Sstas    const HDB_Ext_PKINIT_cert *pc;
1711178825Sdfr    krb5_error_code ret;
1712178825Sdfr    hx509_name name;
1713233294Sstas    size_t i;
1714178825Sdfr
1715233294Sstas    if (cp->cert == NULL) {
1716233294Sstas
1717233294Sstas	*subject_name = strdup("anonymous client client");
1718233294Sstas	if (*subject_name == NULL)
1719233294Sstas	    return ENOMEM;
1720233294Sstas	return 0;
1721233294Sstas    }
1722233294Sstas
1723233294Sstas    ret = hx509_cert_get_base_subject(context->hx509ctx,
1724233294Sstas				      cp->cert,
1725178825Sdfr				      &name);
1726178825Sdfr    if (ret)
1727178825Sdfr	return ret;
1728178825Sdfr
1729178825Sdfr    ret = hx509_name_to_string(name, subject_name);
1730178825Sdfr    hx509_name_free(&name);
1731178825Sdfr    if (ret)
1732178825Sdfr	return ret;
1733178825Sdfr
1734178825Sdfr    kdc_log(context, config, 0,
1735233294Sstas	    "Trying to authorize PK-INIT subject DN %s",
1736178825Sdfr	    *subject_name);
1737178825Sdfr
1738233294Sstas    ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1739233294Sstas    if (ret == 0 && pc) {
1740233294Sstas	hx509_cert cert;
1741233294Sstas	size_t j;
1742233294Sstas
1743233294Sstas	for (j = 0; j < pc->len; j++) {
1744233294Sstas	    ret = hx509_cert_init_data(context->hx509ctx,
1745233294Sstas				       pc->val[j].cert.data,
1746233294Sstas				       pc->val[j].cert.length,
1747233294Sstas				       &cert);
1748233294Sstas	    if (ret)
1749233294Sstas		continue;
1750233294Sstas	    ret = hx509_cert_cmp(cert, cp->cert);
1751233294Sstas	    hx509_cert_free(cert);
1752233294Sstas	    if (ret == 0) {
1753233294Sstas		kdc_log(context, config, 5,
1754233294Sstas			"Found matching PK-INIT cert in hdb");
1755233294Sstas		return 0;
1756233294Sstas	    }
1757233294Sstas	}
1758233294Sstas    }
1759233294Sstas
1760233294Sstas
1761178825Sdfr    if (config->pkinit_princ_in_cert) {
1762178825Sdfr	ret = match_rfc_san(context, config,
1763233294Sstas			    context->hx509ctx,
1764233294Sstas			    cp->cert,
1765178825Sdfr			    client->entry.principal);
1766178825Sdfr	if (ret == 0) {
1767178825Sdfr	    kdc_log(context, config, 5,
1768178825Sdfr		    "Found matching PK-INIT SAN in certificate");
1769178825Sdfr	    return 0;
1770178825Sdfr	}
1771178825Sdfr	ret = match_ms_upn_san(context, config,
1772233294Sstas			       context->hx509ctx,
1773233294Sstas			       cp->cert,
1774233294Sstas			       clientdb,
1775233294Sstas			       client);
1776178825Sdfr	if (ret == 0) {
1777178825Sdfr	    kdc_log(context, config, 5,
1778178825Sdfr		    "Found matching MS UPN SAN in certificate");
1779178825Sdfr	    return 0;
1780178825Sdfr	}
1781178825Sdfr    }
1782178825Sdfr
1783178825Sdfr    ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1784178825Sdfr    if (ret == 0 && acl != NULL) {
1785178825Sdfr	/*
1786178825Sdfr	 * Cheat here and compare the generated name with the string
1787178825Sdfr	 * and not the reverse.
1788178825Sdfr	 */
1789178825Sdfr	for (i = 0; i < acl->len; i++) {
1790178825Sdfr	    if (strcmp(*subject_name, acl->val[0].subject) != 0)
1791178825Sdfr		continue;
1792178825Sdfr
1793178825Sdfr	    /* Don't support isser and anchor checking right now */
1794178825Sdfr	    if (acl->val[0].issuer)
1795178825Sdfr		continue;
1796178825Sdfr	    if (acl->val[0].anchor)
1797178825Sdfr		continue;
1798178825Sdfr
1799178825Sdfr	    kdc_log(context, config, 5,
1800178825Sdfr		    "Found matching PK-INIT database ACL");
1801178825Sdfr	    return 0;
1802178825Sdfr	}
1803178825Sdfr    }
1804178825Sdfr
1805178825Sdfr    for (i = 0; i < principal_mappings.len; i++) {
1806178825Sdfr	krb5_boolean b;
1807178825Sdfr
1808178825Sdfr	b = krb5_principal_compare(context,
1809178825Sdfr				   client->entry.principal,
1810178825Sdfr				   principal_mappings.val[i].principal);
1811178825Sdfr	if (b == FALSE)
1812178825Sdfr	    continue;
1813178825Sdfr	if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1814178825Sdfr	    continue;
1815178825Sdfr	kdc_log(context, config, 5,
1816178825Sdfr		"Found matching PK-INIT FILE ACL");
1817178825Sdfr	return 0;
1818178825Sdfr    }
1819178825Sdfr
1820233294Sstas    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1821233294Sstas    krb5_set_error_message(context, ret,
1822178825Sdfr			  "PKINIT no matching principals for %s",
1823178825Sdfr			  *subject_name);
1824178825Sdfr
1825178825Sdfr    kdc_log(context, config, 5,
1826178825Sdfr	    "PKINIT no matching principals for %s",
1827178825Sdfr	    *subject_name);
1828178825Sdfr
1829178825Sdfr    free(*subject_name);
1830178825Sdfr    *subject_name = NULL;
1831178825Sdfr
1832233294Sstas    return ret;
1833178825Sdfr}
1834178825Sdfr
1835178825Sdfrstatic krb5_error_code
1836233294Sstasadd_principal_mapping(krb5_context context,
1837178825Sdfr		      const char *principal_name,
1838178825Sdfr		      const char * subject)
1839178825Sdfr{
1840178825Sdfr   struct pk_allowed_princ *tmp;
1841178825Sdfr   krb5_principal principal;
1842178825Sdfr   krb5_error_code ret;
1843178825Sdfr
1844178825Sdfr   tmp = realloc(principal_mappings.val,
1845178825Sdfr	         (principal_mappings.len + 1) * sizeof(*tmp));
1846178825Sdfr   if (tmp == NULL)
1847178825Sdfr       return ENOMEM;
1848178825Sdfr   principal_mappings.val = tmp;
1849178825Sdfr
1850178825Sdfr   ret = krb5_parse_name(context, principal_name, &principal);
1851178825Sdfr   if (ret)
1852178825Sdfr       return ret;
1853178825Sdfr
1854178825Sdfr   principal_mappings.val[principal_mappings.len].principal = principal;
1855178825Sdfr
1856178825Sdfr   principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1857178825Sdfr   if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1858178825Sdfr       krb5_free_principal(context, principal);
1859178825Sdfr       return ENOMEM;
1860178825Sdfr   }
1861178825Sdfr   principal_mappings.len++;
1862178825Sdfr
1863178825Sdfr   return 0;
1864178825Sdfr}
1865178825Sdfr
1866178825Sdfrkrb5_error_code
1867178825Sdfr_kdc_add_inital_verified_cas(krb5_context context,
1868178825Sdfr			     krb5_kdc_configuration *config,
1869233294Sstas			     pk_client_params *cp,
1870178825Sdfr			     EncTicketPart *tkt)
1871178825Sdfr{
1872178825Sdfr    AD_INITIAL_VERIFIED_CAS cas;
1873178825Sdfr    krb5_error_code ret;
1874178825Sdfr    krb5_data data;
1875233294Sstas    size_t size = 0;
1876178825Sdfr
1877178825Sdfr    memset(&cas, 0, sizeof(cas));
1878233294Sstas
1879178825Sdfr    /* XXX add CAs to cas here */
1880178825Sdfr
1881178825Sdfr    ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1882178825Sdfr		       &cas, &size, ret);
1883178825Sdfr    if (ret)
1884178825Sdfr	return ret;
1885178825Sdfr    if (data.length != size)
1886178825Sdfr	krb5_abortx(context, "internal asn.1 encoder error");
1887178825Sdfr
1888233294Sstas    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1889178825Sdfr				      KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1890178825Sdfr				      &data);
1891178825Sdfr    krb5_data_free(&data);
1892178825Sdfr    return ret;
1893178825Sdfr}
1894178825Sdfr
1895178825Sdfr/*
1896178825Sdfr *
1897178825Sdfr */
1898178825Sdfr
1899178825Sdfrstatic void
1900178825Sdfrload_mappings(krb5_context context, const char *fn)
1901178825Sdfr{
1902178825Sdfr    krb5_error_code ret;
1903178825Sdfr    char buf[1024];
1904178825Sdfr    unsigned long lineno = 0;
1905178825Sdfr    FILE *f;
1906178825Sdfr
1907178825Sdfr    f = fopen(fn, "r");
1908178825Sdfr    if (f == NULL)
1909178825Sdfr	return;
1910178825Sdfr
1911178825Sdfr    while (fgets(buf, sizeof(buf), f) != NULL) {
1912178825Sdfr	char *subject_name, *p;
1913233294Sstas
1914178825Sdfr	buf[strcspn(buf, "\n")] = '\0';
1915178825Sdfr	lineno++;
1916178825Sdfr
1917178825Sdfr	p = buf + strspn(buf, " \t");
1918178825Sdfr
1919178825Sdfr	if (*p == '#' || *p == '\0')
1920178825Sdfr	    continue;
1921178825Sdfr
1922178825Sdfr	subject_name = strchr(p, ':');
1923178825Sdfr	if (subject_name == NULL) {
1924178825Sdfr	    krb5_warnx(context, "pkinit mapping file line %lu "
1925178825Sdfr		       "missing \":\" :%s",
1926178825Sdfr		       lineno, buf);
1927178825Sdfr	    continue;
1928178825Sdfr	}
1929178825Sdfr	*subject_name++ = '\0';
1930178825Sdfr
1931178825Sdfr	ret = add_principal_mapping(context, p, subject_name);
1932178825Sdfr	if (ret) {
1933178825Sdfr	    krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1934178825Sdfr		      lineno, buf);
1935178825Sdfr	    continue;
1936178825Sdfr	}
1937233294Sstas    }
1938178825Sdfr
1939178825Sdfr    fclose(f);
1940178825Sdfr}
1941233294Sstas
1942178825Sdfr/*
1943178825Sdfr *
1944178825Sdfr */
1945178825Sdfr
1946178825Sdfrkrb5_error_code
1947233294Sstaskrb5_kdc_pk_initialize(krb5_context context,
1948233294Sstas		       krb5_kdc_configuration *config,
1949233294Sstas		       const char *user_id,
1950233294Sstas		       const char *anchors,
1951233294Sstas		       char **pool,
1952233294Sstas		       char **revoke_list)
1953178825Sdfr{
1954178825Sdfr    const char *file;
1955178825Sdfr    char *fn = NULL;
1956178825Sdfr    krb5_error_code ret;
1957178825Sdfr
1958178825Sdfr    file = krb5_config_get_string(context, NULL,
1959178825Sdfr				  "libdefaults", "moduli", NULL);
1960178825Sdfr
1961178825Sdfr    ret = _krb5_parse_moduli(context, file, &moduli);
1962178825Sdfr    if (ret)
1963178825Sdfr	krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1964178825Sdfr
1965178825Sdfr    principal_mappings.len = 0;
1966178825Sdfr    principal_mappings.val = NULL;
1967178825Sdfr
1968178825Sdfr    ret = _krb5_pk_load_id(context,
1969178825Sdfr			   &kdc_identity,
1970178825Sdfr			   user_id,
1971178825Sdfr			   anchors,
1972178825Sdfr			   pool,
1973178825Sdfr			   revoke_list,
1974178825Sdfr			   NULL,
1975178825Sdfr			   NULL,
1976178825Sdfr			   NULL);
1977178825Sdfr    if (ret) {
1978178825Sdfr	krb5_warn(context, ret, "PKINIT: ");
1979178825Sdfr	config->enable_pkinit = 0;
1980178825Sdfr	return ret;
1981178825Sdfr    }
1982178825Sdfr
1983178825Sdfr    {
1984178825Sdfr	hx509_query *q;
1985178825Sdfr	hx509_cert cert;
1986233294Sstas
1987233294Sstas	ret = hx509_query_alloc(context->hx509ctx, &q);
1988178825Sdfr	if (ret) {
1989178825Sdfr	    krb5_warnx(context, "PKINIT: out of memory");
1990178825Sdfr	    return ENOMEM;
1991178825Sdfr	}
1992233294Sstas
1993178825Sdfr	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1994233294Sstas	if (config->pkinit_kdc_friendly_name)
1995233294Sstas	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1996233294Sstas
1997233294Sstas	ret = hx509_certs_find(context->hx509ctx,
1998178825Sdfr			       kdc_identity->certs,
1999178825Sdfr			       q,
2000178825Sdfr			       &cert);
2001233294Sstas	hx509_query_free(context->hx509ctx, q);
2002178825Sdfr	if (ret == 0) {
2003233294Sstas	    if (hx509_cert_check_eku(context->hx509ctx, cert,
2004233294Sstas				     &asn1_oid_id_pkkdcekuoid, 0)) {
2005233294Sstas		hx509_name name;
2006233294Sstas		char *str;
2007233294Sstas		ret = hx509_cert_get_subject(cert, &name);
2008233294Sstas		if (ret == 0) {
2009233294Sstas		    hx509_name_to_string(name, &str);
2010233294Sstas		    krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2011233294Sstas			       "is missing the PK-INIT KDC EKU, this is bad for "
2012233294Sstas			       "interoperability.", str);
2013233294Sstas		    hx509_name_free(&name);
2014233294Sstas		    free(str);
2015233294Sstas		}
2016233294Sstas	    }
2017178825Sdfr	    hx509_cert_free(cert);
2018178825Sdfr	} else
2019178825Sdfr	    krb5_warnx(context, "PKINIT: failed to find a signing "
2020178825Sdfr		       "certifiate with a public key");
2021178825Sdfr    }
2022178825Sdfr
2023233294Sstas    if (krb5_config_get_bool_default(context,
2024233294Sstas				     NULL,
2025233294Sstas				     FALSE,
2026233294Sstas				     "kdc",
2027233294Sstas				     "pkinit_allow_proxy_certificate",
2028233294Sstas				     NULL))
2029233294Sstas	config->pkinit_allow_proxy_certs = 1;
2030178825Sdfr
2031233294Sstas    file = krb5_config_get_string(context,
2032178825Sdfr				  NULL,
2033178825Sdfr				  "kdc",
2034178825Sdfr				  "pkinit_mappings_file",
2035178825Sdfr				  NULL);
2036178825Sdfr    if (file == NULL) {
2037178825Sdfr	asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2038178825Sdfr	file = fn;
2039178825Sdfr    }
2040178825Sdfr
2041178825Sdfr    load_mappings(context, file);
2042178825Sdfr    if (fn)
2043178825Sdfr	free(fn);
2044178825Sdfr
2045178825Sdfr    return 0;
2046178825Sdfr}
2047178825Sdfr
2048178825Sdfr#endif /* PKINIT */
2049