1/*
2 * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "kdc_locl.h"
37
38#ifdef PKINIT
39
40#include <heim_asn1.h>
41#include <rfc2459_asn1.h>
42#include <cms_asn1.h>
43#include <pkinit_asn1.h>
44
45#include <hx509.h>
46#include "crypto-headers.h"
47
48struct pk_client_params {
49    enum krb5_pk_type type;
50    enum { USE_RSA, USE_DH, USE_ECDH } keyex;
51    union {
52	struct {
53	    BIGNUM *public_key;
54	    DH *key;
55	} dh;
56#ifdef HAVE_OPENSSL
57	struct {
58	    EC_KEY *public_key;
59	    EC_KEY *key;
60	} ecdh;
61#endif
62    } u;
63    hx509_cert cert;
64    hx509_cert tacert;
65    unsigned nonce;
66    EncryptionKey reply_key;
67    char *dh_group_name;
68    hx509_peer_info peer;
69    hx509_certs client_anchors;
70    hx509_verify_ctx verify_ctx;
71};
72
73struct pk_principal_mapping {
74    unsigned int len;
75    struct pk_allowed_princ {
76	krb5_principal principal;
77	char *subject;
78    } *val;
79};
80
81static struct krb5_pk_identity *kdc_identity;
82static struct pk_principal_mapping principal_mappings;
83static struct krb5_dh_moduli **moduli;
84
85static struct {
86    krb5_data data;
87    time_t expire;
88    time_t next_update;
89} ocsp;
90
91/*
92 *
93 */
94
95static krb5_error_code
96pk_check_pkauthenticator_win2k(krb5_context context,
97			       PKAuthenticator_Win2k *a,
98			       const KDC_REQ *req)
99{
100    krb5_timestamp now;
101
102    krb5_timeofday (context, &now);
103
104    /* XXX cusec */
105    if (a->ctime == 0 || krb5_time_abs(a->ctime, now) > context->max_skew) {
106	krb5_clear_error_message(context);
107	return KRB5KRB_AP_ERR_SKEW;
108    }
109    return 0;
110}
111
112static krb5_error_code
113pk_check_pkauthenticator(krb5_context context,
114			 PKAuthenticator *a,
115			 const KDC_REQ *req)
116{
117    u_char *buf = NULL;
118    size_t buf_size;
119    krb5_error_code ret;
120    size_t len = 0;
121    krb5_timestamp now;
122    Checksum checksum;
123
124    krb5_timeofday (context, &now);
125
126    /* XXX cusec */
127    if (a->ctime == 0 || krb5_time_abs(a->ctime, now) > context->max_skew) {
128	krb5_clear_error_message(context);
129	return KRB5KRB_AP_ERR_SKEW;
130    }
131
132    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
133    if (ret) {
134	krb5_clear_error_message(context);
135	return ret;
136    }
137    if (buf_size != len)
138	krb5_abortx(context, "Internal error in ASN.1 encoder");
139
140    ret = krb5_create_checksum(context,
141			       NULL,
142			       0,
143			       CKSUMTYPE_SHA1,
144			       buf,
145			       len,
146			       &checksum);
147    free(buf);
148    if (ret) {
149	krb5_clear_error_message(context);
150	return ret;
151    }
152
153    if (a->paChecksum == NULL) {
154	krb5_clear_error_message(context);
155	ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
156	goto out;
157    }
158
159    if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
160	krb5_clear_error_message(context);
161	ret = KRB5KRB_ERR_GENERIC;
162    }
163
164out:
165    free_Checksum(&checksum);
166
167    return ret;
168}
169
170void
171_kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
172{
173    if (cp == NULL)
174        return;
175    if (cp->cert)
176	hx509_cert_free(cp->cert);
177    if (cp->tacert)
178	hx509_cert_free(cp->tacert);
179    if (cp->verify_ctx)
180	hx509_verify_destroy_ctx(cp->verify_ctx);
181    if (cp->keyex == USE_DH) {
182	if (cp->u.dh.key)
183	    DH_free(cp->u.dh.key);
184	if (cp->u.dh.public_key)
185	    BN_free(cp->u.dh.public_key);
186    }
187#ifdef HAVE_OPENSSL
188    if (cp->keyex == USE_ECDH) {
189	if (cp->u.ecdh.key)
190	    EC_KEY_free(cp->u.ecdh.key);
191	if (cp->u.ecdh.public_key)
192	    EC_KEY_free(cp->u.ecdh.public_key);
193    }
194#endif
195    krb5_free_keyblock_contents(context, &cp->reply_key);
196    if (cp->dh_group_name)
197	free(cp->dh_group_name);
198    if (cp->peer)
199	hx509_peer_info_free(cp->peer);
200    if (cp->client_anchors)
201	hx509_certs_free(&cp->client_anchors);
202    memset(cp, 0, sizeof(*cp));
203    free(cp);
204}
205
206static krb5_error_code
207generate_dh_keyblock(krb5_context context,
208		     pk_client_params *client_params,
209                     krb5_enctype enctype)
210{
211    unsigned char *dh_gen_key = NULL;
212    krb5_keyblock key;
213    krb5_error_code ret;
214    int dh_gen_keylen;
215    size_t size;
216
217    memset(&key, 0, sizeof(key));
218
219    if (client_params->keyex == USE_DH) {
220
221	if (client_params->u.dh.public_key == NULL) {
222	    ret = KRB5KRB_ERR_GENERIC;
223	    krb5_set_error_message(context, ret, "public_key");
224	    goto out;
225	}
226
227	if (!DH_generate_key(client_params->u.dh.key)) {
228	    ret = KRB5KRB_ERR_GENERIC;
229	    krb5_set_error_message(context, ret,
230				   "Can't generate Diffie-Hellman keys");
231	    goto out;
232	}
233
234	size = DH_size(client_params->u.dh.key);
235
236	dh_gen_key = malloc(size);
237	if (dh_gen_key == NULL) {
238	    ret = ENOMEM;
239	    krb5_set_error_message(context, ret, "malloc: out of memory");
240	    goto out;
241	}
242
243	dh_gen_keylen = DH_compute_key(dh_gen_key, client_params->u.dh.public_key, client_params->u.dh.key);
244	if (dh_gen_keylen <= 0 || (size_t)dh_gen_keylen < size / 2) {
245	    ret = KRB5KRB_ERR_GENERIC;
246	    krb5_set_error_message(context, ret,
247				   "Can't compute Diffie-Hellman key");
248	    goto out;
249	}
250
251	if ((size_t)dh_gen_keylen < size) {
252	    size_t diff = size - dh_gen_keylen;
253	    memmove(dh_gen_key + diff, dh_gen_key, dh_gen_keylen);
254	    memset(dh_gen_key, 0, diff);
255	    dh_gen_keylen = (int)size;
256	}
257
258#ifdef HAVE_OPENSSL
259    } else if (client_params->keyex == USE_ECDH) {
260
261	if (client_params->u.ecdh.public_key == NULL) {
262	    ret = KRB5KRB_ERR_GENERIC;
263	    krb5_set_error_message(context, ret, "public_key");
264	    goto out;
265	}
266
267	client_params->u.ecdh.key = EC_KEY_new();
268	if (client_params->u.ecdh.key == NULL) {
269	    ret = ENOMEM;
270	    goto out;
271	}
272	EC_KEY_set_group(client_params->u.ecdh.key,
273			 EC_KEY_get0_group(client_params->u.ecdh.public_key));
274
275	if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) {
276	    ret = ENOMEM;
277	    goto out;
278	}
279
280	size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8;
281	dh_gen_key = malloc(size);
282	if (dh_gen_key == NULL) {
283	    ret = ENOMEM;
284	    krb5_set_error_message(context, ret,
285				   N_("malloc: out of memory", ""));
286	    goto out;
287	}
288
289	dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
290					 EC_KEY_get0_public_key(client_params->u.ecdh.public_key),
291					 client_params->u.ecdh.key, NULL);
292
293#endif /* HAVE_OPENSSL */
294    } else {
295	ret = KRB5KRB_ERR_GENERIC;
296	krb5_set_error_message(context, ret,
297			       "Diffie-Hellman not selected keys");
298	goto out;
299    }
300
301    ret = _krb5_pk_octetstring2key(context,
302				   enctype,
303				   dh_gen_key, dh_gen_keylen,
304				   NULL, NULL,
305				   &client_params->reply_key);
306
307 out:
308    if (dh_gen_key)
309	free(dh_gen_key);
310    if (key.keyvalue.data)
311	krb5_free_keyblock_contents(context, &key);
312
313    return ret;
314}
315
316static BIGNUM *
317integer_to_BN(krb5_context context, const char *field, heim_integer *f)
318{
319    BIGNUM *bn;
320
321    bn = BN_bin2bn((const unsigned char *)f->data, (int)f->length, NULL);
322    if (bn == NULL) {
323	krb5_set_error_message(context, KRB5_BADMSGTYPE,
324			       "PKINIT: parsing BN failed %s", field);
325	return NULL;
326    }
327    BN_set_negative(bn, f->negative);
328    return bn;
329}
330
331static krb5_error_code
332get_dh_param(krb5_context context,
333	     krb5_kdc_configuration *config,
334	     SubjectPublicKeyInfo *dh_key_info,
335	     pk_client_params *client_params)
336{
337    DomainParameters dhparam;
338    DH *dh = NULL;
339    krb5_error_code ret;
340
341    memset(&dhparam, 0, sizeof(dhparam));
342
343    if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
344	ret = KRB5_BADMSGTYPE;
345	krb5_set_error_message(context, ret,
346			       "PKINIT: subjectPublicKey not aligned "
347			       "to 8 bit boundary");
348	goto out;
349    }
350
351    if (dh_key_info->algorithm.parameters == NULL) {
352	krb5_set_error_message(context, KRB5_BADMSGTYPE,
353			       "PKINIT missing algorithm parameter "
354			      "in clientPublicValue");
355	return KRB5_BADMSGTYPE;
356    }
357
358    ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
359				  dh_key_info->algorithm.parameters->length,
360				  &dhparam,
361				  NULL);
362    if (ret) {
363	krb5_set_error_message(context, ret, "Can't decode algorithm "
364			       "parameters in clientPublicValue");
365	goto out;
366    }
367
368    ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
369			    &dhparam.p, &dhparam.g, &dhparam.q, moduli,
370			    &client_params->dh_group_name);
371    if (ret) {
372	/* XXX send back proposal of better group */
373	goto out;
374    }
375
376    dh = DH_new();
377    if (dh == NULL) {
378	ret = ENOMEM;
379	krb5_set_error_message(context, ret, "Cannot create DH structure");
380	goto out;
381    }
382    ret = KRB5_BADMSGTYPE;
383    dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
384    if (dh->p == NULL)
385	goto out;
386    dh->g = integer_to_BN(context, "DH base", &dhparam.g);
387    if (dh->g == NULL)
388	goto out;
389    dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
390    if (dh->g == NULL)
391	goto out;
392
393    {
394	heim_integer glue;
395	size_t size;
396
397	ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
398				 dh_key_info->subjectPublicKey.length / 8,
399				 &glue,
400				 &size);
401	if (ret) {
402	    krb5_clear_error_message(context);
403	    return ret;
404	}
405
406	client_params->u.dh.public_key = integer_to_BN(context,
407						       "subjectPublicKey",
408						       &glue);
409	der_free_heim_integer(&glue);
410	if (client_params->u.dh.public_key == NULL) {
411	    ret = KRB5_BADMSGTYPE;
412	    goto out;
413	}
414    }
415
416    client_params->u.dh.key = dh;
417    dh = NULL;
418    ret = 0;
419
420 out:
421    if (dh)
422	DH_free(dh);
423    free_DomainParameters(&dhparam);
424    return ret;
425}
426
427#ifdef HAVE_OPENSSL
428
429static krb5_error_code
430get_ecdh_param(krb5_context context,
431	       krb5_kdc_configuration *config,
432	       SubjectPublicKeyInfo *dh_key_info,
433	       pk_client_params *client_params)
434{
435    ECParameters ecp;
436    EC_KEY *public = NULL;
437    krb5_error_code ret;
438    const unsigned char *p;
439    size_t len;
440    int nid;
441
442    if (dh_key_info->algorithm.parameters == NULL) {
443	krb5_set_error_message(context, KRB5_BADMSGTYPE,
444			       "PKINIT missing algorithm parameter "
445			       "in clientPublicValue");
446	return KRB5_BADMSGTYPE;
447    }
448
449    memset(&ecp, 0, sizeof(ecp));
450
451    ret = decode_ECParameters(dh_key_info->algorithm.parameters->data,
452			      dh_key_info->algorithm.parameters->length, &ecp, &len);
453    if (ret)
454	goto out;
455
456    if (ecp.element != choice_ECParameters_namedCurve) {
457	ret = KRB5_BADMSGTYPE;
458	goto out;
459    }
460
461    if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0)
462	nid = NID_X9_62_prime256v1;
463    else {
464	ret = KRB5_BADMSGTYPE;
465	goto out;
466    }
467
468    /* XXX verify group is ok */
469
470    public = EC_KEY_new_by_curve_name(nid);
471
472    p = dh_key_info->subjectPublicKey.data;
473    len = dh_key_info->subjectPublicKey.length / 8;
474    if (o2i_ECPublicKey(&public, &p, len) == NULL) {
475	ret = KRB5_BADMSGTYPE;
476	krb5_set_error_message(context, ret,
477			       "PKINIT failed to decode ECDH key");
478	goto out;
479    }
480    client_params->u.ecdh.public_key = public;
481    public = NULL;
482
483 out:
484    if (public)
485	EC_KEY_free(public);
486    free_ECParameters(&ecp);
487    return ret;
488}
489
490#endif /* HAVE_OPENSSL */
491
492krb5_error_code
493_kdc_pk_rd_padata(krb5_context context,
494		  krb5_kdc_configuration *config,
495		  const KDC_REQ *req,
496		  const PA_DATA *pa,
497		  hdb_entry_ex *client,
498		  pk_client_params **ret_params)
499{
500    pk_client_params *cp;
501    krb5_error_code ret;
502    heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
503    krb5_data eContent = { 0, NULL };
504    krb5_data signed_content = { 0, NULL };
505    const char *type = "unknown type";
506    hx509_certs trust_anchors;
507    int have_data = 0;
508    const HDB_Ext_PKINIT_cert *pc;
509    heim_array_t signer_certs = NULL;
510
511    *ret_params = NULL;
512
513    if (!config->enable_pkinit) {
514	kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
515	krb5_clear_error_message(context);
516	return 0;
517    }
518
519    cp = calloc(1, sizeof(*cp));
520    if (cp == NULL) {
521	krb5_clear_error_message(context);
522	ret = ENOMEM;
523	goto out;
524    }
525
526    ret = hx509_certs_init(context->hx509ctx,
527			   "MEMORY:trust-anchors",
528			   0, NULL, &trust_anchors);
529    if (ret) {
530	krb5_set_error_message(context, ret, "failed to create trust anchors");
531	goto out;
532    }
533
534    ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
535			    kdc_identity->anchors);
536    if (ret) {
537	hx509_certs_free(&trust_anchors);
538	krb5_set_error_message(context, ret, "failed to merge trust anchors");
539	goto out;
540    }
541
542    /* Add any registered certificates for this client as trust anchors */
543    ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
544    if (ret == 0 && pc != NULL) {
545	hx509_cert cert;
546	unsigned int i;
547
548	for (i = 0; i < pc->len; i++) {
549	    ret = hx509_cert_init_data(context->hx509ctx,
550				       pc->val[i].cert.data,
551				       pc->val[i].cert.length,
552				       &cert);
553	    if (ret)
554		continue;
555	    hx509_certs_add(context->hx509ctx, trust_anchors, cert);
556	    hx509_cert_free(cert);
557	}
558    }
559
560    ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
561    if (ret) {
562	hx509_certs_free(&trust_anchors);
563	krb5_set_error_message(context, ret, "failed to create verify context");
564	goto out;
565    }
566
567    hx509_verify_set_time(cp->verify_ctx, kdc_time);
568    hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
569    hx509_certs_free(&trust_anchors);
570
571    if (config->pkinit_allow_proxy_certs)
572	hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
573
574    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
575	PA_PK_AS_REQ_Win2k r;
576
577	type = "PK-INIT-Win2k";
578
579	if (req->req_body.kdc_options.request_anonymous) {
580	    ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
581	    krb5_set_error_message(context, ret,
582				   "Anon not supported in RSA mode");
583	    goto out;
584	}
585
586	ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
587					pa->padata_value.length,
588					&r,
589					NULL);
590	if (ret) {
591	    krb5_set_error_message(context, ret, "Can't decode "
592				   "PK-AS-REQ-Win2k: %d", ret);
593	    goto out;
594	}
595
596	ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
597					   &contentInfoOid,
598					   &signed_content,
599					   &have_data);
600	free_PA_PK_AS_REQ_Win2k(&r);
601	if (ret) {
602	    krb5_set_error_message(context, ret,
603				   "Can't unwrap ContentInfo(win): %d", ret);
604	    goto out;
605	}
606
607    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
608	PA_PK_AS_REQ r;
609
610	type = "PK-INIT-IETF";
611
612	ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
613				  pa->padata_value.length,
614				  &r,
615				  NULL);
616	if (ret) {
617	    krb5_set_error_message(context, ret,
618				   "Can't decode PK-AS-REQ: %d", ret);
619	    goto out;
620	}
621
622	/* XXX look at r.kdcPkId */
623	if (r.trustedCertifiers) {
624	    ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
625	    unsigned int i, maxedi;
626
627	    ret = hx509_certs_init(context->hx509ctx,
628				   "MEMORY:client-anchors",
629				   0, NULL,
630				   &cp->client_anchors);
631	    if (ret) {
632		krb5_set_error_message(context, ret,
633				       "Can't allocate client anchors: %d",
634				       ret);
635		goto out;
636
637	    }
638	    /*
639	     * If the client sent more then 10 EDI, don't bother
640	     * looking more then 10 of performance reasons.
641	     */
642	    maxedi = edi->len;
643	    if (maxedi > 10)
644		maxedi = 10;
645	    for (i = 0; i < maxedi; i++) {
646		IssuerAndSerialNumber iasn;
647		hx509_query *q;
648		hx509_cert cert;
649		size_t size;
650
651		if (edi->val[i].issuerAndSerialNumber == NULL)
652		    continue;
653
654		ret = hx509_query_alloc(context->hx509ctx, &q);
655		if (ret) {
656		    krb5_set_error_message(context, ret,
657					  "Failed to allocate hx509_query");
658		    goto out;
659		}
660
661		ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
662						   edi->val[i].issuerAndSerialNumber->length,
663						   &iasn,
664						   &size);
665		if (ret) {
666		    hx509_query_free(context->hx509ctx, q);
667		    continue;
668		}
669		ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
670		free_IssuerAndSerialNumber(&iasn);
671		if (ret) {
672		    hx509_query_free(context->hx509ctx, q);
673		    continue;
674		}
675
676		ret = hx509_certs_find(context->hx509ctx,
677				       kdc_identity->certs,
678				       q,
679				       &cert);
680		hx509_query_free(context->hx509ctx, q);
681		if (ret)
682		    continue;
683		hx509_certs_add(context->hx509ctx,
684				cp->client_anchors, cert);
685		hx509_cert_free(cert);
686	    }
687	}
688
689	ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
690					   &contentInfoOid,
691					   &signed_content,
692					   &have_data);
693	free_PA_PK_AS_REQ(&r);
694	if (ret) {
695	    krb5_set_error_message(context, ret,
696				   "Can't unwrap ContentInfo: %d", ret);
697	    goto out;
698	}
699
700    } else {
701	krb5_clear_error_message(context);
702	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
703	goto out;
704    }
705
706    ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
707    if (ret != 0) {
708	ret = KRB5KRB_ERR_GENERIC;
709	krb5_set_error_message(context, ret,
710			       "PK-AS-REQ-Win2k invalid content type oid");
711	goto out;
712    }
713
714    if (!have_data) {
715	ret = KRB5KRB_ERR_GENERIC;
716	krb5_set_error_message(context, ret,
717			      "PK-AS-REQ-Win2k no signed auth pack");
718	goto out;
719    }
720
721    {
722	int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
723	hx509_evaluate eval;
724	size_t len;
725
726	if (req->req_body.kdc_options.request_anonymous)
727	    flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
728
729	ret = hx509_cms_verify_signed(context->hx509ctx,
730				      cp->verify_ctx,
731				      flags,
732				      signed_content.data,
733				      signed_content.length,
734				      NULL,
735				      kdc_identity->certpool,
736				      &eContentType,
737				      &eContent,
738				      &signer_certs);
739	if (ret) {
740	    char *s = hx509_get_error_string(context->hx509ctx, ret);
741	    krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
742		       s, ret);
743	    free(s);
744	    goto out;
745	}
746
747	/* if not anonymous, pick up certs */
748	if (signer_certs) {
749	    ret = KRB5_KDC_ERR_CANT_VERIFY_CERTIFICATE;
750
751	    len = heim_array_get_length(signer_certs);
752	    if (len == 0)
753		goto out;
754
755	    eval = heim_array_copy_value(signer_certs, 0);
756	    if (eval == NULL)
757		goto out;
758
759	    len = hx509_evaluate_get_length(eval);
760	    if (len == 0) {
761		hx509_evaluate_free(eval);
762		goto out;
763	    }
764
765	    cp->cert = hx509_evaluate_get_cert(eval, 0);
766	    if (cp->cert == NULL) {
767		hx509_evaluate_free(eval);
768		goto out;
769	    }
770
771	    cp->tacert = hx509_evaluate_get_ta(eval);
772	    hx509_evaluate_free(eval);
773	    if (cp->tacert == NULL)
774		goto out;
775	}
776    }
777
778    /* Signature is correct, now verify the signed message */
779    if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
780	der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
781    {
782	ret = KRB5_BADMSGTYPE;
783	krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
784	goto out;
785    }
786
787    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
788	AuthPack_Win2k ap;
789
790	ret = decode_AuthPack_Win2k(eContent.data,
791				    eContent.length,
792				    &ap,
793				    NULL);
794	if (ret) {
795	    krb5_set_error_message(context, ret,
796				   "Can't decode AuthPack: %d", ret);
797	    goto out;
798	}
799
800	ret = pk_check_pkauthenticator_win2k(context,
801					     &ap.pkAuthenticator,
802					     req);
803	if (ret) {
804	    free_AuthPack_Win2k(&ap);
805	    goto out;
806	}
807
808	cp->type = PKINIT_WIN2K;
809	cp->nonce = ap.pkAuthenticator.nonce;
810
811	if (ap.clientPublicValue) {
812	    ret = KRB5KRB_ERR_GENERIC;
813	    krb5_set_error_message(context, ret,
814				   "DH not supported for windows");
815	    goto out;
816	}
817	free_AuthPack_Win2k(&ap);
818
819    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
820	AuthPack ap;
821
822	ret = decode_AuthPack(eContent.data,
823			      eContent.length,
824			      &ap,
825			      NULL);
826	if (ret) {
827	    krb5_set_error_message(context, ret,
828				   "Can't decode AuthPack: %d", ret);
829	    free_AuthPack(&ap);
830	    goto out;
831	}
832
833	if (req->req_body.kdc_options.request_anonymous &&
834	    ap.clientPublicValue == NULL) {
835	    free_AuthPack(&ap);
836	    ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
837	    krb5_set_error_message(context, ret,
838				   "Anon not supported in RSA mode");
839	    goto out;
840	}
841
842	ret = pk_check_pkauthenticator(context,
843				       &ap.pkAuthenticator,
844				       req);
845	if (ret) {
846	    free_AuthPack(&ap);
847	    goto out;
848	}
849
850	cp->type = PKINIT_27;
851	cp->nonce = ap.pkAuthenticator.nonce;
852
853	if (ap.clientPublicValue) {
854	    if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
855		cp->keyex = USE_DH;
856		ret = get_dh_param(context, config,
857				   ap.clientPublicValue, cp);
858#ifdef HAVE_OPENSSL
859	    } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
860		cp->keyex = USE_ECDH;
861		ret = get_ecdh_param(context, config,
862				     ap.clientPublicValue, cp);
863#endif /* HAVE_OPENSSL */
864	    } else {
865		ret = KRB5_BADMSGTYPE;
866		krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
867	    }
868	    if (ret) {
869		free_AuthPack(&ap);
870		goto out;
871	    }
872	} else
873	    cp->keyex = USE_RSA;
874
875	ret = hx509_peer_info_alloc(context->hx509ctx,
876					&cp->peer);
877	if (ret) {
878	    free_AuthPack(&ap);
879	    goto out;
880	}
881
882	if (ap.supportedCMSTypes) {
883	    ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
884					       cp->peer,
885					       ap.supportedCMSTypes->val,
886					       ap.supportedCMSTypes->len);
887	    if (ret) {
888		free_AuthPack(&ap);
889		goto out;
890	    }
891	} else {
892	    /* assume old client */
893	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
894					hx509_crypto_des_rsdi_ede3_cbc());
895	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
896					hx509_signature_rsa_with_sha1());
897	    hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
898					hx509_signature_sha1());
899	}
900	free_AuthPack(&ap);
901    } else
902	krb5_abortx(context, "internal pkinit error");
903
904    kdc_log(context, config, 0, "PK-INIT request of type %s", type);
905
906out:
907    if (ret)
908	krb5_warn(context, ret, "PKINIT");
909
910    heim_release(signer_certs);
911
912    if (signed_content.data)
913	free(signed_content.data);
914    krb5_data_free(&eContent);
915    der_free_oid(&eContentType);
916    der_free_oid(&contentInfoOid);
917    if (ret) {
918        _kdc_pk_free_client_param(context, cp);
919    } else
920	*ret_params = cp;
921    return ret;
922}
923
924/*
925 *
926 */
927
928static krb5_error_code
929BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
930{
931    integer->length = BN_num_bytes(bn);
932    integer->data = malloc(integer->length);
933    if (integer->data == NULL) {
934	krb5_clear_error_message(context);
935	return ENOMEM;
936    }
937    BN_bn2bin(bn, integer->data);
938    integer->negative = BN_is_negative(bn);
939    return 0;
940}
941
942static krb5_error_code
943pk_mk_pa_reply_enckey(krb5_context context,
944		      krb5_kdc_configuration *config,
945		      pk_client_params *cp,
946		      const KDC_REQ *req,
947		      const krb5_data *req_buffer,
948		      krb5_keyblock *reply_key,
949		      ContentInfo *content_info,
950		      hx509_cert *kdc_cert)
951{
952    const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
953    krb5_error_code ret;
954    krb5_data buf, signed_data;
955    size_t size = 0;
956    int do_win2k = 0;
957
958    krb5_data_zero(&buf);
959    krb5_data_zero(&signed_data);
960
961    *kdc_cert = NULL;
962
963    /*
964     * If the message client is a win2k-type but it send pa data
965     * 09-binding it expects a IETF (checksum) reply so there can be
966     * no replay attacks.
967     */
968
969    switch (cp->type) {
970    case PKINIT_WIN2K: {
971	int i = 0;
972	if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
973	    && config->pkinit_require_binding == 0)
974	{
975	    do_win2k = 1;
976	}
977	sdAlg = &asn1_oid_id_pkcs7_data;
978	evAlg = &asn1_oid_id_pkcs7_data;
979	envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
980	break;
981    }
982    case PKINIT_27:
983	sdAlg = &asn1_oid_id_pkrkeydata;
984	evAlg = &asn1_oid_id_pkcs7_signedData;
985	break;
986    }
987
988    if (do_win2k) {
989	ReplyKeyPack_Win2k kp;
990	memset(&kp, 0, sizeof(kp));
991
992	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
993	if (ret) {
994	    krb5_clear_error_message(context);
995	    goto out;
996	}
997	kp.nonce = cp->nonce;
998
999	ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
1000			   buf.data, buf.length,
1001			   &kp, &size,ret);
1002	free_ReplyKeyPack_Win2k(&kp);
1003    } else {
1004	krb5_crypto ascrypto;
1005	ReplyKeyPack kp;
1006	memset(&kp, 0, sizeof(kp));
1007
1008	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
1009	if (ret) {
1010	    krb5_clear_error_message(context);
1011	    goto out;
1012	}
1013
1014	ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
1015	if (ret) {
1016	    krb5_clear_error_message(context);
1017	    goto out;
1018	}
1019
1020	ret = krb5_create_checksum(context, ascrypto, 6, 0,
1021				   req_buffer->data, req_buffer->length,
1022				   &kp.asChecksum);
1023	if (ret) {
1024	    krb5_clear_error_message(context);
1025	    goto out;
1026	}
1027
1028	ret = krb5_crypto_destroy(context, ascrypto);
1029	if (ret) {
1030	    krb5_clear_error_message(context);
1031	    goto out;
1032	}
1033	ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1034	free_ReplyKeyPack(&kp);
1035    }
1036    if (ret) {
1037	krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1038			       "failed (%d)", ret);
1039	goto out;
1040    }
1041    if (buf.length != size)
1042	krb5_abortx(context, "Internal ASN.1 encoder error");
1043
1044    {
1045	hx509_query *q;
1046	hx509_cert cert;
1047
1048	ret = hx509_query_alloc(context->hx509ctx, &q);
1049	if (ret)
1050	    goto out;
1051
1052	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1053	if (config->pkinit_kdc_friendly_name)
1054	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1055
1056	ret = hx509_certs_find(context->hx509ctx,
1057			       kdc_identity->certs,
1058			       q,
1059			       &cert);
1060	hx509_query_free(context->hx509ctx, q);
1061	if (ret)
1062	    goto out;
1063
1064	ret = hx509_cms_create_signed_1(context->hx509ctx,
1065					0,
1066					sdAlg,
1067					buf.data,
1068					buf.length,
1069					NULL,
1070					cert,
1071					cp->peer,
1072					cp->client_anchors,
1073					kdc_identity->certpool,
1074					&signed_data);
1075	*kdc_cert = cert;
1076    }
1077
1078    krb5_data_free(&buf);
1079    if (ret)
1080	goto out;
1081
1082    if (cp->type == PKINIT_WIN2K) {
1083	ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1084					 &signed_data,
1085					 &buf);
1086	if (ret)
1087	    goto out;
1088	krb5_data_free(&signed_data);
1089	signed_data = buf;
1090    }
1091
1092    ret = hx509_cms_envelope_1(context->hx509ctx,
1093			       HX509_CMS_EV_NO_KU_CHECK,
1094			       cp->cert,
1095			       signed_data.data, signed_data.length,
1096			       envelopedAlg,
1097			       evAlg, &buf);
1098    if (ret)
1099	goto out;
1100
1101    ret = _krb5_pk_mk_ContentInfo(context,
1102				  &buf,
1103				  &asn1_oid_id_pkcs7_envelopedData,
1104				  content_info);
1105out:
1106    if (ret && *kdc_cert) {
1107        hx509_cert_free(*kdc_cert);
1108	*kdc_cert = NULL;
1109    }
1110
1111    krb5_data_free(&buf);
1112    krb5_data_free(&signed_data);
1113    return ret;
1114}
1115
1116/*
1117 *
1118 */
1119
1120static krb5_error_code
1121pk_mk_pa_reply_dh(krb5_context context,
1122		  krb5_kdc_configuration *config,
1123      		  pk_client_params *cp,
1124		  ContentInfo *content_info,
1125		  hx509_cert *kdc_cert)
1126{
1127    KDCDHKeyInfo dh_info;
1128    krb5_data signed_data, buf;
1129    ContentInfo contentinfo;
1130    krb5_error_code ret;
1131    hx509_cert cert;
1132    hx509_query *q;
1133    size_t size = 0;
1134
1135    memset(&contentinfo, 0, sizeof(contentinfo));
1136    memset(&dh_info, 0, sizeof(dh_info));
1137    krb5_data_zero(&signed_data);
1138    krb5_data_zero(&buf);
1139
1140    *kdc_cert = NULL;
1141
1142    if (cp->keyex == USE_DH) {
1143	DH *kdc_dh = cp->u.dh.key;
1144	heim_integer i;
1145
1146	ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1147	if (ret)
1148	    return ret;
1149
1150	ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1151	der_free_heim_integer(&i);
1152	if (ret) {
1153	    krb5_set_error_message(context, ret, "ASN.1 encoding of "
1154				   "DHPublicKey failed (%d)", ret);
1155	    return ret;
1156	}
1157	if (buf.length != size)
1158	    krb5_abortx(context, "Internal ASN.1 encoder error");
1159
1160	dh_info.subjectPublicKey.length = buf.length * 8;
1161	dh_info.subjectPublicKey.data = buf.data;
1162	krb5_data_zero(&buf);
1163#ifdef HAVE_OPENSSL
1164    } else if (cp->keyex == USE_ECDH) {
1165	unsigned char *p;
1166	int len;
1167
1168	len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1169	if (len <= 0)
1170	    abort();
1171
1172	p = malloc(len);
1173	if (p == NULL)
1174	    abort();
1175
1176	dh_info.subjectPublicKey.length = len * 8;
1177	dh_info.subjectPublicKey.data = p;
1178
1179	len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1180	if (len <= 0)
1181	    abort();
1182#endif
1183    } else
1184	krb5_abortx(context, "no keyex selected ?");
1185
1186
1187    dh_info.nonce = cp->nonce;
1188
1189    ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1190		       ret);
1191    if (ret) {
1192	krb5_set_error_message(context, ret, "ASN.1 encoding of "
1193			       "KdcDHKeyInfo failed (%d)", ret);
1194	goto out;
1195    }
1196    if (buf.length != size)
1197	krb5_abortx(context, "Internal ASN.1 encoder error");
1198
1199    /*
1200     * Create the SignedData structure and sign the KdcDHKeyInfo
1201     * filled in above
1202     */
1203
1204    ret = hx509_query_alloc(context->hx509ctx, &q);
1205    if (ret)
1206	goto out;
1207
1208    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1209    if (config->pkinit_kdc_friendly_name)
1210	hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1211
1212    ret = hx509_certs_find(context->hx509ctx,
1213			   kdc_identity->certs,
1214			   q,
1215			   &cert);
1216    hx509_query_free(context->hx509ctx, q);
1217    if (ret)
1218	goto out;
1219
1220    ret = hx509_cms_create_signed_1(context->hx509ctx,
1221				    0,
1222				    &asn1_oid_id_pkdhkeydata,
1223				    buf.data,
1224				    buf.length,
1225				    NULL,
1226				    cert,
1227				    cp->peer,
1228				    cp->client_anchors,
1229				    kdc_identity->certpool,
1230				    &signed_data);
1231    if (ret) {
1232	kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1233	goto out;
1234    }
1235    *kdc_cert = cert;
1236
1237    ret = _krb5_pk_mk_ContentInfo(context,
1238				  &signed_data,
1239				  &asn1_oid_id_pkcs7_signedData,
1240				  content_info);
1241    if (ret)
1242	goto out;
1243
1244 out:
1245    if (ret && *kdc_cert) {
1246	hx509_cert_free(*kdc_cert);
1247	*kdc_cert = NULL;
1248    }
1249
1250    krb5_data_free(&buf);
1251    krb5_data_free(&signed_data);
1252    free_KDCDHKeyInfo(&dh_info);
1253
1254    return ret;
1255}
1256
1257/*
1258 *
1259 */
1260
1261static krb5_error_code
1262create_fxkey(krb5_context context,
1263	     METHOD_DATA *md,
1264	     krb5_keyblock *reply_key,
1265	     krb5_enctype sessionetype,
1266	     krb5_keyblock *sessionkey)
1267{
1268    krb5_crypto reply_crypto = NULL, kxkey_crypto = NULL;
1269    krb5_data pepper1, pepper2, data;
1270    EncryptedData encdata;
1271    krb5_error_code ret;
1272    krb5_keyblock kxkey;
1273    size_t size = 0;
1274
1275    ret = krb5_generate_random_keyblock(context, sessionetype, &kxkey);
1276    if (ret)
1277	return ret;
1278
1279    ret = krb5_crypto_init(context, reply_key, 0, &reply_crypto);
1280    if (ret)
1281	goto out;
1282
1283    ret = krb5_crypto_init(context, &kxkey, 0, &kxkey_crypto);
1284    if (ret)
1285	goto out;
1286
1287    pepper1.data = "PKINIT";
1288    pepper1.length = strlen(pepper1.data);
1289    /* MIT uses KEYEXCHANGE, rfc6112 say KeyExchange */
1290    pepper2.data = "KEYEXCHANGE";
1291    pepper2.length = strlen(pepper2.data);
1292
1293    ret = krb5_crypto_fx_cf2(context, kxkey_crypto, reply_crypto,
1294			     &pepper1, &pepper2, sessionetype,
1295			     sessionkey);
1296    if (ret)
1297	goto out;
1298
1299    ASN1_MALLOC_ENCODE(EncryptionKey, data.data, data.length, &kxkey, &size, ret);
1300    if (ret)
1301	goto out;
1302    if(data.length != size)
1303	krb5_abortx(context, "internal error in ASN.1 encoder");
1304
1305    ret = krb5_encrypt_EncryptedData(context,
1306				     reply_crypto,
1307				     KRB5_KU_PA_PKINIT_KX,
1308				     data.data,
1309				     data.length,
1310				     0,
1311				     &encdata);
1312    free(data.data);
1313
1314    ASN1_MALLOC_ENCODE(EncryptedData, data.data, data.length, &encdata, &size, ret);
1315    free_EncryptedData(&encdata);
1316    if (ret)
1317	goto out;
1318    if(data.length != size)
1319	krb5_abortx(context, "internal error in ASN.1 encoder");
1320
1321    ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, data.data, data.length);
1322    if (ret)
1323	free(data.data);
1324
1325 out:
1326    if (ret)
1327	krb5_set_error_message(context, ret, "Failed adding PKINIT_KX %d padata", ret);
1328
1329    krb5_free_keyblock_contents(context, &kxkey);
1330    if (reply_crypto)
1331	krb5_crypto_destroy(context, reply_crypto);
1332    if (kxkey_crypto)
1333	krb5_crypto_destroy(context, kxkey_crypto);
1334
1335    return ret;
1336}
1337
1338
1339/*
1340 *
1341 */
1342
1343krb5_error_code
1344_kdc_pk_mk_pa_reply(kdc_request_t r, pk_client_params *cp)
1345{
1346    hx509_cert kdc_cert = NULL;
1347    size_t len = 0, size = 0;
1348    krb5_enctype enctype;
1349    krb5_error_code ret;
1350    void *buf = NULL;
1351    int pa_type;
1352
1353    if (!r->config->enable_pkinit) {
1354	krb5_clear_error_message(r->context);
1355	return 0;
1356    }
1357
1358    ret = _kdc_get_preferred_enctype(r->context,
1359				     r->config,
1360				     r->client,
1361				     r->client_name,
1362				     r->req.req_body.etype.val,
1363				     r->req.req_body.etype.len,
1364				     &enctype);
1365    if (ret)
1366	return ret;
1367
1368    if (cp->type == PKINIT_27) {
1369	PA_PK_AS_REP rep;
1370	const char *type = "unknown", *other = "";
1371
1372	memset(&rep, 0, sizeof(rep));
1373
1374	pa_type = KRB5_PADATA_PK_AS_REP;
1375
1376	if (cp->keyex == USE_RSA) {
1377	    ContentInfo info;
1378
1379	    type = "enckey";
1380
1381	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1382
1383	    ret = krb5_generate_random_keyblock(r->context, enctype,
1384						&cp->reply_key);
1385	    if (ret) {
1386		free_PA_PK_AS_REP(&rep);
1387		goto out;
1388	    }
1389	    ret = pk_mk_pa_reply_enckey(r->context,
1390					r->config,
1391					cp,
1392					&r->req,
1393					&r->request,
1394					&cp->reply_key,
1395					&info,
1396					&kdc_cert);
1397	    if (ret) {
1398		free_PA_PK_AS_REP(&rep);
1399		goto out;
1400	    }
1401	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1402			       rep.u.encKeyPack.length, &info, &size,
1403			       ret);
1404	    free_ContentInfo(&info);
1405	    if (ret) {
1406		krb5_set_error_message(r->context, ret, "encoding of Key ContentInfo "
1407				       "failed %d", ret);
1408		free_PA_PK_AS_REP(&rep);
1409		goto out;
1410	    }
1411	    if (rep.u.encKeyPack.length != size)
1412		krb5_abortx(r->context, "Internal ASN.1 encoder error");
1413
1414	    ret = krb5_generate_random_keyblock(r->context, r->sessionetype,
1415						&r->session_key);
1416	    if (ret) {
1417		free_PA_PK_AS_REP(&rep);
1418		goto out;
1419	    }
1420
1421	} else {
1422	    ContentInfo info;
1423
1424	    switch (cp->keyex) {
1425	    case USE_DH: type = "dh"; break;
1426#ifdef HAVE_OPENSSL
1427	    case USE_ECDH: type = "ecdh"; break;
1428#endif
1429	    default: krb5_abortx(r->context, "unknown keyex");
1430	    }
1431
1432	    if (cp->dh_group_name)
1433		other = cp->dh_group_name;
1434
1435	    rep.element = choice_PA_PK_AS_REP_dhInfo;
1436
1437	    ret = generate_dh_keyblock(r->context, cp, enctype);
1438	    if (ret)
1439		return ret;
1440
1441	    ret = pk_mk_pa_reply_dh(r->context, r->config,
1442				    cp,
1443				    &info,
1444				    &kdc_cert);
1445	    if (ret) {
1446		free_PA_PK_AS_REP(&rep);
1447		krb5_set_error_message(r->context, ret,
1448				       "create pa-reply-dh "
1449				       "failed %d", ret);
1450		goto out;
1451	    }
1452
1453	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1454			       rep.u.dhInfo.dhSignedData.length, &info, &size,
1455			       ret);
1456	    free_ContentInfo(&info);
1457	    if (ret) {
1458		krb5_set_error_message(r->context, ret,
1459				       "encoding of Key ContentInfo "
1460				       "failed %d", ret);
1461		free_PA_PK_AS_REP(&rep);
1462		goto out;
1463	    }
1464	    if (rep.u.encKeyPack.length != size)
1465		krb5_abortx(r->context, "Internal ASN.1 encoder error");
1466
1467	    ret = create_fxkey(r->context, &r->outpadata, &cp->reply_key, r->sessionetype, &r->session_key);
1468	    if (ret) {
1469		free_PA_PK_AS_REP(&rep);
1470		goto out;
1471	    }
1472	}
1473
1474#define use_btmm_with_enckey 1
1475	if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1476	    PA_PK_AS_REP_BTMM btmm;
1477	    heim_any any;
1478
1479	    any.data = rep.u.encKeyPack.data;
1480	    any.length = rep.u.encKeyPack.length;
1481
1482	    btmm.dhSignedData = NULL;
1483	    btmm.encKeyPack = &any;
1484
1485	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1486	} else {
1487	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1488	}
1489
1490	free_PA_PK_AS_REP(&rep);
1491	if (ret) {
1492	    krb5_set_error_message(r->context, ret,
1493				   "encode PA-PK-AS-REP failed %d", ret);
1494	    goto out;
1495	}
1496	if (len != size)
1497	    krb5_abortx(r->context, "Internal ASN.1 encoder error");
1498
1499	kdc_log(r->context, r->config, 0, "PK-INIT using %s %s", type, other);
1500
1501    } else if (cp->type == PKINIT_WIN2K) {
1502	PA_PK_AS_REP_Win2k rep;
1503	ContentInfo info;
1504
1505	if (cp->keyex != USE_RSA) {
1506	    ret = KRB5KRB_ERR_GENERIC;
1507	    krb5_set_error_message(r->context, ret,
1508				   "Windows PK-INIT doesn't support DH");
1509	    goto out;
1510	}
1511
1512	memset(&rep, 0, sizeof(rep));
1513
1514	pa_type = KRB5_PADATA_PK_AS_REP_19;
1515	rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1516
1517	ret = krb5_generate_random_keyblock(r->context, enctype,
1518					    &cp->reply_key);
1519	if (ret) {
1520	    free_PA_PK_AS_REP_Win2k(&rep);
1521	    goto out;
1522	}
1523	ret = pk_mk_pa_reply_enckey(r->context,
1524				    r->config,
1525				    cp,
1526				    &r->req,
1527				    &r->request,
1528				    &cp->reply_key,
1529				    &info,
1530				    &kdc_cert);
1531	if (ret) {
1532	    free_PA_PK_AS_REP_Win2k(&rep);
1533	    goto out;
1534	}
1535	ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1536			   rep.u.encKeyPack.length, &info, &size,
1537			   ret);
1538	free_ContentInfo(&info);
1539	if (ret) {
1540	    krb5_set_error_message(r->context, ret, "encoding of Key ContentInfo "
1541				  "failed %d", ret);
1542	    free_PA_PK_AS_REP_Win2k(&rep);
1543	    goto out;
1544	}
1545	if (rep.u.encKeyPack.length != size)
1546	    krb5_abortx(r->context, "Internal ASN.1 encoder error");
1547
1548	ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1549	free_PA_PK_AS_REP_Win2k(&rep);
1550	if (ret) {
1551	    krb5_set_error_message(r->context, ret,
1552				  "encode PA-PK-AS-REP-Win2k failed %d", ret);
1553	    goto out;
1554	}
1555	if (len != size)
1556	    krb5_abortx(r->context, "Internal ASN.1 encoder error");
1557
1558	ret = krb5_generate_random_keyblock(r->context, r->sessionetype,
1559					    &r->session_key);
1560	if (ret) {
1561	    free(buf);
1562	    goto out;
1563	}
1564
1565    } else
1566	krb5_abortx(r->context, "PK-INIT internal error");
1567
1568
1569    ret = krb5_padata_add(r->context, &r->outpadata, pa_type, buf, len);
1570    if (ret) {
1571	krb5_set_error_message(r->context, ret,
1572			       "Failed adding PA-PK-AS-REP %d", ret);
1573	free(buf);
1574	goto out;
1575    }
1576
1577    if (r->config->pkinit_kdc_ocsp_file) {
1578
1579	if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1580	    struct stat sb;
1581	    ssize_t sret;
1582	    int fd;
1583
1584	    krb5_data_free(&ocsp.data);
1585
1586	    ocsp.expire = 0;
1587	    ocsp.next_update = kdc_time + 60 * 5;
1588
1589	    fd = open(r->config->pkinit_kdc_ocsp_file, O_RDONLY);
1590	    if (fd < 0) {
1591		kdc_log(r->context, r->config, 0,
1592			"PK-INIT failed to open ocsp data file %d", errno);
1593		goto out_ocsp;
1594	    }
1595	    ret = fstat(fd, &sb);
1596	    if (ret) {
1597		ret = errno;
1598		close(fd);
1599		kdc_log(r->context, r->config, 0,
1600			"PK-INIT failed to stat ocsp data %d", ret);
1601		goto out_ocsp;
1602	    }
1603	    if (sb.st_size > (off_t)(SIZE_T_MAX >> 1)) {
1604		ret = errno;
1605		close(fd);
1606		kdc_log(r->context, r->config, 0,
1607			"PK-INIT OCSP data too large %d", ret);
1608		goto out_ocsp;
1609	    }
1610	    ret = krb5_data_alloc(&ocsp.data, (size_t)sb.st_size);
1611	    if (ret) {
1612		close(fd);
1613		kdc_log(r->context, r->config, 0,
1614			"PK-INIT failed to stat ocsp data %d", ret);
1615		goto out_ocsp;
1616	    }
1617	    ocsp.data.length = (size_t)sb.st_size;
1618	    sret = read(fd, ocsp.data.data, (size_t)sb.st_size);
1619	    close(fd);
1620	    if (sret != sb.st_size) {
1621		kdc_log(r->context, r->config, 0,
1622			"PK-INIT failed to read ocsp data %d", errno);
1623		goto out_ocsp;
1624	    }
1625
1626	    ret = hx509_ocsp_verify(r->context->hx509ctx,
1627				    kdc_time,
1628				    kdc_cert,
1629				    0,
1630				    ocsp.data.data, ocsp.data.length,
1631				    &ocsp.expire);
1632	    if (ret) {
1633		kdc_log(r->context, r->config, 0,
1634			"PK-INIT failed to verify ocsp data %d", ret);
1635		krb5_data_free(&ocsp.data);
1636		ocsp.expire = 0;
1637	    } else if (ocsp.expire > 180) {
1638		ocsp.expire -= 180; /* refetch the ocsp before it expire */
1639		ocsp.next_update = ocsp.expire;
1640	    } else {
1641		ocsp.next_update = kdc_time;
1642	    }
1643	out_ocsp:
1644	    ret = 0;
1645	}
1646
1647	if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1648
1649	    ret = krb5_padata_add(r->context, &r->outpadata,
1650				  KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1651				  ocsp.data.data, ocsp.data.length);
1652	    if (ret) {
1653		krb5_set_error_message(r->context, ret,
1654				       "Failed adding OCSP response %d", ret);
1655		goto out;
1656	    }
1657	}
1658    }
1659
1660out:
1661    if (kdc_cert)
1662	hx509_cert_free(kdc_cert);
1663
1664    if (ret == 0)
1665	ret = krb5_copy_keyblock_contents(r->context, &cp->reply_key, &r->reply_key);
1666    return ret;
1667}
1668
1669static int
1670match_rfc_san(krb5_context context,
1671	      krb5_kdc_configuration *config,
1672	      hx509_context hx509ctx,
1673	      hx509_cert client_cert,
1674	      krb5_const_principal match)
1675{
1676    hx509_octet_string_list list;
1677    int ret, found = 0;
1678    size_t i;
1679
1680    memset(&list, 0 , sizeof(list));
1681
1682    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1683						   client_cert,
1684						   &asn1_oid_id_pkinit_san,
1685						   &list);
1686    if (ret)
1687	goto out;
1688
1689    for (i = 0; !found && i < list.len; i++) {
1690	krb5_principal_data principal;
1691	KRB5PrincipalName kn;
1692	size_t size;
1693
1694	ret = decode_KRB5PrincipalName(list.val[i].data,
1695				       list.val[i].length,
1696				       &kn, &size);
1697	if (ret) {
1698	    const char *msg = krb5_get_error_message(context, ret);
1699	    kdc_log(context, config, 0,
1700		    "Decoding kerberos name in certificate failed: %s", msg);
1701	    krb5_free_error_message(context, msg);
1702	    break;
1703	}
1704	if (size != list.val[i].length) {
1705	    kdc_log(context, config, 0,
1706		    "Decoding kerberos name have extra bits on the end");
1707	    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1708	    break;
1709	}
1710
1711	principal.name = kn.principalName;
1712	principal.realm = kn.realm;
1713
1714	if (krb5_principal_compare(context, &principal, match) == TRUE)
1715	    found = 1;
1716	free_KRB5PrincipalName(&kn);
1717    }
1718
1719out:
1720    hx509_free_octet_string_list(&list);
1721    if (ret)
1722	return ret;
1723
1724    if (!found)
1725	return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1726
1727    return 0;
1728}
1729
1730static int
1731match_ms_upn_san(krb5_context context,
1732		 krb5_kdc_configuration *config,
1733		 hx509_context hx509ctx,
1734		 hx509_cert client_cert,
1735		 HDB *clientdb,
1736		 hdb_entry_ex *client)
1737{
1738    hx509_octet_string_list list;
1739    krb5_principal principal = NULL;
1740    int ret;
1741    MS_UPN_SAN upn;
1742    size_t size;
1743
1744    memset(&list, 0 , sizeof(list));
1745
1746    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1747						   client_cert,
1748						   &asn1_oid_id_pkinit_ms_san,
1749						   &list);
1750    if (ret)
1751	goto out;
1752    if (list.len == 0) {
1753	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1754	goto out;
1755    }
1756
1757    if (list.len != 1) {
1758	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1759	kdc_log(context, config, 0,
1760		"More then one PK-INIT MS UPN SAN");
1761	goto out;
1762    }
1763
1764    ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1765    if (ret) {
1766	kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1767	goto out;
1768    }
1769    if (size != list.val[0].length) {
1770	free_MS_UPN_SAN(&upn);
1771	kdc_log(context, config, 0, "Trailing data in ");
1772	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1773	goto out;
1774    }
1775
1776    kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1777
1778    ret = krb5_parse_name(context, upn, &principal);
1779    free_MS_UPN_SAN(&upn);
1780    if (ret) {
1781	kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1782	goto out;
1783    }
1784
1785    if (clientdb->hdb_check_pkinit_ms_upn_match) {
1786	ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1787    } else {
1788
1789	/*
1790	 * This is very wrong, but will do for a fallback
1791	 */
1792	strupr(principal->realm);
1793
1794	if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1795	    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1796    }
1797
1798out:
1799    if (principal)
1800	krb5_free_principal(context, principal);
1801    hx509_free_octet_string_list(&list);
1802
1803    return ret;
1804}
1805
1806krb5_error_code
1807_kdc_pk_check_client(krb5_context context,
1808		     krb5_kdc_configuration *config,
1809		     HDB *clientdb,
1810		     hdb_entry_ex *client,
1811		     InitiatorName *pku2uInitiatorAssertion,
1812		     pk_client_params *cp,
1813		     char **subject_name)
1814{
1815    const HDB_Ext_PKINIT_acl *acl;
1816    const HDB_Ext_PKINIT_cert *pc;
1817    krb5_error_code ret;
1818    hx509_name name, issuer = NULL, taname = NULL;
1819    size_t i;
1820
1821    if (cp->cert == NULL) {
1822
1823	*subject_name = strdup("anonymous client");
1824	if (*subject_name == NULL)
1825	    return ENOMEM;
1826	return 0;
1827    }
1828
1829    ret = hx509_cert_get_base_subject(context->hx509ctx,
1830				      cp->cert,
1831				      &name);
1832    if (ret)
1833	return ret;
1834
1835    ret = hx509_cert_get_issuer(cp->cert, &issuer);
1836    if (ret) {
1837	hx509_name_free(&name);
1838	return ret;
1839    }
1840
1841    ret = hx509_cert_get_subject(cp->tacert, &taname);
1842    if (ret) {
1843	hx509_name_free(&name);
1844	hx509_name_free(&issuer);
1845	return ret;
1846    }
1847
1848    ret = hx509_name_to_string(name, subject_name);
1849    if (ret) {
1850	hx509_name_free(&name);
1851	hx509_name_free(&issuer);
1852	hx509_name_free(&taname);
1853	return ret;
1854    }
1855
1856    kdc_log(context, config, 0,
1857	    "Trying to authorize PK-INIT subject DN %s",
1858	    *subject_name);
1859
1860    if (pku2uInitiatorAssertion) {
1861	if (pku2uInitiatorAssertion->element != choice_InitiatorName_nameNotInCert) {
1862	    kdc_log(context, config, 5,
1863		    "init name assertion not nameNotInCert");
1864	    goto fail;
1865	}
1866	/* XXX check that assertion is in the cert */
1867    }
1868
1869    ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1870    if (ret == 0 && pc) {
1871	hx509_cert cert;
1872	size_t j;
1873
1874	for (j = 0; j < pc->len; j++) {
1875	    ret = hx509_cert_init_data(context->hx509ctx,
1876				       pc->val[j].cert.data,
1877				       pc->val[j].cert.length,
1878				       &cert);
1879	    if (ret)
1880		continue;
1881	    ret = hx509_cert_cmp(cert, cp->cert);
1882	    hx509_cert_free(cert);
1883	    if (ret == 0) {
1884		hx509_name_free(&name);
1885		hx509_name_free(&issuer);
1886		hx509_name_free(&taname);
1887		kdc_log(context, config, 5,
1888			"Found matching PK-INIT cert in hdb");
1889		return 0;
1890	    }
1891	}
1892    }
1893
1894    if (config->pkinit_princ_in_cert) {
1895	ret = match_rfc_san(context, config,
1896			    context->hx509ctx,
1897			    cp->cert,
1898			    client->entry.principal);
1899	if (ret == 0) {
1900	    hx509_name_free(&name);
1901	    hx509_name_free(&issuer);
1902	    hx509_name_free(&taname);
1903	    kdc_log(context, config, 5,
1904		    "Found matching PK-INIT SAN in certificate");
1905	    return 0;
1906	}
1907	ret = match_ms_upn_san(context, config,
1908			       context->hx509ctx,
1909			       cp->cert,
1910			       clientdb,
1911			       client);
1912	if (ret == 0) {
1913	    hx509_name_free(&name);
1914	    hx509_name_free(&issuer);
1915	    hx509_name_free(&taname);
1916	    kdc_log(context, config, 5,
1917		    "Found matching MS UPN SAN in certificate");
1918	    return 0;
1919	}
1920    }
1921
1922    ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1923    if (ret == 0 && acl != NULL) {
1924
1925	for (i = 0; i < acl->len; i++) {
1926	    hx509_name n;
1927
1928	    ret = hx509_parse_name(context->hx509ctx, acl->val[i].subject, &n);
1929	    if (ret)
1930		continue;
1931
1932	    ret = hx509_name_cmp(name, n);
1933	    hx509_name_free(&n);
1934	    if (ret)
1935		continue;
1936
1937	    if (acl->val[i].issuer) {
1938		ret = hx509_parse_name(context->hx509ctx,
1939				       *acl->val[i].issuer, &n);
1940		if (ret)
1941		    continue;
1942
1943		ret = hx509_name_cmp(issuer, n);
1944		hx509_name_free(&n);
1945		if (ret)
1946		    continue;
1947	    }
1948	    /* Don't support anchor checking right now */
1949	    if (acl->val[i].anchor) {
1950		ret = hx509_parse_name(context->hx509ctx,
1951				       *acl->val[i].anchor, &n);
1952		if (ret)
1953		    continue;
1954
1955		ret = hx509_name_cmp(taname, n);
1956		hx509_name_free(&n);
1957		if (ret)
1958		    continue;
1959	    }
1960
1961	    hx509_name_free(&name);
1962	    hx509_name_free(&issuer);
1963	    hx509_name_free(&taname);
1964	    kdc_log(context, config, 5,
1965		    "Found matching PK-INIT database ACL");
1966	    return 0;
1967	}
1968    }
1969
1970    for (i = 0; i < principal_mappings.len; i++) {
1971	krb5_boolean b;
1972
1973	b = krb5_principal_compare(context,
1974				   client->entry.principal,
1975				   principal_mappings.val[i].principal);
1976	if (b == FALSE)
1977	    continue;
1978	if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1979	    continue;
1980
1981	hx509_name_free(&name);
1982	hx509_name_free(&issuer);
1983	hx509_name_free(&taname);
1984
1985	kdc_log(context, config, 5,
1986		"Found matching PK-INIT FILE ACL");
1987	return 0;
1988    }
1989
1990 fail:
1991    hx509_name_free(&name);
1992    hx509_name_free(&issuer);
1993    hx509_name_free(&taname);
1994
1995    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1996    krb5_set_error_message(context, ret,
1997			  "PKINIT no matching principals for %s",
1998			  *subject_name);
1999
2000    kdc_log(context, config, 5,
2001	    "PKINIT no matching principals for %s",
2002	    *subject_name);
2003
2004    free(*subject_name);
2005    *subject_name = NULL;
2006
2007    return ret;
2008}
2009
2010static krb5_error_code
2011add_principal_mapping(krb5_context context,
2012		      const char *principal_name,
2013		      const char * subject)
2014{
2015   struct pk_allowed_princ *tmp;
2016   krb5_principal principal;
2017   krb5_error_code ret;
2018
2019   tmp = realloc(principal_mappings.val,
2020	         (principal_mappings.len + 1) * sizeof(*tmp));
2021   if (tmp == NULL)
2022       return ENOMEM;
2023   principal_mappings.val = tmp;
2024
2025   ret = krb5_parse_name(context, principal_name, &principal);
2026   if (ret)
2027       return ret;
2028
2029   principal_mappings.val[principal_mappings.len].principal = principal;
2030
2031   principal_mappings.val[principal_mappings.len].subject = strdup(subject);
2032   if (principal_mappings.val[principal_mappings.len].subject == NULL) {
2033       krb5_free_principal(context, principal);
2034       return ENOMEM;
2035   }
2036   principal_mappings.len++;
2037
2038   return 0;
2039}
2040
2041krb5_error_code
2042_kdc_add_inital_verified_cas(krb5_context context,
2043			     krb5_kdc_configuration *config,
2044			     pk_client_params *cp,
2045			     EncTicketPart *tkt)
2046{
2047    AD_INITIAL_VERIFIED_CAS cas;
2048    krb5_error_code ret;
2049    krb5_data data;
2050    size_t size = 0;
2051
2052    memset(&cas, 0, sizeof(cas));
2053
2054    /* XXX add CAs to cas here */
2055
2056    ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
2057		       &cas, &size, ret);
2058    if (ret)
2059	return ret;
2060    if (data.length != size)
2061	krb5_abortx(context, "internal asn.1 encoder error");
2062
2063    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
2064				      KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
2065				      &data);
2066    krb5_data_free(&data);
2067    return ret;
2068}
2069
2070/*
2071 *
2072 */
2073
2074static void
2075load_mappings(krb5_context context, const char *fn)
2076{
2077    krb5_error_code ret;
2078    char buf[1024];
2079    unsigned long lineno = 0;
2080    FILE *f;
2081
2082    f = fopen(fn, "r");
2083    if (f == NULL)
2084	return;
2085
2086    while (fgets(buf, sizeof(buf), f) != NULL) {
2087	char *subject_name, *p;
2088
2089	buf[strcspn(buf, "\n")] = '\0';
2090	lineno++;
2091
2092	p = buf + strspn(buf, " \t");
2093
2094	if (*p == '#' || *p == '\0')
2095	    continue;
2096
2097	subject_name = strchr(p, ':');
2098	if (subject_name == NULL) {
2099	    krb5_warnx(context, "pkinit mapping file line %lu "
2100		       "missing \":\" :%s",
2101		       lineno, buf);
2102	    continue;
2103	}
2104	*subject_name++ = '\0';
2105
2106	ret = add_principal_mapping(context, p, subject_name);
2107	if (ret) {
2108	    krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
2109		      lineno, buf);
2110	    continue;
2111	}
2112    }
2113
2114    fclose(f);
2115}
2116
2117/*
2118 *
2119 */
2120
2121krb5_error_code
2122krb5_kdc_pk_initialize(krb5_context context,
2123		       krb5_kdc_configuration *config,
2124		       const char *user_id,
2125		       const char *anchors,
2126		       char **pool,
2127		       char **revoke_list)
2128{
2129    const char *file;
2130    char *fn = NULL;
2131    krb5_error_code ret;
2132
2133    file = krb5_config_get_string(context, NULL,
2134				  "libdefaults", "moduli", NULL);
2135
2136    ret = _krb5_parse_moduli(context, file, &moduli);
2137    if (ret)
2138	krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
2139
2140    principal_mappings.len = 0;
2141    principal_mappings.val = NULL;
2142
2143    ret = _krb5_pk_load_id(context,
2144			   &kdc_identity,
2145			   user_id,
2146			   anchors,
2147			   pool,
2148			   revoke_list,
2149			   NULL,
2150			   NULL,
2151			   NULL);
2152    if (ret) {
2153	krb5_warn(context, ret, "PKINIT: ");
2154	config->enable_pkinit = 0;
2155	return ret;
2156    }
2157
2158    {
2159	hx509_query *q;
2160	hx509_cert cert;
2161
2162	ret = hx509_query_alloc(context->hx509ctx, &q);
2163	if (ret) {
2164	    krb5_warnx(context, "PKINIT: out of memory");
2165	    return ENOMEM;
2166	}
2167
2168	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2169	if (config->pkinit_kdc_friendly_name)
2170	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2171
2172	ret = hx509_certs_find(context->hx509ctx,
2173			       kdc_identity->certs,
2174			       q,
2175			       &cert);
2176	hx509_query_free(context->hx509ctx, q);
2177	if (ret == 0) {
2178	    if (hx509_cert_check_eku(context->hx509ctx, cert,
2179				     &asn1_oid_id_apple_system_id, 0) == 0)
2180	    {
2181		/* AppleID BTMM cert, then is all fine */
2182	    } else if (hx509_cert_check_eku(context->hx509ctx, cert,
2183					    &asn1_oid_id_pkkdcekuoid, 0))
2184	    {
2185		hx509_name name;
2186		char *str;
2187		ret = hx509_cert_get_subject(cert, &name);
2188		if (ret == 0) {
2189		    hx509_name_to_string(name, &str);
2190		    krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2191			       "is missing the PK-INIT KDC EKU, this is bad for "
2192			       "interoperability.", str);
2193		    hx509_name_free(&name);
2194		    free(str);
2195		}
2196	    }
2197	    hx509_cert_free(cert);
2198	} else
2199	    krb5_warnx(context, "PKINIT: failed to find a signing "
2200		       "certifiate with a public key");
2201    }
2202
2203    if (krb5_config_get_bool_default(context,
2204				     NULL,
2205				     FALSE,
2206				     "kdc",
2207				     "pkinit_allow_proxy_certificate",
2208				     NULL))
2209	config->pkinit_allow_proxy_certs = 1;
2210
2211    file = krb5_config_get_string(context,
2212				  NULL,
2213				  "kdc",
2214				  "pkinit_mappings_file",
2215				  NULL);
2216    if (file == NULL) {
2217	asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2218	file = fn;
2219    }
2220
2221    load_mappings(context, file);
2222    if (fn)
2223	free(fn);
2224
2225    return 0;
2226}
2227
2228#endif /* PKINIT */
2229