pkinit.c revision 178825
1/*
2 * Copyright (c) 2003 - 2006 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "kdc_locl.h"
35
36RCSID("$Id: pkinit.c 22243 2007-12-08 23:39:30Z lha $");
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
48/* XXX copied from lib/krb5/pkinit.c */
49struct krb5_pk_identity {
50    hx509_context hx509ctx;
51    hx509_verify_ctx verify_ctx;
52    hx509_certs certs;
53    hx509_certs anchors;
54    hx509_certs certpool;
55    hx509_revoke_ctx revoke;
56};
57
58enum pkinit_type {
59    PKINIT_COMPAT_WIN2K = 1,
60    PKINIT_COMPAT_27 = 3
61};
62
63struct pk_client_params {
64    enum pkinit_type type;
65    BIGNUM *dh_public_key;
66    hx509_cert cert;
67    unsigned nonce;
68    DH *dh;
69    EncryptionKey reply_key;
70    char *dh_group_name;
71    hx509_peer_info peer;
72    hx509_certs client_anchors;
73};
74
75struct pk_principal_mapping {
76    unsigned int len;
77    struct pk_allowed_princ {
78	krb5_principal principal;
79	char *subject;
80    } *val;
81};
82
83static struct krb5_pk_identity *kdc_identity;
84static struct pk_principal_mapping principal_mappings;
85static struct krb5_dh_moduli **moduli;
86
87static struct {
88    krb5_data data;
89    time_t expire;
90    time_t next_update;
91} ocsp;
92
93/*
94 *
95 */
96
97static krb5_error_code
98pk_check_pkauthenticator_win2k(krb5_context context,
99			       PKAuthenticator_Win2k *a,
100			       const KDC_REQ *req)
101{
102    krb5_timestamp now;
103
104    krb5_timeofday (context, &now);
105
106    /* XXX cusec */
107    if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
108	krb5_clear_error_string(context);
109	return KRB5KRB_AP_ERR_SKEW;
110    }
111    return 0;
112}
113
114static krb5_error_code
115pk_check_pkauthenticator(krb5_context context,
116			 PKAuthenticator *a,
117			 const KDC_REQ *req)
118{
119    u_char *buf = NULL;
120    size_t buf_size;
121    krb5_error_code ret;
122    size_t len;
123    krb5_timestamp now;
124    Checksum checksum;
125
126    krb5_timeofday (context, &now);
127
128    /* XXX cusec */
129    if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
130	krb5_clear_error_string(context);
131	return KRB5KRB_AP_ERR_SKEW;
132    }
133
134    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
135    if (ret) {
136	krb5_clear_error_string(context);
137	return ret;
138    }
139    if (buf_size != len)
140	krb5_abortx(context, "Internal error in ASN.1 encoder");
141
142    ret = krb5_create_checksum(context,
143			       NULL,
144			       0,
145			       CKSUMTYPE_SHA1,
146			       buf,
147			       len,
148			       &checksum);
149    free(buf);
150    if (ret) {
151	krb5_clear_error_string(context);
152	return ret;
153    }
154
155    if (a->paChecksum == NULL) {
156	krb5_clear_error_string(context);
157	ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
158	goto out;
159    }
160
161    if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
162	krb5_clear_error_string(context);
163	ret = KRB5KRB_ERR_GENERIC;
164    }
165
166out:
167    free_Checksum(&checksum);
168
169    return ret;
170}
171
172void
173_kdc_pk_free_client_param(krb5_context context,
174			  pk_client_params *client_params)
175{
176    if (client_params->cert)
177	hx509_cert_free(client_params->cert);
178    if (client_params->dh)
179	DH_free(client_params->dh);
180    if (client_params->dh_public_key)
181	BN_free(client_params->dh_public_key);
182    krb5_free_keyblock_contents(context, &client_params->reply_key);
183    if (client_params->dh_group_name)
184	free(client_params->dh_group_name);
185    if (client_params->peer)
186	hx509_peer_info_free(client_params->peer);
187    if (client_params->client_anchors)
188	hx509_certs_free(&client_params->client_anchors);
189    memset(client_params, 0, sizeof(*client_params));
190    free(client_params);
191}
192
193static krb5_error_code
194generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
195                     krb5_enctype enctype, krb5_keyblock *reply_key)
196{
197    unsigned char *dh_gen_key = NULL;
198    krb5_keyblock key;
199    krb5_error_code ret;
200    size_t dh_gen_keylen, size;
201
202    memset(&key, 0, sizeof(key));
203
204    if (!DH_generate_key(client_params->dh)) {
205	krb5_set_error_string(context, "Can't generate Diffie-Hellman keys");
206	ret = KRB5KRB_ERR_GENERIC;
207	goto out;
208    }
209    if (client_params->dh_public_key == NULL) {
210	krb5_set_error_string(context, "dh_public_key");
211	ret = KRB5KRB_ERR_GENERIC;
212	goto out;
213    }
214
215    dh_gen_keylen = DH_size(client_params->dh);
216    size = BN_num_bytes(client_params->dh->p);
217    if (size < dh_gen_keylen)
218	size = dh_gen_keylen;
219
220    dh_gen_key = malloc(size);
221    if (dh_gen_key == NULL) {
222	krb5_set_error_string(context, "malloc: out of memory");
223	ret = ENOMEM;
224	goto out;
225    }
226    memset(dh_gen_key, 0, size - dh_gen_keylen);
227
228    dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
229				   client_params->dh_public_key,
230				   client_params->dh);
231    if (dh_gen_keylen == -1) {
232	krb5_set_error_string(context, "Can't compute Diffie-Hellman key");
233	ret = KRB5KRB_ERR_GENERIC;
234	goto out;
235    }
236
237    ret = _krb5_pk_octetstring2key(context,
238				   enctype,
239				   dh_gen_key, dh_gen_keylen,
240				   NULL, NULL,
241				   reply_key);
242
243 out:
244    if (dh_gen_key)
245	free(dh_gen_key);
246    if (key.keyvalue.data)
247	krb5_free_keyblock_contents(context, &key);
248
249    return ret;
250}
251
252static BIGNUM *
253integer_to_BN(krb5_context context, const char *field, heim_integer *f)
254{
255    BIGNUM *bn;
256
257    bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
258    if (bn == NULL) {
259	krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
260	return NULL;
261    }
262    BN_set_negative(bn, f->negative);
263    return bn;
264}
265
266static krb5_error_code
267get_dh_param(krb5_context context,
268	     krb5_kdc_configuration *config,
269	     SubjectPublicKeyInfo *dh_key_info,
270	     pk_client_params *client_params)
271{
272    DomainParameters dhparam;
273    DH *dh = NULL;
274    krb5_error_code ret;
275
276    memset(&dhparam, 0, sizeof(dhparam));
277
278    if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
279	krb5_set_error_string(context,
280			      "PKINIT invalid oid in clientPublicValue");
281	return KRB5_BADMSGTYPE;
282    }
283
284    if (dh_key_info->algorithm.parameters == NULL) {
285	krb5_set_error_string(context, "PKINIT missing algorithm parameter "
286			      "in clientPublicValue");
287	return KRB5_BADMSGTYPE;
288    }
289
290    ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
291				  dh_key_info->algorithm.parameters->length,
292				  &dhparam,
293				  NULL);
294    if (ret) {
295	krb5_set_error_string(context, "Can't decode algorithm "
296			      "parameters in clientPublicValue");
297	goto out;
298    }
299
300    if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
301	ret = KRB5_BADMSGTYPE;
302	krb5_set_error_string(context, "PKINIT: subjectPublicKey not aligned "
303			      "to 8 bit boundary");
304	goto out;
305    }
306
307
308    ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
309			    &dhparam.p, &dhparam.g, &dhparam.q, moduli,
310			    &client_params->dh_group_name);
311    if (ret) {
312	/* XXX send back proposal of better group */
313	goto out;
314    }
315
316    dh = DH_new();
317    if (dh == NULL) {
318	krb5_set_error_string(context, "Cannot create DH structure");
319	ret = ENOMEM;
320	goto out;
321    }
322    ret = KRB5_BADMSGTYPE;
323    dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
324    if (dh->p == NULL)
325	goto out;
326    dh->g = integer_to_BN(context, "DH base", &dhparam.g);
327    if (dh->g == NULL)
328	goto out;
329    dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
330    if (dh->g == NULL)
331	goto out;
332
333    {
334	heim_integer glue;
335	size_t size;
336
337	ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
338				 dh_key_info->subjectPublicKey.length / 8,
339				 &glue,
340				 &size);
341	if (ret) {
342	    krb5_clear_error_string(context);
343	    return ret;
344	}
345
346	client_params->dh_public_key = integer_to_BN(context,
347						     "subjectPublicKey",
348						     &glue);
349	der_free_heim_integer(&glue);
350	if (client_params->dh_public_key == NULL)
351	    goto out;
352    }
353
354    client_params->dh = dh;
355    dh = NULL;
356    ret = 0;
357
358 out:
359    if (dh)
360	DH_free(dh);
361    free_DomainParameters(&dhparam);
362    return ret;
363}
364
365krb5_error_code
366_kdc_pk_rd_padata(krb5_context context,
367		  krb5_kdc_configuration *config,
368		  const KDC_REQ *req,
369		  const PA_DATA *pa,
370		  pk_client_params **ret_params)
371{
372    pk_client_params *client_params;
373    krb5_error_code ret;
374    heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
375    krb5_data eContent = { 0, NULL };
376    krb5_data signed_content = { 0, NULL };
377    const char *type = "unknown type";
378    int have_data = 0;
379
380    *ret_params = NULL;
381
382    if (!config->enable_pkinit) {
383	kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
384	krb5_clear_error_string(context);
385	return 0;
386    }
387
388    hx509_verify_set_time(kdc_identity->verify_ctx, _kdc_now.tv_sec);
389
390    client_params = calloc(1, sizeof(*client_params));
391    if (client_params == NULL) {
392	krb5_clear_error_string(context);
393	ret = ENOMEM;
394	goto out;
395    }
396
397    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
398	PA_PK_AS_REQ_Win2k r;
399
400	type = "PK-INIT-Win2k";
401
402	ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
403					pa->padata_value.length,
404					&r,
405					NULL);
406	if (ret) {
407	    krb5_set_error_string(context, "Can't decode "
408				  "PK-AS-REQ-Win2k: %d", ret);
409	    goto out;
410	}
411
412	ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
413					   &contentInfoOid,
414					   &signed_content,
415					   &have_data);
416	free_PA_PK_AS_REQ_Win2k(&r);
417	if (ret) {
418	    krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
419	    goto out;
420	}
421
422    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
423	PA_PK_AS_REQ r;
424
425	type = "PK-INIT-IETF";
426
427	ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
428				  pa->padata_value.length,
429				  &r,
430				  NULL);
431	if (ret) {
432	    krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
433	    goto out;
434	}
435
436	/* XXX look at r.kdcPkId */
437	if (r.trustedCertifiers) {
438	    ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
439	    unsigned int i;
440
441	    ret = hx509_certs_init(kdc_identity->hx509ctx,
442				   "MEMORY:client-anchors",
443				   0, NULL,
444				   &client_params->client_anchors);
445	    if (ret) {
446		krb5_set_error_string(context, "Can't allocate client anchors: %d", ret);
447		goto out;
448
449	    }
450	    for (i = 0; i < edi->len; i++) {
451		IssuerAndSerialNumber iasn;
452		hx509_query *q;
453		hx509_cert cert;
454		size_t size;
455
456		if (edi->val[i].issuerAndSerialNumber == NULL)
457		    continue;
458
459		ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
460		if (ret) {
461		    krb5_set_error_string(context,
462					  "Failed to allocate hx509_query");
463		    goto out;
464		}
465
466		ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
467						   edi->val[i].issuerAndSerialNumber->length,
468						   &iasn,
469						   &size);
470		if (ret) {
471		    hx509_query_free(kdc_identity->hx509ctx, q);
472		    continue;
473		}
474		ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
475		free_IssuerAndSerialNumber(&iasn);
476		if (ret)
477		    continue;
478
479		ret = hx509_certs_find(kdc_identity->hx509ctx,
480				       kdc_identity->certs,
481				       q,
482				       &cert);
483		hx509_query_free(kdc_identity->hx509ctx, q);
484		if (ret)
485		    continue;
486		hx509_certs_add(kdc_identity->hx509ctx,
487				client_params->client_anchors, cert);
488		hx509_cert_free(cert);
489	    }
490	}
491
492	ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
493					   &contentInfoOid,
494					   &signed_content,
495					   &have_data);
496	free_PA_PK_AS_REQ(&r);
497	if (ret) {
498	    krb5_set_error_string(context, "Can't unwrap ContentInfo: %d", ret);
499	    goto out;
500	}
501
502    } else {
503	krb5_clear_error_string(context);
504	ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
505	goto out;
506    }
507
508    ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
509    if (ret != 0) {
510	krb5_set_error_string(context, "PK-AS-REQ-Win2k invalid content "
511			      "type oid");
512	ret = KRB5KRB_ERR_GENERIC;
513	goto out;
514    }
515
516    if (!have_data) {
517	krb5_set_error_string(context,
518			      "PK-AS-REQ-Win2k no signed auth pack");
519	ret = KRB5KRB_ERR_GENERIC;
520	goto out;
521    }
522
523    {
524	hx509_certs signer_certs;
525
526	ret = hx509_cms_verify_signed(kdc_identity->hx509ctx,
527				      kdc_identity->verify_ctx,
528				      signed_content.data,
529				      signed_content.length,
530				      NULL,
531				      kdc_identity->certpool,
532				      &eContentType,
533				      &eContent,
534				      &signer_certs);
535	if (ret) {
536	    char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret);
537	    krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
538		       s, ret);
539	    free(s);
540	    goto out;
541	}
542
543	ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs,
544				 &client_params->cert);
545	hx509_certs_free(&signer_certs);
546	if (ret)
547	    goto out;
548    }
549
550    /* Signature is correct, now verify the signed message */
551    if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 &&
552	der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0)
553    {
554	krb5_set_error_string(context, "got wrong oid for pkauthdata");
555	ret = KRB5_BADMSGTYPE;
556	goto out;
557    }
558
559    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
560	AuthPack_Win2k ap;
561
562	ret = decode_AuthPack_Win2k(eContent.data,
563				    eContent.length,
564				    &ap,
565				    NULL);
566	if (ret) {
567	    krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
568	    goto out;
569	}
570
571	ret = pk_check_pkauthenticator_win2k(context,
572					     &ap.pkAuthenticator,
573					     req);
574	if (ret) {
575	    free_AuthPack_Win2k(&ap);
576	    goto out;
577	}
578
579	client_params->type = PKINIT_COMPAT_WIN2K;
580	client_params->nonce = ap.pkAuthenticator.nonce;
581
582	if (ap.clientPublicValue) {
583	    krb5_set_error_string(context, "DH not supported for windows");
584	    ret = KRB5KRB_ERR_GENERIC;
585	    goto out;
586	}
587	free_AuthPack_Win2k(&ap);
588
589    } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
590	AuthPack ap;
591
592	ret = decode_AuthPack(eContent.data,
593			      eContent.length,
594			      &ap,
595			      NULL);
596	if (ret) {
597	    krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
598	    free_AuthPack(&ap);
599	    goto out;
600	}
601
602	ret = pk_check_pkauthenticator(context,
603				       &ap.pkAuthenticator,
604				       req);
605	if (ret) {
606	    free_AuthPack(&ap);
607	    goto out;
608	}
609
610	client_params->type = PKINIT_COMPAT_27;
611	client_params->nonce = ap.pkAuthenticator.nonce;
612
613	if (ap.clientPublicValue) {
614	    ret = get_dh_param(context, config,
615			       ap.clientPublicValue, client_params);
616	    if (ret) {
617		free_AuthPack(&ap);
618		goto out;
619	    }
620	}
621
622	if (ap.supportedCMSTypes) {
623	    ret = hx509_peer_info_alloc(kdc_identity->hx509ctx,
624					&client_params->peer);
625	    if (ret) {
626		free_AuthPack(&ap);
627		goto out;
628	    }
629	    ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx,
630					       client_params->peer,
631					       ap.supportedCMSTypes->val,
632					       ap.supportedCMSTypes->len);
633	    if (ret) {
634		free_AuthPack(&ap);
635		goto out;
636	    }
637	}
638	free_AuthPack(&ap);
639    } else
640	krb5_abortx(context, "internal pkinit error");
641
642    kdc_log(context, config, 0, "PK-INIT request of type %s", type);
643
644out:
645    if (ret)
646	krb5_warn(context, ret, "PKINIT");
647
648    if (signed_content.data)
649	free(signed_content.data);
650    krb5_data_free(&eContent);
651    der_free_oid(&eContentType);
652    der_free_oid(&contentInfoOid);
653    if (ret)
654	_kdc_pk_free_client_param(context, client_params);
655    else
656	*ret_params = client_params;
657    return ret;
658}
659
660/*
661 *
662 */
663
664static krb5_error_code
665BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
666{
667    integer->length = BN_num_bytes(bn);
668    integer->data = malloc(integer->length);
669    if (integer->data == NULL) {
670	krb5_clear_error_string(context);
671	return ENOMEM;
672    }
673    BN_bn2bin(bn, integer->data);
674    integer->negative = BN_is_negative(bn);
675    return 0;
676}
677
678static krb5_error_code
679pk_mk_pa_reply_enckey(krb5_context context,
680		      krb5_kdc_configuration *config,
681		      pk_client_params *client_params,
682		      const KDC_REQ *req,
683		      const krb5_data *req_buffer,
684		      krb5_keyblock *reply_key,
685		      ContentInfo *content_info)
686{
687    const heim_oid *envelopedAlg = NULL, *sdAlg = NULL;
688    krb5_error_code ret;
689    krb5_data buf, signed_data;
690    size_t size;
691    int do_win2k = 0;
692
693    krb5_data_zero(&buf);
694    krb5_data_zero(&signed_data);
695
696    /*
697     * If the message client is a win2k-type but it send pa data
698     * 09-binding it expects a IETF (checksum) reply so there can be
699     * no replay attacks.
700     */
701
702    switch (client_params->type) {
703    case PKINIT_COMPAT_WIN2K: {
704	int i = 0;
705	if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
706	    && config->pkinit_require_binding == 0)
707	{
708	    do_win2k = 1;
709	}
710	break;
711    }
712    case PKINIT_COMPAT_27:
713	break;
714    default:
715	krb5_abortx(context, "internal pkinit error");
716    }
717
718    if (do_win2k) {
719	ReplyKeyPack_Win2k kp;
720	memset(&kp, 0, sizeof(kp));
721
722	envelopedAlg = oid_id_rsadsi_des_ede3_cbc();
723	sdAlg = oid_id_pkcs7_data();
724
725	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
726	if (ret) {
727	    krb5_clear_error_string(context);
728	    goto out;
729	}
730	kp.nonce = client_params->nonce;
731
732	ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
733			   buf.data, buf.length,
734			   &kp, &size,ret);
735	free_ReplyKeyPack_Win2k(&kp);
736    } else {
737	krb5_crypto ascrypto;
738	ReplyKeyPack kp;
739	memset(&kp, 0, sizeof(kp));
740
741	sdAlg = oid_id_pkrkeydata();
742
743	ret = copy_EncryptionKey(reply_key, &kp.replyKey);
744	if (ret) {
745	    krb5_clear_error_string(context);
746	    goto out;
747	}
748
749	ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
750	if (ret) {
751	    krb5_clear_error_string(context);
752	    goto out;
753	}
754
755	ret = krb5_create_checksum(context, ascrypto, 6, 0,
756				   req_buffer->data, req_buffer->length,
757				   &kp.asChecksum);
758	if (ret) {
759	    krb5_clear_error_string(context);
760	    goto out;
761	}
762
763	ret = krb5_crypto_destroy(context, ascrypto);
764	if (ret) {
765	    krb5_clear_error_string(context);
766	    goto out;
767	}
768	ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
769	free_ReplyKeyPack(&kp);
770    }
771    if (ret) {
772	krb5_set_error_string(context, "ASN.1 encoding of ReplyKeyPack "
773			      "failed (%d)", ret);
774	goto out;
775    }
776    if (buf.length != size)
777	krb5_abortx(context, "Internal ASN.1 encoder error");
778
779    {
780	hx509_query *q;
781	hx509_cert cert;
782
783	ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
784	if (ret)
785	    goto out;
786
787	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
788	hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
789
790	ret = hx509_certs_find(kdc_identity->hx509ctx,
791			       kdc_identity->certs,
792			       q,
793			       &cert);
794	hx509_query_free(kdc_identity->hx509ctx, q);
795	if (ret)
796	    goto out;
797
798	ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
799					0,
800					sdAlg,
801					buf.data,
802					buf.length,
803					NULL,
804					cert,
805					client_params->peer,
806					client_params->client_anchors,
807					kdc_identity->certpool,
808					&signed_data);
809	hx509_cert_free(cert);
810    }
811
812    krb5_data_free(&buf);
813    if (ret)
814	goto out;
815
816    if (client_params->type == PKINIT_COMPAT_WIN2K) {
817	ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
818					 &signed_data,
819					 &buf);
820	if (ret)
821	    goto out;
822	krb5_data_free(&signed_data);
823	signed_data = buf;
824    }
825
826    ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
827			       0,
828			       client_params->cert,
829			       signed_data.data, signed_data.length,
830			       envelopedAlg,
831			       oid_id_pkcs7_signedData(), &buf);
832    if (ret)
833	goto out;
834
835    ret = _krb5_pk_mk_ContentInfo(context,
836				  &buf,
837				  oid_id_pkcs7_envelopedData(),
838				  content_info);
839out:
840    krb5_data_free(&buf);
841    krb5_data_free(&signed_data);
842    return ret;
843}
844
845/*
846 *
847 */
848
849static krb5_error_code
850pk_mk_pa_reply_dh(krb5_context context,
851                  DH *kdc_dh,
852      		  pk_client_params *client_params,
853                  krb5_keyblock *reply_key,
854		  ContentInfo *content_info,
855		  hx509_cert *kdc_cert)
856{
857    KDCDHKeyInfo dh_info;
858    krb5_data signed_data, buf;
859    ContentInfo contentinfo;
860    krb5_error_code ret;
861    size_t size;
862    heim_integer i;
863
864    memset(&contentinfo, 0, sizeof(contentinfo));
865    memset(&dh_info, 0, sizeof(dh_info));
866    krb5_data_zero(&buf);
867    krb5_data_zero(&signed_data);
868
869    *kdc_cert = NULL;
870
871    ret = BN_to_integer(context, kdc_dh->pub_key, &i);
872    if (ret)
873	return ret;
874
875    ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
876    if (ret) {
877	krb5_set_error_string(context, "ASN.1 encoding of "
878			      "DHPublicKey failed (%d)", ret);
879	krb5_clear_error_string(context);
880	return ret;
881    }
882    if (buf.length != size)
883	krb5_abortx(context, "Internal ASN.1 encoder error");
884
885    dh_info.subjectPublicKey.length = buf.length * 8;
886    dh_info.subjectPublicKey.data = buf.data;
887
888    dh_info.nonce = client_params->nonce;
889
890    ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
891		       ret);
892    if (ret) {
893	krb5_set_error_string(context, "ASN.1 encoding of "
894			      "KdcDHKeyInfo failed (%d)", ret);
895	goto out;
896    }
897    if (buf.length != size)
898	krb5_abortx(context, "Internal ASN.1 encoder error");
899
900    /*
901     * Create the SignedData structure and sign the KdcDHKeyInfo
902     * filled in above
903     */
904
905    {
906	hx509_query *q;
907	hx509_cert cert;
908
909	ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
910	if (ret)
911	    goto out;
912
913	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
914	hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
915
916	ret = hx509_certs_find(kdc_identity->hx509ctx,
917			       kdc_identity->certs,
918			       q,
919			       &cert);
920	hx509_query_free(kdc_identity->hx509ctx, q);
921	if (ret)
922	    goto out;
923
924	ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
925					0,
926					oid_id_pkdhkeydata(),
927					buf.data,
928					buf.length,
929					NULL,
930					cert,
931					client_params->peer,
932					client_params->client_anchors,
933					kdc_identity->certpool,
934					&signed_data);
935	*kdc_cert = cert;
936    }
937    if (ret)
938	goto out;
939
940    ret = _krb5_pk_mk_ContentInfo(context,
941				  &signed_data,
942				  oid_id_pkcs7_signedData(),
943				  content_info);
944    if (ret)
945	goto out;
946
947 out:
948    if (ret && *kdc_cert) {
949	hx509_cert_free(*kdc_cert);
950	*kdc_cert = NULL;
951    }
952
953    krb5_data_free(&buf);
954    krb5_data_free(&signed_data);
955    free_KDCDHKeyInfo(&dh_info);
956
957    return ret;
958}
959
960/*
961 *
962 */
963
964krb5_error_code
965_kdc_pk_mk_pa_reply(krb5_context context,
966		    krb5_kdc_configuration *config,
967		    pk_client_params *client_params,
968		    const hdb_entry_ex *client,
969		    const KDC_REQ *req,
970		    const krb5_data *req_buffer,
971		    krb5_keyblock **reply_key,
972		    METHOD_DATA *md)
973{
974    krb5_error_code ret;
975    void *buf;
976    size_t len, size;
977    krb5_enctype enctype;
978    int pa_type;
979    hx509_cert kdc_cert = NULL;
980    int i;
981
982    if (!config->enable_pkinit) {
983	krb5_clear_error_string(context);
984	return 0;
985    }
986
987    if (req->req_body.etype.len > 0) {
988	for (i = 0; i < req->req_body.etype.len; i++)
989	    if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
990		break;
991	if (req->req_body.etype.len <= i) {
992	    ret = KRB5KRB_ERR_GENERIC;
993	    krb5_set_error_string(context,
994				  "No valid enctype available from client");
995	    goto out;
996	}
997	enctype = req->req_body.etype.val[i];
998    } else
999	enctype = ETYPE_DES3_CBC_SHA1;
1000
1001    if (client_params->type == PKINIT_COMPAT_27) {
1002	PA_PK_AS_REP rep;
1003	const char *type, *other = "";
1004
1005	memset(&rep, 0, sizeof(rep));
1006
1007	pa_type = KRB5_PADATA_PK_AS_REP;
1008
1009	if (client_params->dh == NULL) {
1010	    ContentInfo info;
1011
1012	    type = "enckey";
1013
1014	    rep.element = choice_PA_PK_AS_REP_encKeyPack;
1015
1016	    ret = krb5_generate_random_keyblock(context, enctype,
1017						&client_params->reply_key);
1018	    if (ret) {
1019		free_PA_PK_AS_REP(&rep);
1020		goto out;
1021	    }
1022	    ret = pk_mk_pa_reply_enckey(context,
1023					config,
1024					client_params,
1025					req,
1026					req_buffer,
1027					&client_params->reply_key,
1028					&info);
1029	    if (ret) {
1030		free_PA_PK_AS_REP(&rep);
1031		goto out;
1032	    }
1033	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1034			       rep.u.encKeyPack.length, &info, &size,
1035			       ret);
1036	    free_ContentInfo(&info);
1037	    if (ret) {
1038		krb5_set_error_string(context, "encoding of Key ContentInfo "
1039				      "failed %d", ret);
1040		free_PA_PK_AS_REP(&rep);
1041		goto out;
1042	    }
1043	    if (rep.u.encKeyPack.length != size)
1044		krb5_abortx(context, "Internal ASN.1 encoder error");
1045
1046	} else {
1047	    ContentInfo info;
1048
1049	    type = "dh";
1050	    if (client_params->dh_group_name)
1051		other = client_params->dh_group_name;
1052
1053	    rep.element = choice_PA_PK_AS_REP_dhInfo;
1054
1055	    ret = generate_dh_keyblock(context, client_params, enctype,
1056				       &client_params->reply_key);
1057	    if (ret)
1058		return ret;
1059
1060	    ret = pk_mk_pa_reply_dh(context, client_params->dh,
1061				    client_params,
1062				    &client_params->reply_key,
1063				    &info,
1064				    &kdc_cert);
1065
1066	    ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1067			       rep.u.dhInfo.dhSignedData.length, &info, &size,
1068			       ret);
1069	    free_ContentInfo(&info);
1070	    if (ret) {
1071		krb5_set_error_string(context, "encoding of Key ContentInfo "
1072				      "failed %d", ret);
1073		free_PA_PK_AS_REP(&rep);
1074		goto out;
1075	    }
1076	    if (rep.u.encKeyPack.length != size)
1077		krb5_abortx(context, "Internal ASN.1 encoder error");
1078
1079	}
1080	if (ret) {
1081	    free_PA_PK_AS_REP(&rep);
1082	    goto out;
1083	}
1084
1085	ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1086	free_PA_PK_AS_REP(&rep);
1087	if (ret) {
1088	    krb5_set_error_string(context, "encode PA-PK-AS-REP failed %d",
1089				  ret);
1090	    goto out;
1091	}
1092	if (len != size)
1093	    krb5_abortx(context, "Internal ASN.1 encoder error");
1094
1095	kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1096
1097    } else if (client_params->type == PKINIT_COMPAT_WIN2K) {
1098	PA_PK_AS_REP_Win2k rep;
1099	ContentInfo info;
1100
1101	if (client_params->dh) {
1102	    krb5_set_error_string(context, "Windows PK-INIT doesn't support DH");
1103	    ret = KRB5KRB_ERR_GENERIC;
1104	    goto out;
1105	}
1106
1107	memset(&rep, 0, sizeof(rep));
1108
1109	pa_type = KRB5_PADATA_PK_AS_REP_19;
1110	rep.element = choice_PA_PK_AS_REP_encKeyPack;
1111
1112	ret = krb5_generate_random_keyblock(context, enctype,
1113					    &client_params->reply_key);
1114	if (ret) {
1115	    free_PA_PK_AS_REP_Win2k(&rep);
1116	    goto out;
1117	}
1118	ret = pk_mk_pa_reply_enckey(context,
1119				    config,
1120				    client_params,
1121				    req,
1122				    req_buffer,
1123				    &client_params->reply_key,
1124				    &info);
1125	if (ret) {
1126	    free_PA_PK_AS_REP_Win2k(&rep);
1127	    goto out;
1128	}
1129	ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1130			   rep.u.encKeyPack.length, &info, &size,
1131			   ret);
1132	free_ContentInfo(&info);
1133	if (ret) {
1134	    krb5_set_error_string(context, "encoding of Key ContentInfo "
1135				  "failed %d", ret);
1136	    free_PA_PK_AS_REP_Win2k(&rep);
1137	    goto out;
1138	}
1139	if (rep.u.encKeyPack.length != size)
1140	    krb5_abortx(context, "Internal ASN.1 encoder error");
1141
1142	ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1143	free_PA_PK_AS_REP_Win2k(&rep);
1144	if (ret) {
1145	    krb5_set_error_string(context,
1146				  "encode PA-PK-AS-REP-Win2k failed %d", ret);
1147	    goto out;
1148	}
1149	if (len != size)
1150	    krb5_abortx(context, "Internal ASN.1 encoder error");
1151
1152    } else
1153	krb5_abortx(context, "PK-INIT internal error");
1154
1155
1156    ret = krb5_padata_add(context, md, pa_type, buf, len);
1157    if (ret) {
1158	krb5_set_error_string(context, "failed adding PA-PK-AS-REP %d", ret);
1159	free(buf);
1160	goto out;
1161    }
1162
1163    if (config->pkinit_kdc_ocsp_file) {
1164
1165	if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1166	    struct stat sb;
1167	    int fd;
1168
1169	    krb5_data_free(&ocsp.data);
1170
1171	    ocsp.expire = 0;
1172	    ocsp.next_update = kdc_time + 60 * 5;
1173
1174	    fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1175	    if (fd < 0) {
1176		kdc_log(context, config, 0,
1177			"PK-INIT failed to open ocsp data file %d", errno);
1178		goto out_ocsp;
1179	    }
1180	    ret = fstat(fd, &sb);
1181	    if (ret) {
1182		ret = errno;
1183		close(fd);
1184		kdc_log(context, config, 0,
1185			"PK-INIT failed to stat ocsp data %d", ret);
1186		goto out_ocsp;
1187	    }
1188
1189	    ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1190	    if (ret) {
1191		close(fd);
1192		kdc_log(context, config, 0,
1193			"PK-INIT failed to stat ocsp data %d", ret);
1194		goto out_ocsp;
1195	    }
1196	    ocsp.data.length = sb.st_size;
1197	    ret = read(fd, ocsp.data.data, sb.st_size);
1198	    close(fd);
1199	    if (ret != sb.st_size) {
1200		kdc_log(context, config, 0,
1201			"PK-INIT failed to read ocsp data %d", errno);
1202		goto out_ocsp;
1203	    }
1204
1205	    ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
1206				    kdc_time,
1207				    kdc_cert,
1208				    0,
1209				    ocsp.data.data, ocsp.data.length,
1210				    &ocsp.expire);
1211	    if (ret) {
1212		kdc_log(context, config, 0,
1213			"PK-INIT failed to verify ocsp data %d", ret);
1214		krb5_data_free(&ocsp.data);
1215		ocsp.expire = 0;
1216	    } else if (ocsp.expire > 180) {
1217		ocsp.expire -= 180; /* refetch the ocsp before it expire */
1218		ocsp.next_update = ocsp.expire;
1219	    } else {
1220		ocsp.next_update = kdc_time;
1221	    }
1222	out_ocsp:
1223	    ret = 0;
1224	}
1225
1226	if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1227
1228	    ret = krb5_padata_add(context, md,
1229				  KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1230				  ocsp.data.data, ocsp.data.length);
1231	    if (ret) {
1232		krb5_set_error_string(context,
1233				      "Failed adding OCSP response %d", ret);
1234		goto out;
1235	    }
1236	}
1237    }
1238
1239out:
1240    if (kdc_cert)
1241	hx509_cert_free(kdc_cert);
1242
1243    if (ret == 0)
1244	*reply_key = &client_params->reply_key;
1245    return ret;
1246}
1247
1248static int
1249match_rfc_san(krb5_context context,
1250	      krb5_kdc_configuration *config,
1251	      hx509_context hx509ctx,
1252	      hx509_cert client_cert,
1253	      krb5_const_principal match)
1254{
1255    hx509_octet_string_list list;
1256    int ret, i, found = 0;
1257
1258    memset(&list, 0 , sizeof(list));
1259
1260    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1261						   client_cert,
1262						   oid_id_pkinit_san(),
1263						   &list);
1264    if (ret)
1265	goto out;
1266
1267    for (i = 0; !found && i < list.len; i++) {
1268	krb5_principal_data principal;
1269	KRB5PrincipalName kn;
1270	size_t size;
1271
1272	ret = decode_KRB5PrincipalName(list.val[i].data,
1273				       list.val[i].length,
1274				       &kn, &size);
1275	if (ret) {
1276	    kdc_log(context, config, 0,
1277		    "Decoding kerberos name in certificate failed: %s",
1278		    krb5_get_err_text(context, ret));
1279	    break;
1280	}
1281	if (size != list.val[i].length) {
1282	    kdc_log(context, config, 0,
1283		    "Decoding kerberos name have extra bits on the end");
1284	    return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1285	}
1286
1287	principal.name = kn.principalName;
1288	principal.realm = kn.realm;
1289
1290	if (krb5_principal_compare(context, &principal, match) == TRUE)
1291	    found = 1;
1292	free_KRB5PrincipalName(&kn);
1293    }
1294
1295out:
1296    hx509_free_octet_string_list(&list);
1297    if (ret)
1298	return ret;
1299
1300    if (!found)
1301	return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1302
1303    return 0;
1304}
1305
1306static int
1307match_ms_upn_san(krb5_context context,
1308		 krb5_kdc_configuration *config,
1309		 hx509_context hx509ctx,
1310		 hx509_cert client_cert,
1311		 krb5_const_principal match)
1312{
1313    hx509_octet_string_list list;
1314    krb5_principal principal = NULL;
1315    int ret, found = 0;
1316    MS_UPN_SAN upn;
1317    size_t size;
1318
1319    memset(&list, 0 , sizeof(list));
1320
1321    ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1322						   client_cert,
1323						   oid_id_pkinit_ms_san(),
1324						   &list);
1325    if (ret)
1326	goto out;
1327
1328    if (list.len != 1) {
1329	kdc_log(context, config, 0,
1330		"More then one PK-INIT MS UPN SAN");
1331	goto out;
1332    }
1333
1334    ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1335    if (ret) {
1336	kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1337	goto out;
1338    }
1339
1340    kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1341
1342    ret = krb5_parse_name(context, upn, &principal);
1343    free_MS_UPN_SAN(&upn);
1344    if (ret) {
1345	kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1346	goto out;
1347    }
1348
1349    /*
1350     * This is very wrong, but will do for now, should really and a
1351     * plugin to the windc layer to very this ACL.
1352    */
1353    strupr(principal->realm);
1354
1355    if (krb5_principal_compare(context, principal, match) == TRUE)
1356	found = 1;
1357
1358out:
1359    if (principal)
1360	krb5_free_principal(context, principal);
1361    hx509_free_octet_string_list(&list);
1362    if (ret)
1363	return ret;
1364
1365    if (!found)
1366	return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1367
1368    return 0;
1369}
1370
1371krb5_error_code
1372_kdc_pk_check_client(krb5_context context,
1373		     krb5_kdc_configuration *config,
1374		     const hdb_entry_ex *client,
1375		     pk_client_params *client_params,
1376		     char **subject_name)
1377{
1378    const HDB_Ext_PKINIT_acl *acl;
1379    krb5_error_code ret;
1380    hx509_name name;
1381    int i;
1382
1383    ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
1384				      client_params->cert,
1385				      &name);
1386    if (ret)
1387	return ret;
1388
1389    ret = hx509_name_to_string(name, subject_name);
1390    hx509_name_free(&name);
1391    if (ret)
1392	return ret;
1393
1394    kdc_log(context, config, 0,
1395	    "Trying to authorize PK-INIT subject DN %s",
1396	    *subject_name);
1397
1398    if (config->pkinit_princ_in_cert) {
1399	ret = match_rfc_san(context, config,
1400			    kdc_identity->hx509ctx,
1401			    client_params->cert,
1402			    client->entry.principal);
1403	if (ret == 0) {
1404	    kdc_log(context, config, 5,
1405		    "Found matching PK-INIT SAN in certificate");
1406	    return 0;
1407	}
1408	ret = match_ms_upn_san(context, config,
1409			       kdc_identity->hx509ctx,
1410			       client_params->cert,
1411			       client->entry.principal);
1412	if (ret == 0) {
1413	    kdc_log(context, config, 5,
1414		    "Found matching MS UPN SAN in certificate");
1415	    return 0;
1416	}
1417    }
1418
1419    ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1420    if (ret == 0 && acl != NULL) {
1421	/*
1422	 * Cheat here and compare the generated name with the string
1423	 * and not the reverse.
1424	 */
1425	for (i = 0; i < acl->len; i++) {
1426	    if (strcmp(*subject_name, acl->val[0].subject) != 0)
1427		continue;
1428
1429	    /* Don't support isser and anchor checking right now */
1430	    if (acl->val[0].issuer)
1431		continue;
1432	    if (acl->val[0].anchor)
1433		continue;
1434
1435	    kdc_log(context, config, 5,
1436		    "Found matching PK-INIT database ACL");
1437	    return 0;
1438	}
1439    }
1440
1441    for (i = 0; i < principal_mappings.len; i++) {
1442	krb5_boolean b;
1443
1444	b = krb5_principal_compare(context,
1445				   client->entry.principal,
1446				   principal_mappings.val[i].principal);
1447	if (b == FALSE)
1448	    continue;
1449	if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1450	    continue;
1451	kdc_log(context, config, 5,
1452		"Found matching PK-INIT FILE ACL");
1453	return 0;
1454    }
1455
1456    krb5_set_error_string(context,
1457			  "PKINIT no matching principals for %s",
1458			  *subject_name);
1459
1460    kdc_log(context, config, 5,
1461	    "PKINIT no matching principals for %s",
1462	    *subject_name);
1463
1464    free(*subject_name);
1465    *subject_name = NULL;
1466
1467    return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1468}
1469
1470static krb5_error_code
1471add_principal_mapping(krb5_context context,
1472		      const char *principal_name,
1473		      const char * subject)
1474{
1475   struct pk_allowed_princ *tmp;
1476   krb5_principal principal;
1477   krb5_error_code ret;
1478
1479   tmp = realloc(principal_mappings.val,
1480	         (principal_mappings.len + 1) * sizeof(*tmp));
1481   if (tmp == NULL)
1482       return ENOMEM;
1483   principal_mappings.val = tmp;
1484
1485   ret = krb5_parse_name(context, principal_name, &principal);
1486   if (ret)
1487       return ret;
1488
1489   principal_mappings.val[principal_mappings.len].principal = principal;
1490
1491   principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1492   if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1493       krb5_free_principal(context, principal);
1494       return ENOMEM;
1495   }
1496   principal_mappings.len++;
1497
1498   return 0;
1499}
1500
1501krb5_error_code
1502_kdc_add_inital_verified_cas(krb5_context context,
1503			     krb5_kdc_configuration *config,
1504			     pk_client_params *params,
1505			     EncTicketPart *tkt)
1506{
1507    AD_INITIAL_VERIFIED_CAS cas;
1508    krb5_error_code ret;
1509    krb5_data data;
1510    size_t size;
1511
1512    memset(&cas, 0, sizeof(cas));
1513
1514    /* XXX add CAs to cas here */
1515
1516    ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1517		       &cas, &size, ret);
1518    if (ret)
1519	return ret;
1520    if (data.length != size)
1521	krb5_abortx(context, "internal asn.1 encoder error");
1522
1523    ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1524				      KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1525				      &data);
1526    krb5_data_free(&data);
1527    return ret;
1528}
1529
1530/*
1531 *
1532 */
1533
1534static void
1535load_mappings(krb5_context context, const char *fn)
1536{
1537    krb5_error_code ret;
1538    char buf[1024];
1539    unsigned long lineno = 0;
1540    FILE *f;
1541
1542    f = fopen(fn, "r");
1543    if (f == NULL)
1544	return;
1545
1546    while (fgets(buf, sizeof(buf), f) != NULL) {
1547	char *subject_name, *p;
1548
1549	buf[strcspn(buf, "\n")] = '\0';
1550	lineno++;
1551
1552	p = buf + strspn(buf, " \t");
1553
1554	if (*p == '#' || *p == '\0')
1555	    continue;
1556
1557	subject_name = strchr(p, ':');
1558	if (subject_name == NULL) {
1559	    krb5_warnx(context, "pkinit mapping file line %lu "
1560		       "missing \":\" :%s",
1561		       lineno, buf);
1562	    continue;
1563	}
1564	*subject_name++ = '\0';
1565
1566	ret = add_principal_mapping(context, p, subject_name);
1567	if (ret) {
1568	    krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1569		      lineno, buf);
1570	    continue;
1571	}
1572    }
1573
1574    fclose(f);
1575}
1576
1577/*
1578 *
1579 */
1580
1581krb5_error_code
1582_kdc_pk_initialize(krb5_context context,
1583		   krb5_kdc_configuration *config,
1584		   const char *user_id,
1585		   const char *anchors,
1586		   char **pool,
1587		   char **revoke_list)
1588{
1589    const char *file;
1590    char *fn = NULL;
1591    krb5_error_code ret;
1592
1593    file = krb5_config_get_string(context, NULL,
1594				  "libdefaults", "moduli", NULL);
1595
1596    ret = _krb5_parse_moduli(context, file, &moduli);
1597    if (ret)
1598	krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1599
1600    principal_mappings.len = 0;
1601    principal_mappings.val = NULL;
1602
1603    ret = _krb5_pk_load_id(context,
1604			   &kdc_identity,
1605			   user_id,
1606			   anchors,
1607			   pool,
1608			   revoke_list,
1609			   NULL,
1610			   NULL,
1611			   NULL);
1612    if (ret) {
1613	krb5_warn(context, ret, "PKINIT: ");
1614	config->enable_pkinit = 0;
1615	return ret;
1616    }
1617
1618    {
1619	hx509_query *q;
1620	hx509_cert cert;
1621
1622	ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
1623	if (ret) {
1624	    krb5_warnx(context, "PKINIT: out of memory");
1625	    return ENOMEM;
1626	}
1627
1628	hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1629	hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1630
1631	ret = hx509_certs_find(kdc_identity->hx509ctx,
1632			       kdc_identity->certs,
1633			       q,
1634			       &cert);
1635	hx509_query_free(kdc_identity->hx509ctx, q);
1636	if (ret == 0) {
1637	    if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
1638				     oid_id_pkkdcekuoid(), 0))
1639		krb5_warnx(context, "WARNING Found KDC certificate "
1640			   "is missing the PK-INIT KDC EKU, this is bad for "
1641			   "interoperability.");
1642	    hx509_cert_free(cert);
1643	} else
1644	    krb5_warnx(context, "PKINIT: failed to find a signing "
1645		       "certifiate with a public key");
1646    }
1647
1648    ret = krb5_config_get_bool_default(context,
1649				       NULL,
1650				       FALSE,
1651				       "kdc",
1652				       "pkinit_allow_proxy_certificate",
1653				       NULL);
1654    _krb5_pk_allow_proxy_certificate(kdc_identity, ret);
1655
1656    file = krb5_config_get_string(context,
1657				  NULL,
1658				  "kdc",
1659				  "pkinit_mappings_file",
1660				  NULL);
1661    if (file == NULL) {
1662	asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
1663	file = fn;
1664    }
1665
1666    load_mappings(context, file);
1667    if (fn)
1668	free(fn);
1669
1670    return 0;
1671}
1672
1673#endif /* PKINIT */
1674