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