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 || 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 (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    default:
987	krb5_abortx(context, "internal pkinit error");
988    }
989
990    if (do_win2k) {
991	ReplyKeyPack_Win2k kp;
992	memset(&kp, 0, sizeof(kp));
993
994	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
995	if (ret) {
996	    krb5_clear_error_message(context);
997	    goto out;
998	}
999	kp.nonce = cp->nonce;
1000
1001	ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
1002			   buf.data, buf.length,
1003			   &kp, &size,ret);
1004	free_ReplyKeyPack_Win2k(&kp);
1005    } else {
1006	krb5_crypto ascrypto;
1007	ReplyKeyPack kp;
1008	memset(&kp, 0, sizeof(kp));
1009
1010	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
1011	if (ret) {
1012	    krb5_clear_error_message(context);
1013	    goto out;
1014	}
1015
1016	ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
1017	if (ret) {
1018	    krb5_clear_error_message(context);
1019	    goto out;
1020	}
1021
1022	ret = krb5_create_checksum(context, ascrypto, 6, 0,
1023				   req_buffer->data, req_buffer->length,
1024				   &kp.asChecksum);
1025	if (ret) {
1026	    krb5_clear_error_message(context);
1027	    goto out;
1028	}
1029
1030	ret = krb5_crypto_destroy(context, ascrypto);
1031	if (ret) {
1032	    krb5_clear_error_message(context);
1033	    goto out;
1034	}
1035	ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1036	free_ReplyKeyPack(&kp);
1037    }
1038    if (ret) {
1039	krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1040			       "failed (%d)", ret);
1041	goto out;
1042    }
1043    if (buf.length != size)
1044	krb5_abortx(context, "Internal ASN.1 encoder error");
1045
1046    {
1047	hx509_query *q;
1048	hx509_cert cert;
1049
1050	ret = hx509_query_alloc(context->hx509ctx, &q);
1051	if (ret)
1052	    goto out;
1053
1054	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1055	if (config->pkinit_kdc_friendly_name)
1056	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1057
1058	ret = hx509_certs_find(context->hx509ctx,
1059			       kdc_identity->certs,
1060			       q,
1061			       &cert);
1062	hx509_query_free(context->hx509ctx, q);
1063	if (ret)
1064	    goto out;
1065
1066	ret = hx509_cms_create_signed_1(context->hx509ctx,
1067					0,
1068					sdAlg,
1069					buf.data,
1070					buf.length,
1071					NULL,
1072					cert,
1073					cp->peer,
1074					cp->client_anchors,
1075					kdc_identity->certpool,
1076					&signed_data);
1077	*kdc_cert = cert;
1078    }
1079
1080    krb5_data_free(&buf);
1081    if (ret)
1082	goto out;
1083
1084    if (cp->type == PKINIT_WIN2K) {
1085	ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1086					 &signed_data,
1087					 &buf);
1088	if (ret)
1089	    goto out;
1090	krb5_data_free(&signed_data);
1091	signed_data = buf;
1092    }
1093
1094    ret = hx509_cms_envelope_1(context->hx509ctx,
1095			       HX509_CMS_EV_NO_KU_CHECK,
1096			       cp->cert,
1097			       signed_data.data, signed_data.length,
1098			       envelopedAlg,
1099			       evAlg, &buf);
1100    if (ret)
1101	goto out;
1102
1103    ret = _krb5_pk_mk_ContentInfo(context,
1104				  &buf,
1105				  &asn1_oid_id_pkcs7_envelopedData,
1106				  content_info);
1107out:
1108    if (ret && *kdc_cert) {
1109        hx509_cert_free(*kdc_cert);
1110	*kdc_cert = NULL;
1111    }
1112
1113    krb5_data_free(&buf);
1114    krb5_data_free(&signed_data);
1115    return ret;
1116}
1117
1118/*
1119 *
1120 */
1121
1122static krb5_error_code
1123pk_mk_pa_reply_dh(krb5_context context,
1124		  krb5_kdc_configuration *config,
1125      		  pk_client_params *cp,
1126		  ContentInfo *content_info,
1127		  hx509_cert *kdc_cert)
1128{
1129    KDCDHKeyInfo dh_info;
1130    krb5_data signed_data, buf;
1131    ContentInfo contentinfo;
1132    krb5_error_code ret;
1133    hx509_cert cert;
1134    hx509_query *q;
1135    size_t size = 0;
1136
1137    memset(&contentinfo, 0, sizeof(contentinfo));
1138    memset(&dh_info, 0, sizeof(dh_info));
1139    krb5_data_zero(&signed_data);
1140    krb5_data_zero(&buf);
1141
1142    *kdc_cert = NULL;
1143
1144    if (cp->keyex == USE_DH) {
1145	DH *kdc_dh = cp->u.dh.key;
1146	heim_integer i;
1147
1148	ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1149	if (ret)
1150	    return ret;
1151
1152	ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1153	der_free_heim_integer(&i);
1154	if (ret) {
1155	    krb5_set_error_message(context, ret, "ASN.1 encoding of "
1156				   "DHPublicKey failed (%d)", ret);
1157	    return ret;
1158	}
1159	if (buf.length != size)
1160	    krb5_abortx(context, "Internal ASN.1 encoder error");
1161
1162	dh_info.subjectPublicKey.length = buf.length * 8;
1163	dh_info.subjectPublicKey.data = buf.data;
1164	krb5_data_zero(&buf);
1165#ifdef HAVE_OPENSSL
1166    } else if (cp->keyex == USE_ECDH) {
1167	unsigned char *p;
1168	int len;
1169
1170	len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1171	if (len <= 0)
1172	    abort();
1173
1174	p = malloc(len);
1175	if (p == NULL)
1176	    abort();
1177
1178	dh_info.subjectPublicKey.length = len * 8;
1179	dh_info.subjectPublicKey.data = p;
1180
1181	len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1182	if (len <= 0)
1183	    abort();
1184#endif
1185    } else
1186	krb5_abortx(context, "no keyex selected ?");
1187
1188
1189    dh_info.nonce = cp->nonce;
1190
1191    ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1192		       ret);
1193    if (ret) {
1194	krb5_set_error_message(context, ret, "ASN.1 encoding of "
1195			       "KdcDHKeyInfo failed (%d)", ret);
1196	goto out;
1197    }
1198    if (buf.length != size)
1199	krb5_abortx(context, "Internal ASN.1 encoder error");
1200
1201    /*
1202     * Create the SignedData structure and sign the KdcDHKeyInfo
1203     * filled in above
1204     */
1205
1206    ret = hx509_query_alloc(context->hx509ctx, &q);
1207    if (ret)
1208	goto out;
1209
1210    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1211    if (config->pkinit_kdc_friendly_name)
1212	hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1213
1214    ret = hx509_certs_find(context->hx509ctx,
1215			   kdc_identity->certs,
1216			   q,
1217			   &cert);
1218    hx509_query_free(context->hx509ctx, q);
1219    if (ret)
1220	goto out;
1221
1222    ret = hx509_cms_create_signed_1(context->hx509ctx,
1223				    0,
1224				    &asn1_oid_id_pkdhkeydata,
1225				    buf.data,
1226				    buf.length,
1227				    NULL,
1228				    cert,
1229				    cp->peer,
1230				    cp->client_anchors,
1231				    kdc_identity->certpool,
1232				    &signed_data);
1233    if (ret) {
1234	kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1235	goto out;
1236    }
1237    *kdc_cert = cert;
1238
1239    ret = _krb5_pk_mk_ContentInfo(context,
1240				  &signed_data,
1241				  &asn1_oid_id_pkcs7_signedData,
1242				  content_info);
1243    if (ret)
1244	goto out;
1245
1246 out:
1247    if (ret && *kdc_cert) {
1248	hx509_cert_free(*kdc_cert);
1249	*kdc_cert = NULL;
1250    }
1251
1252    krb5_data_free(&buf);
1253    krb5_data_free(&signed_data);
1254    free_KDCDHKeyInfo(&dh_info);
1255
1256    return ret;
1257}
1258
1259/*
1260 *
1261 */
1262
1263krb5_error_code
1264_kdc_pk_mk_pa_reply(krb5_context context,
1265		    krb5_kdc_configuration *config,
1266		    pk_client_params *cp,
1267		    const hdb_entry_ex *client,
1268		    krb5_enctype sessionetype,
1269		    const KDC_REQ *req,
1270		    const krb5_data *req_buffer,
1271		    krb5_keyblock *reply_key,
1272		    krb5_keyblock *sessionkey,
1273		    METHOD_DATA *md)
1274{
1275    krb5_error_code ret;
1276    void *buf = NULL;
1277    size_t len = 0, size = 0;
1278    krb5_enctype enctype;
1279    int pa_type;
1280    hx509_cert kdc_cert = NULL;
1281    size_t i;
1282
1283    if (!config->enable_pkinit) {
1284	krb5_clear_error_message(context);
1285	return 0;
1286    }
1287
1288    if (req->req_body.etype.len > 0) {
1289	for (i = 0; i < req->req_body.etype.len; i++)
1290	    if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1291		break;
1292	if (req->req_body.etype.len <= i) {
1293	    ret = KRB5KRB_ERR_GENERIC;
1294	    krb5_set_error_message(context, ret,
1295				   "No valid enctype available from client");
1296	    goto out;
1297	}
1298	enctype = req->req_body.etype.val[i];
1299    } else
1300	enctype = ETYPE_DES3_CBC_SHA1;
1301
1302    if (cp->type == PKINIT_27) {
1303	PA_PK_AS_REP rep;
1304	const char *type, *other = "";
1305
1306	memset(&rep, 0, sizeof(rep));
1307
1308	pa_type = KRB5_PADATA_PK_AS_REP;
1309
1310	if (cp->keyex == USE_RSA) {
1311	    ContentInfo info;
1312
1313	    type = "enckey";
1314
1315	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1316
1317	    ret = krb5_generate_random_keyblock(context, enctype,
1318						&cp->reply_key);
1319	    if (ret) {
1320		free_PA_PK_AS_REP(&rep);
1321		goto out;
1322	    }
1323	    ret = pk_mk_pa_reply_enckey(context,
1324					config,
1325					cp,
1326					req,
1327					req_buffer,
1328					&cp->reply_key,
1329					&info,
1330					&kdc_cert);
1331	    if (ret) {
1332		free_PA_PK_AS_REP(&rep);
1333		goto out;
1334	    }
1335	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1336			       rep.u.encKeyPack.length, &info, &size,
1337			       ret);
1338	    free_ContentInfo(&info);
1339	    if (ret) {
1340		krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1341				       "failed %d", ret);
1342		free_PA_PK_AS_REP(&rep);
1343		goto out;
1344	    }
1345	    if (rep.u.encKeyPack.length != size)
1346		krb5_abortx(context, "Internal ASN.1 encoder error");
1347
1348	    ret = krb5_generate_random_keyblock(context, sessionetype,
1349						sessionkey);
1350	    if (ret) {
1351		free_PA_PK_AS_REP(&rep);
1352		goto out;
1353	    }
1354
1355	} else {
1356	    ContentInfo info;
1357
1358	    switch (cp->keyex) {
1359	    case USE_DH: type = "dh"; break;
1360#ifdef HAVE_OPENSSL
1361	    case USE_ECDH: type = "ecdh"; break;
1362#endif
1363	    default: krb5_abortx(context, "unknown keyex"); break;
1364	    }
1365
1366	    if (cp->dh_group_name)
1367		other = cp->dh_group_name;
1368
1369	    rep.element = choice_PA_PK_AS_REP_dhInfo;
1370
1371	    ret = generate_dh_keyblock(context, cp, enctype);
1372	    if (ret)
1373		return ret;
1374
1375	    ret = pk_mk_pa_reply_dh(context, config,
1376				    cp,
1377				    &info,
1378				    &kdc_cert);
1379	    if (ret) {
1380		free_PA_PK_AS_REP(&rep);
1381		krb5_set_error_message(context, ret,
1382				       "create pa-reply-dh "
1383				       "failed %d", ret);
1384		goto out;
1385	    }
1386
1387	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1388			       rep.u.dhInfo.dhSignedData.length, &info, &size,
1389			       ret);
1390	    free_ContentInfo(&info);
1391	    if (ret) {
1392		krb5_set_error_message(context, ret,
1393				       "encoding of Key ContentInfo "
1394				       "failed %d", ret);
1395		free_PA_PK_AS_REP(&rep);
1396		goto out;
1397	    }
1398	    if (rep.u.encKeyPack.length != size)
1399		krb5_abortx(context, "Internal ASN.1 encoder error");
1400
1401	    /* XXX KRB-FX-CF2 */
1402	    ret = krb5_generate_random_keyblock(context, sessionetype,
1403						sessionkey);
1404	    if (ret) {
1405		free_PA_PK_AS_REP(&rep);
1406		goto out;
1407	    }
1408
1409	    /* XXX Add PA-PKINIT-KX */
1410
1411	}
1412
1413#define use_btmm_with_enckey 1
1414	if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1415	    PA_PK_AS_REP_BTMM btmm;
1416	    heim_any any;
1417
1418	    any.data = rep.u.encKeyPack.data;
1419	    any.length = rep.u.encKeyPack.length;
1420
1421	    btmm.dhSignedData = NULL;
1422	    btmm.encKeyPack = &any;
1423
1424	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1425	} else {
1426	    ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1427	}
1428
1429	free_PA_PK_AS_REP(&rep);
1430	if (ret) {
1431	    krb5_set_error_message(context, ret,
1432				   "encode PA-PK-AS-REP failed %d", ret);
1433	    goto out;
1434	}
1435	if (len != size)
1436	    krb5_abortx(context, "Internal ASN.1 encoder error");
1437
1438	kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1439
1440    } else if (cp->type == PKINIT_WIN2K) {
1441	PA_PK_AS_REP_Win2k rep;
1442	ContentInfo info;
1443
1444	if (cp->keyex != USE_RSA) {
1445	    ret = KRB5KRB_ERR_GENERIC;
1446	    krb5_set_error_message(context, ret,
1447				   "Windows PK-INIT doesn't support DH");
1448	    goto out;
1449	}
1450
1451	memset(&rep, 0, sizeof(rep));
1452
1453	pa_type = KRB5_PADATA_PK_AS_REP_19;
1454	rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1455
1456	ret = krb5_generate_random_keyblock(context, enctype,
1457					    &cp->reply_key);
1458	if (ret) {
1459	    free_PA_PK_AS_REP_Win2k(&rep);
1460	    goto out;
1461	}
1462	ret = pk_mk_pa_reply_enckey(context,
1463				    config,
1464				    cp,
1465				    req,
1466				    req_buffer,
1467				    &cp->reply_key,
1468				    &info,
1469				    &kdc_cert);
1470	if (ret) {
1471	    free_PA_PK_AS_REP_Win2k(&rep);
1472	    goto out;
1473	}
1474	ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1475			   rep.u.encKeyPack.length, &info, &size,
1476			   ret);
1477	free_ContentInfo(&info);
1478	if (ret) {
1479	    krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1480				  "failed %d", ret);
1481	    free_PA_PK_AS_REP_Win2k(&rep);
1482	    goto out;
1483	}
1484	if (rep.u.encKeyPack.length != size)
1485	    krb5_abortx(context, "Internal ASN.1 encoder error");
1486
1487	ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1488	free_PA_PK_AS_REP_Win2k(&rep);
1489	if (ret) {
1490	    krb5_set_error_message(context, ret,
1491				  "encode PA-PK-AS-REP-Win2k failed %d", ret);
1492	    goto out;
1493	}
1494	if (len != size)
1495	    krb5_abortx(context, "Internal ASN.1 encoder error");
1496
1497	ret = krb5_generate_random_keyblock(context, sessionetype,
1498					    sessionkey);
1499	if (ret) {
1500	    free(buf);
1501	    goto out;
1502	}
1503
1504    } else
1505	krb5_abortx(context, "PK-INIT internal error");
1506
1507
1508    ret = krb5_padata_add(context, md, pa_type, buf, len);
1509    if (ret) {
1510	krb5_set_error_message(context, ret,
1511			       "Failed adding PA-PK-AS-REP %d", ret);
1512	free(buf);
1513	goto out;
1514    }
1515
1516    if (config->pkinit_kdc_ocsp_file) {
1517
1518	if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1519	    struct stat sb;
1520	    ssize_t sret;
1521	    int fd;
1522
1523	    krb5_data_free(&ocsp.data);
1524
1525	    ocsp.expire = 0;
1526	    ocsp.next_update = kdc_time + 60 * 5;
1527
1528	    fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1529	    if (fd < 0) {
1530		kdc_log(context, config, 0,
1531			"PK-INIT failed to open ocsp data file %d", errno);
1532		goto out_ocsp;
1533	    }
1534	    ret = fstat(fd, &sb);
1535	    if (ret) {
1536		ret = errno;
1537		close(fd);
1538		kdc_log(context, config, 0,
1539			"PK-INIT failed to stat ocsp data %d", ret);
1540		goto out_ocsp;
1541	    }
1542	    if (sb.st_size > SIZE_T_MAX) {
1543		ret = errno;
1544		close(fd);
1545		kdc_log(context, config, 0,
1546			"PK-INIT OCSP data too large %d", ret);
1547		goto out_ocsp;
1548	    }
1549	    ret = krb5_data_alloc(&ocsp.data, (size_t)sb.st_size);
1550	    if (ret) {
1551		close(fd);
1552		kdc_log(context, config, 0,
1553			"PK-INIT failed to stat ocsp data %d", ret);
1554		goto out_ocsp;
1555	    }
1556	    ocsp.data.length = (size_t)sb.st_size;
1557	    sret = read(fd, ocsp.data.data, (size_t)sb.st_size);
1558	    close(fd);
1559	    if (sret != sb.st_size) {
1560		kdc_log(context, config, 0,
1561			"PK-INIT failed to read ocsp data %d", errno);
1562		goto out_ocsp;
1563	    }
1564
1565	    ret = hx509_ocsp_verify(context->hx509ctx,
1566				    kdc_time,
1567				    kdc_cert,
1568				    0,
1569				    ocsp.data.data, ocsp.data.length,
1570				    &ocsp.expire);
1571	    if (ret) {
1572		kdc_log(context, config, 0,
1573			"PK-INIT failed to verify ocsp data %d", ret);
1574		krb5_data_free(&ocsp.data);
1575		ocsp.expire = 0;
1576	    } else if (ocsp.expire > 180) {
1577		ocsp.expire -= 180; /* refetch the ocsp before it expire */
1578		ocsp.next_update = ocsp.expire;
1579	    } else {
1580		ocsp.next_update = kdc_time;
1581	    }
1582	out_ocsp:
1583	    ret = 0;
1584	}
1585
1586	if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1587
1588	    ret = krb5_padata_add(context, md,
1589				  KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1590				  ocsp.data.data, ocsp.data.length);
1591	    if (ret) {
1592		krb5_set_error_message(context, ret,
1593				       "Failed adding OCSP response %d", ret);
1594		goto out;
1595	    }
1596	}
1597    }
1598
1599out:
1600    if (kdc_cert)
1601	hx509_cert_free(kdc_cert);
1602
1603    if (ret == 0)
1604	ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key);
1605    return ret;
1606}
1607
1608static int
1609match_rfc_san(krb5_context context,
1610	      krb5_kdc_configuration *config,
1611	      hx509_context hx509ctx,
1612	      hx509_cert client_cert,
1613	      krb5_const_principal match)
1614{
1615    hx509_octet_string_list list;
1616    int ret, found = 0;
1617    size_t i;
1618
1619    memset(&list, 0 , sizeof(list));
1620
1621    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1622						   client_cert,
1623						   &asn1_oid_id_pkinit_san,
1624						   &list);
1625    if (ret)
1626	goto out;
1627
1628    for (i = 0; !found && i < list.len; i++) {
1629	krb5_principal_data principal;
1630	KRB5PrincipalName kn;
1631	size_t size;
1632
1633	ret = decode_KRB5PrincipalName(list.val[i].data,
1634				       list.val[i].length,
1635				       &kn, &size);
1636	if (ret) {
1637	    const char *msg = krb5_get_error_message(context, ret);
1638	    kdc_log(context, config, 0,
1639		    "Decoding kerberos name in certificate failed: %s", msg);
1640	    krb5_free_error_message(context, msg);
1641	    break;
1642	}
1643	if (size != list.val[i].length) {
1644	    kdc_log(context, config, 0,
1645		    "Decoding kerberos name have extra bits on the end");
1646	    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1647	    break;
1648	}
1649
1650	principal.name = kn.principalName;
1651	principal.realm = kn.realm;
1652
1653	if (krb5_principal_compare(context, &principal, match) == TRUE)
1654	    found = 1;
1655	free_KRB5PrincipalName(&kn);
1656    }
1657
1658out:
1659    hx509_free_octet_string_list(&list);
1660    if (ret)
1661	return ret;
1662
1663    if (!found)
1664	return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1665
1666    return 0;
1667}
1668
1669static int
1670match_ms_upn_san(krb5_context context,
1671		 krb5_kdc_configuration *config,
1672		 hx509_context hx509ctx,
1673		 hx509_cert client_cert,
1674		 HDB *clientdb,
1675		 hdb_entry_ex *client)
1676{
1677    hx509_octet_string_list list;
1678    krb5_principal principal = NULL;
1679    int ret;
1680    MS_UPN_SAN upn;
1681    size_t size;
1682
1683    memset(&list, 0 , sizeof(list));
1684
1685    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1686						   client_cert,
1687						   &asn1_oid_id_pkinit_ms_san,
1688						   &list);
1689    if (ret)
1690	goto out;
1691    if (list.len == 0) {
1692	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1693	goto out;
1694    }
1695
1696    if (list.len != 1) {
1697	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1698	kdc_log(context, config, 0,
1699		"More then one PK-INIT MS UPN SAN");
1700	goto out;
1701    }
1702
1703    ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1704    if (ret) {
1705	kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1706	goto out;
1707    }
1708    if (size != list.val[0].length) {
1709	free_MS_UPN_SAN(&upn);
1710	kdc_log(context, config, 0, "Trailing data in ");
1711	ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1712	goto out;
1713    }
1714
1715    kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1716
1717    ret = krb5_parse_name(context, upn, &principal);
1718    free_MS_UPN_SAN(&upn);
1719    if (ret) {
1720	kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1721	goto out;
1722    }
1723
1724    if (clientdb->hdb_check_pkinit_ms_upn_match) {
1725	ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1726    } else {
1727
1728	/*
1729	 * This is very wrong, but will do for a fallback
1730	 */
1731	strupr(principal->realm);
1732
1733	if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1734	    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1735    }
1736
1737out:
1738    if (principal)
1739	krb5_free_principal(context, principal);
1740    hx509_free_octet_string_list(&list);
1741
1742    return ret;
1743}
1744
1745krb5_error_code
1746_kdc_pk_check_client(krb5_context context,
1747		     krb5_kdc_configuration *config,
1748		     HDB *clientdb,
1749		     hdb_entry_ex *client,
1750		     InitiatorName *pku2uInitiatorAssertion,
1751		     pk_client_params *cp,
1752		     char **subject_name)
1753{
1754    const HDB_Ext_PKINIT_acl *acl;
1755    const HDB_Ext_PKINIT_cert *pc;
1756    krb5_error_code ret;
1757    hx509_name name, issuer = NULL, taname = NULL;
1758    size_t i;
1759
1760    if (cp->cert == NULL) {
1761
1762	*subject_name = strdup("anonymous client");
1763	if (*subject_name == NULL)
1764	    return ENOMEM;
1765	return 0;
1766    }
1767
1768    ret = hx509_cert_get_base_subject(context->hx509ctx,
1769				      cp->cert,
1770				      &name);
1771    if (ret)
1772	return ret;
1773
1774    ret = hx509_cert_get_issuer(cp->cert, &issuer);
1775    if (ret) {
1776	hx509_name_free(&name);
1777	return ret;
1778    }
1779
1780    ret = hx509_cert_get_subject(cp->tacert, &taname);
1781    if (ret) {
1782	hx509_name_free(&name);
1783	hx509_name_free(&issuer);
1784	return ret;
1785    }
1786
1787    ret = hx509_name_to_string(name, subject_name);
1788    if (ret) {
1789	hx509_name_free(&name);
1790	hx509_name_free(&issuer);
1791	hx509_name_free(&taname);
1792	return ret;
1793    }
1794
1795    kdc_log(context, config, 0,
1796	    "Trying to authorize PK-INIT subject DN %s",
1797	    *subject_name);
1798
1799    if (pku2uInitiatorAssertion) {
1800	if (pku2uInitiatorAssertion->element != choice_InitiatorName_nameNotInCert) {
1801	    kdc_log(context, config, 5,
1802		    "init name assertion not nameNotInCert");
1803	    goto fail;
1804	}
1805	/* XXX check that assertion is in the cert */
1806    }
1807
1808    ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1809    if (ret == 0 && pc) {
1810	hx509_cert cert;
1811	size_t j;
1812
1813	for (j = 0; j < pc->len; j++) {
1814	    ret = hx509_cert_init_data(context->hx509ctx,
1815				       pc->val[j].cert.data,
1816				       pc->val[j].cert.length,
1817				       &cert);
1818	    if (ret)
1819		continue;
1820	    ret = hx509_cert_cmp(cert, cp->cert);
1821	    hx509_cert_free(cert);
1822	    if (ret == 0) {
1823		hx509_name_free(&name);
1824		hx509_name_free(&issuer);
1825		hx509_name_free(&taname);
1826		kdc_log(context, config, 5,
1827			"Found matching PK-INIT cert in hdb");
1828		return 0;
1829	    }
1830	}
1831    }
1832
1833    if (config->pkinit_princ_in_cert) {
1834	ret = match_rfc_san(context, config,
1835			    context->hx509ctx,
1836			    cp->cert,
1837			    client->entry.principal);
1838	if (ret == 0) {
1839	    hx509_name_free(&name);
1840	    hx509_name_free(&issuer);
1841	    hx509_name_free(&taname);
1842	    kdc_log(context, config, 5,
1843		    "Found matching PK-INIT SAN in certificate");
1844	    return 0;
1845	}
1846	ret = match_ms_upn_san(context, config,
1847			       context->hx509ctx,
1848			       cp->cert,
1849			       clientdb,
1850			       client);
1851	if (ret == 0) {
1852	    hx509_name_free(&name);
1853	    hx509_name_free(&issuer);
1854	    hx509_name_free(&taname);
1855	    kdc_log(context, config, 5,
1856		    "Found matching MS UPN SAN in certificate");
1857	    return 0;
1858	}
1859    }
1860
1861    ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1862    if (ret == 0 && acl != NULL) {
1863
1864	for (i = 0; i < acl->len; i++) {
1865	    hx509_name n;
1866
1867	    ret = hx509_parse_name(context->hx509ctx, acl->val[i].subject, &n);
1868	    if (ret)
1869		continue;
1870
1871	    ret = hx509_name_cmp(name, n);
1872	    hx509_name_free(&n);
1873	    if (ret)
1874		continue;
1875
1876	    if (acl->val[i].issuer) {
1877		ret = hx509_parse_name(context->hx509ctx,
1878				       *acl->val[i].issuer, &n);
1879		if (ret)
1880		    continue;
1881
1882		ret = hx509_name_cmp(issuer, n);
1883		hx509_name_free(&n);
1884		if (ret)
1885		    continue;
1886	    }
1887	    /* Don't support anchor checking right now */
1888	    if (acl->val[i].anchor) {
1889		ret = hx509_parse_name(context->hx509ctx,
1890				       *acl->val[i].anchor, &n);
1891		if (ret)
1892		    continue;
1893
1894		ret = hx509_name_cmp(taname, n);
1895		hx509_name_free(&n);
1896		if (ret)
1897		    continue;
1898	    }
1899
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 database ACL");
1905	    return 0;
1906	}
1907    }
1908
1909    for (i = 0; i < principal_mappings.len; i++) {
1910	krb5_boolean b;
1911
1912	b = krb5_principal_compare(context,
1913				   client->entry.principal,
1914				   principal_mappings.val[i].principal);
1915	if (b == FALSE)
1916	    continue;
1917	if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1918	    continue;
1919
1920	hx509_name_free(&name);
1921	hx509_name_free(&issuer);
1922	hx509_name_free(&taname);
1923
1924	kdc_log(context, config, 5,
1925		"Found matching PK-INIT FILE ACL");
1926	return 0;
1927    }
1928
1929 fail:
1930    hx509_name_free(&name);
1931    hx509_name_free(&issuer);
1932    hx509_name_free(&taname);
1933
1934    ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1935    krb5_set_error_message(context, ret,
1936			  "PKINIT no matching principals for %s",
1937			  *subject_name);
1938
1939    kdc_log(context, config, 5,
1940	    "PKINIT no matching principals for %s",
1941	    *subject_name);
1942
1943    free(*subject_name);
1944    *subject_name = NULL;
1945
1946    return ret;
1947}
1948
1949static krb5_error_code
1950add_principal_mapping(krb5_context context,
1951		      const char *principal_name,
1952		      const char * subject)
1953{
1954   struct pk_allowed_princ *tmp;
1955   krb5_principal principal;
1956   krb5_error_code ret;
1957
1958   tmp = realloc(principal_mappings.val,
1959	         (principal_mappings.len + 1) * sizeof(*tmp));
1960   if (tmp == NULL)
1961       return ENOMEM;
1962   principal_mappings.val = tmp;
1963
1964   ret = krb5_parse_name(context, principal_name, &principal);
1965   if (ret)
1966       return ret;
1967
1968   principal_mappings.val[principal_mappings.len].principal = principal;
1969
1970   principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1971   if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1972       krb5_free_principal(context, principal);
1973       return ENOMEM;
1974   }
1975   principal_mappings.len++;
1976
1977   return 0;
1978}
1979
1980krb5_error_code
1981_kdc_add_inital_verified_cas(krb5_context context,
1982			     krb5_kdc_configuration *config,
1983			     pk_client_params *cp,
1984			     EncTicketPart *tkt)
1985{
1986    AD_INITIAL_VERIFIED_CAS cas;
1987    krb5_error_code ret;
1988    krb5_data data;
1989    size_t size = 0;
1990
1991    memset(&cas, 0, sizeof(cas));
1992
1993    /* XXX add CAs to cas here */
1994
1995    ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1996		       &cas, &size, ret);
1997    if (ret)
1998	return ret;
1999    if (data.length != size)
2000	krb5_abortx(context, "internal asn.1 encoder error");
2001
2002    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
2003				      KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
2004				      &data);
2005    krb5_data_free(&data);
2006    return ret;
2007}
2008
2009/*
2010 *
2011 */
2012
2013static void
2014load_mappings(krb5_context context, const char *fn)
2015{
2016    krb5_error_code ret;
2017    char buf[1024];
2018    unsigned long lineno = 0;
2019    FILE *f;
2020
2021    f = fopen(fn, "r");
2022    if (f == NULL)
2023	return;
2024
2025    while (fgets(buf, sizeof(buf), f) != NULL) {
2026	char *subject_name, *p;
2027
2028	buf[strcspn(buf, "\n")] = '\0';
2029	lineno++;
2030
2031	p = buf + strspn(buf, " \t");
2032
2033	if (*p == '#' || *p == '\0')
2034	    continue;
2035
2036	subject_name = strchr(p, ':');
2037	if (subject_name == NULL) {
2038	    krb5_warnx(context, "pkinit mapping file line %lu "
2039		       "missing \":\" :%s",
2040		       lineno, buf);
2041	    continue;
2042	}
2043	*subject_name++ = '\0';
2044
2045	ret = add_principal_mapping(context, p, subject_name);
2046	if (ret) {
2047	    krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
2048		      lineno, buf);
2049	    continue;
2050	}
2051    }
2052
2053    fclose(f);
2054}
2055
2056/*
2057 *
2058 */
2059
2060krb5_error_code
2061krb5_kdc_pk_initialize(krb5_context context,
2062		       krb5_kdc_configuration *config,
2063		       const char *user_id,
2064		       const char *anchors,
2065		       char **pool,
2066		       char **revoke_list)
2067{
2068    const char *file;
2069    char *fn = NULL;
2070    krb5_error_code ret;
2071
2072    file = krb5_config_get_string(context, NULL,
2073				  "libdefaults", "moduli", NULL);
2074
2075    ret = _krb5_parse_moduli(context, file, &moduli);
2076    if (ret)
2077	krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
2078
2079    principal_mappings.len = 0;
2080    principal_mappings.val = NULL;
2081
2082    ret = _krb5_pk_load_id(context,
2083			   &kdc_identity,
2084			   user_id,
2085			   anchors,
2086			   pool,
2087			   revoke_list,
2088			   NULL,
2089			   NULL,
2090			   NULL);
2091    if (ret) {
2092	krb5_warn(context, ret, "PKINIT: ");
2093	config->enable_pkinit = 0;
2094	return ret;
2095    }
2096
2097    {
2098	hx509_query *q;
2099	hx509_cert cert;
2100
2101	ret = hx509_query_alloc(context->hx509ctx, &q);
2102	if (ret) {
2103	    krb5_warnx(context, "PKINIT: out of memory");
2104	    return ENOMEM;
2105	}
2106
2107	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2108	if (config->pkinit_kdc_friendly_name)
2109	    hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2110
2111	ret = hx509_certs_find(context->hx509ctx,
2112			       kdc_identity->certs,
2113			       q,
2114			       &cert);
2115	hx509_query_free(context->hx509ctx, q);
2116	if (ret == 0) {
2117	    if (hx509_cert_check_eku(context->hx509ctx, cert,
2118				     &asn1_oid_id_apple_system_id, 0) == 0)
2119	    {
2120		/* AppleID BTMM cert, then is all fine */
2121	    } else if (hx509_cert_check_eku(context->hx509ctx, cert,
2122					    &asn1_oid_id_pkkdcekuoid, 0))
2123	    {
2124		hx509_name name;
2125		char *str;
2126		ret = hx509_cert_get_subject(cert, &name);
2127		if (ret == 0) {
2128		    hx509_name_to_string(name, &str);
2129		    krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2130			       "is missing the PK-INIT KDC EKU, this is bad for "
2131			       "interoperability.", str);
2132		    hx509_name_free(&name);
2133		    free(str);
2134		}
2135	    }
2136	    hx509_cert_free(cert);
2137	} else
2138	    krb5_warnx(context, "PKINIT: failed to find a signing "
2139		       "certifiate with a public key");
2140    }
2141
2142    if (krb5_config_get_bool_default(context,
2143				     NULL,
2144				     FALSE,
2145				     "kdc",
2146				     "pkinit_allow_proxy_certificate",
2147				     NULL))
2148	config->pkinit_allow_proxy_certs = 1;
2149
2150    file = krb5_config_get_string(context,
2151				  NULL,
2152				  "kdc",
2153				  "pkinit_mappings_file",
2154				  NULL);
2155    if (file == NULL) {
2156	asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2157	file = fn;
2158    }
2159
2160    load_mappings(context, file);
2161    if (fn)
2162	free(fn);
2163
2164    return 0;
2165}
2166
2167#endif /* PKINIT */
2168