Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / crypto / dist / heimdal / kdc / pkinit.c
blobbe54d99860a15ef0acf900a40372fa071daff1fd
1 /*
2 * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "kdc_locl.h"
36 __RCSID("$Heimdal: pkinit.c 22243 2007-12-08 23:39:30Z lha $"
37 "$NetBSD$");
39 #ifdef PKINIT
41 #include <heim_asn1.h>
42 #include <rfc2459_asn1.h>
43 #include <cms_asn1.h>
44 #include <pkinit_asn1.h>
46 #include <hx509.h>
47 #include "crypto-headers.h"
49 /* XXX copied from lib/krb5/pkinit.c */
50 struct krb5_pk_identity {
51 hx509_context hx509ctx;
52 hx509_verify_ctx verify_ctx;
53 hx509_certs certs;
54 hx509_certs anchors;
55 hx509_certs certpool;
56 hx509_revoke_ctx revoke;
59 enum pkinit_type {
60 PKINIT_COMPAT_WIN2K = 1,
61 PKINIT_COMPAT_27 = 3
64 struct pk_client_params {
65 enum pkinit_type type;
66 BIGNUM *dh_public_key;
67 hx509_cert cert;
68 unsigned nonce;
69 DH *dh;
70 EncryptionKey reply_key;
71 char *dh_group_name;
72 hx509_peer_info peer;
73 hx509_certs client_anchors;
76 struct pk_principal_mapping {
77 unsigned int len;
78 struct pk_allowed_princ {
79 krb5_principal principal;
80 char *subject;
81 } *val;
84 static struct krb5_pk_identity *kdc_identity;
85 static struct pk_principal_mapping principal_mappings;
86 static struct krb5_dh_moduli **moduli;
88 static struct {
89 krb5_data data;
90 time_t expire;
91 time_t next_update;
92 } ocsp;
98 static krb5_error_code
99 pk_check_pkauthenticator_win2k(krb5_context context,
100 PKAuthenticator_Win2k *a,
101 const KDC_REQ *req)
103 krb5_timestamp now;
105 krb5_timeofday (context, &now);
107 /* XXX cusec */
108 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
109 krb5_clear_error_string(context);
110 return KRB5KRB_AP_ERR_SKEW;
112 return 0;
115 static krb5_error_code
116 pk_check_pkauthenticator(krb5_context context,
117 PKAuthenticator *a,
118 const KDC_REQ *req)
120 u_char *buf = NULL;
121 size_t buf_size;
122 krb5_error_code ret;
123 size_t len;
124 krb5_timestamp now;
125 Checksum checksum;
127 krb5_timeofday (context, &now);
129 /* XXX cusec */
130 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
131 krb5_clear_error_string(context);
132 return KRB5KRB_AP_ERR_SKEW;
135 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
136 if (ret) {
137 krb5_clear_error_string(context);
138 return ret;
140 if (buf_size != len)
141 krb5_abortx(context, "Internal error in ASN.1 encoder");
143 ret = krb5_create_checksum(context,
144 NULL,
146 CKSUMTYPE_SHA1,
147 buf,
148 len,
149 &checksum);
150 free(buf);
151 if (ret) {
152 krb5_clear_error_string(context);
153 return ret;
156 if (a->paChecksum == NULL) {
157 krb5_clear_error_string(context);
158 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
159 goto out;
162 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
163 krb5_clear_error_string(context);
164 ret = KRB5KRB_ERR_GENERIC;
167 out:
168 free_Checksum(&checksum);
170 return ret;
173 void
174 _kdc_pk_free_client_param(krb5_context context,
175 pk_client_params *client_params)
177 if (client_params->cert)
178 hx509_cert_free(client_params->cert);
179 if (client_params->dh)
180 DH_free(client_params->dh);
181 if (client_params->dh_public_key)
182 BN_free(client_params->dh_public_key);
183 krb5_free_keyblock_contents(context, &client_params->reply_key);
184 if (client_params->dh_group_name)
185 free(client_params->dh_group_name);
186 if (client_params->peer)
187 hx509_peer_info_free(client_params->peer);
188 if (client_params->client_anchors)
189 hx509_certs_free(&client_params->client_anchors);
190 memset(client_params, 0, sizeof(*client_params));
191 free(client_params);
194 static krb5_error_code
195 generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
196 krb5_enctype enctype, krb5_keyblock *reply_key)
198 unsigned char *dh_gen_key = NULL;
199 krb5_keyblock key;
200 krb5_error_code ret;
201 size_t dh_gen_keylen, size;
203 memset(&key, 0, sizeof(key));
205 if (!DH_generate_key(client_params->dh)) {
206 krb5_set_error_string(context, "Can't generate Diffie-Hellman keys");
207 ret = KRB5KRB_ERR_GENERIC;
208 goto out;
210 if (client_params->dh_public_key == NULL) {
211 krb5_set_error_string(context, "dh_public_key");
212 ret = KRB5KRB_ERR_GENERIC;
213 goto out;
216 dh_gen_keylen = DH_size(client_params->dh);
217 size = BN_num_bytes(client_params->dh->p);
218 if (size < dh_gen_keylen)
219 size = dh_gen_keylen;
221 dh_gen_key = malloc(size);
222 if (dh_gen_key == NULL) {
223 krb5_set_error_string(context, "malloc: out of memory");
224 ret = ENOMEM;
225 goto out;
227 memset(dh_gen_key, 0, size - dh_gen_keylen);
229 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
230 client_params->dh_public_key,
231 client_params->dh);
232 if (dh_gen_keylen == -1) {
233 krb5_set_error_string(context, "Can't compute Diffie-Hellman key");
234 ret = KRB5KRB_ERR_GENERIC;
235 goto out;
238 ret = _krb5_pk_octetstring2key(context,
239 enctype,
240 dh_gen_key, dh_gen_keylen,
241 NULL, NULL,
242 reply_key);
244 out:
245 if (dh_gen_key)
246 free(dh_gen_key);
247 if (key.keyvalue.data)
248 krb5_free_keyblock_contents(context, &key);
250 return ret;
253 static BIGNUM *
254 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
256 BIGNUM *bn;
258 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
259 if (bn == NULL) {
260 krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
261 return NULL;
263 BN_set_negative(bn, f->negative);
264 return bn;
267 static krb5_error_code
268 get_dh_param(krb5_context context,
269 krb5_kdc_configuration *config,
270 SubjectPublicKeyInfo *dh_key_info,
271 pk_client_params *client_params)
273 DomainParameters dhparam;
274 DH *dh = NULL;
275 krb5_error_code ret;
277 memset(&dhparam, 0, sizeof(dhparam));
279 if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
280 krb5_set_error_string(context,
281 "PKINIT invalid oid in clientPublicValue");
282 return KRB5_BADMSGTYPE;
285 if (dh_key_info->algorithm.parameters == NULL) {
286 krb5_set_error_string(context, "PKINIT missing algorithm parameter "
287 "in clientPublicValue");
288 return KRB5_BADMSGTYPE;
291 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
292 dh_key_info->algorithm.parameters->length,
293 &dhparam,
294 NULL);
295 if (ret) {
296 krb5_set_error_string(context, "Can't decode algorithm "
297 "parameters in clientPublicValue");
298 goto out;
301 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
302 ret = KRB5_BADMSGTYPE;
303 krb5_set_error_string(context, "PKINIT: subjectPublicKey not aligned "
304 "to 8 bit boundary");
305 goto out;
309 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
310 &dhparam.p, &dhparam.g, &dhparam.q, moduli,
311 &client_params->dh_group_name);
312 if (ret) {
313 /* XXX send back proposal of better group */
314 goto out;
317 dh = DH_new();
318 if (dh == NULL) {
319 krb5_set_error_string(context, "Cannot create DH structure");
320 ret = ENOMEM;
321 goto out;
323 ret = KRB5_BADMSGTYPE;
324 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
325 if (dh->p == NULL)
326 goto out;
327 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
328 if (dh->g == NULL)
329 goto out;
330 dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
331 if (dh->g == NULL)
332 goto out;
335 heim_integer glue;
336 size_t size;
338 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
339 dh_key_info->subjectPublicKey.length / 8,
340 &glue,
341 &size);
342 if (ret) {
343 krb5_clear_error_string(context);
344 return ret;
347 client_params->dh_public_key = integer_to_BN(context,
348 "subjectPublicKey",
349 &glue);
350 der_free_heim_integer(&glue);
351 if (client_params->dh_public_key == NULL)
352 goto out;
355 client_params->dh = dh;
356 dh = NULL;
357 ret = 0;
359 out:
360 if (dh)
361 DH_free(dh);
362 free_DomainParameters(&dhparam);
363 return ret;
366 krb5_error_code
367 _kdc_pk_rd_padata(krb5_context context,
368 krb5_kdc_configuration *config,
369 const KDC_REQ *req,
370 const PA_DATA *pa,
371 pk_client_params **ret_params)
373 pk_client_params *client_params;
374 krb5_error_code ret;
375 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
376 krb5_data eContent = { 0, NULL };
377 krb5_data signed_content = { 0, NULL };
378 const char *type = "unknown type";
379 int have_data = 0;
381 *ret_params = NULL;
383 if (!config->enable_pkinit) {
384 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
385 krb5_clear_error_string(context);
386 return 0;
389 hx509_verify_set_time(kdc_identity->verify_ctx, _kdc_now.tv_sec);
391 client_params = calloc(1, sizeof(*client_params));
392 if (client_params == NULL) {
393 krb5_clear_error_string(context);
394 ret = ENOMEM;
395 goto out;
398 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
399 PA_PK_AS_REQ_Win2k r;
401 type = "PK-INIT-Win2k";
403 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
404 pa->padata_value.length,
406 NULL);
407 if (ret) {
408 krb5_set_error_string(context, "Can't decode "
409 "PK-AS-REQ-Win2k: %d", ret);
410 goto out;
413 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
414 &contentInfoOid,
415 &signed_content,
416 &have_data);
417 free_PA_PK_AS_REQ_Win2k(&r);
418 if (ret) {
419 krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
420 goto out;
423 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
424 PA_PK_AS_REQ r;
426 type = "PK-INIT-IETF";
428 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
429 pa->padata_value.length,
431 NULL);
432 if (ret) {
433 krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
434 goto out;
437 /* XXX look at r.kdcPkId */
438 if (r.trustedCertifiers) {
439 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
440 unsigned int i;
442 ret = hx509_certs_init(kdc_identity->hx509ctx,
443 "MEMORY:client-anchors",
444 0, NULL,
445 &client_params->client_anchors);
446 if (ret) {
447 krb5_set_error_string(context, "Can't allocate client anchors: %d", ret);
448 goto out;
451 for (i = 0; i < edi->len; i++) {
452 IssuerAndSerialNumber iasn;
453 hx509_query *q;
454 hx509_cert cert;
455 size_t size;
457 if (edi->val[i].issuerAndSerialNumber == NULL)
458 continue;
460 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
461 if (ret) {
462 krb5_set_error_string(context,
463 "Failed to allocate hx509_query");
464 goto out;
467 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
468 edi->val[i].issuerAndSerialNumber->length,
469 &iasn,
470 &size);
471 if (ret) {
472 hx509_query_free(kdc_identity->hx509ctx, q);
473 continue;
475 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
476 free_IssuerAndSerialNumber(&iasn);
477 if (ret)
478 continue;
480 ret = hx509_certs_find(kdc_identity->hx509ctx,
481 kdc_identity->certs,
483 &cert);
484 hx509_query_free(kdc_identity->hx509ctx, q);
485 if (ret)
486 continue;
487 hx509_certs_add(kdc_identity->hx509ctx,
488 client_params->client_anchors, cert);
489 hx509_cert_free(cert);
493 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
494 &contentInfoOid,
495 &signed_content,
496 &have_data);
497 free_PA_PK_AS_REQ(&r);
498 if (ret) {
499 krb5_set_error_string(context, "Can't unwrap ContentInfo: %d", ret);
500 goto out;
503 } else {
504 krb5_clear_error_string(context);
505 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
506 goto out;
509 ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
510 if (ret != 0) {
511 krb5_set_error_string(context, "PK-AS-REQ-Win2k invalid content "
512 "type oid");
513 ret = KRB5KRB_ERR_GENERIC;
514 goto out;
517 if (!have_data) {
518 krb5_set_error_string(context,
519 "PK-AS-REQ-Win2k no signed auth pack");
520 ret = KRB5KRB_ERR_GENERIC;
521 goto out;
525 hx509_certs signer_certs;
527 ret = hx509_cms_verify_signed(kdc_identity->hx509ctx,
528 kdc_identity->verify_ctx,
529 signed_content.data,
530 signed_content.length,
531 NULL,
532 kdc_identity->certpool,
533 &eContentType,
534 &eContent,
535 &signer_certs);
536 if (ret) {
537 char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret);
538 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
539 s, ret);
540 free(s);
541 goto out;
544 ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs,
545 &client_params->cert);
546 hx509_certs_free(&signer_certs);
547 if (ret)
548 goto out;
551 /* Signature is correct, now verify the signed message */
552 if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 &&
553 der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0)
555 krb5_set_error_string(context, "got wrong oid for pkauthdata");
556 ret = KRB5_BADMSGTYPE;
557 goto out;
560 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
561 AuthPack_Win2k ap;
563 ret = decode_AuthPack_Win2k(eContent.data,
564 eContent.length,
565 &ap,
566 NULL);
567 if (ret) {
568 krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
569 goto out;
572 ret = pk_check_pkauthenticator_win2k(context,
573 &ap.pkAuthenticator,
574 req);
575 if (ret) {
576 free_AuthPack_Win2k(&ap);
577 goto out;
580 client_params->type = PKINIT_COMPAT_WIN2K;
581 client_params->nonce = ap.pkAuthenticator.nonce;
583 if (ap.clientPublicValue) {
584 krb5_set_error_string(context, "DH not supported for windows");
585 ret = KRB5KRB_ERR_GENERIC;
586 goto out;
588 free_AuthPack_Win2k(&ap);
590 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
591 AuthPack ap;
593 ret = decode_AuthPack(eContent.data,
594 eContent.length,
595 &ap,
596 NULL);
597 if (ret) {
598 krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
599 free_AuthPack(&ap);
600 goto out;
603 ret = pk_check_pkauthenticator(context,
604 &ap.pkAuthenticator,
605 req);
606 if (ret) {
607 free_AuthPack(&ap);
608 goto out;
611 client_params->type = PKINIT_COMPAT_27;
612 client_params->nonce = ap.pkAuthenticator.nonce;
614 if (ap.clientPublicValue) {
615 ret = get_dh_param(context, config,
616 ap.clientPublicValue, client_params);
617 if (ret) {
618 free_AuthPack(&ap);
619 goto out;
623 if (ap.supportedCMSTypes) {
624 ret = hx509_peer_info_alloc(kdc_identity->hx509ctx,
625 &client_params->peer);
626 if (ret) {
627 free_AuthPack(&ap);
628 goto out;
630 ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx,
631 client_params->peer,
632 ap.supportedCMSTypes->val,
633 ap.supportedCMSTypes->len);
634 if (ret) {
635 free_AuthPack(&ap);
636 goto out;
639 free_AuthPack(&ap);
640 } else
641 krb5_abortx(context, "internal pkinit error");
643 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
645 out:
646 if (ret)
647 krb5_warn(context, ret, "PKINIT");
649 if (signed_content.data)
650 free(signed_content.data);
651 krb5_data_free(&eContent);
652 der_free_oid(&eContentType);
653 der_free_oid(&contentInfoOid);
654 if (ret)
655 _kdc_pk_free_client_param(context, client_params);
656 else
657 *ret_params = client_params;
658 return ret;
665 static krb5_error_code
666 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
668 integer->length = BN_num_bytes(bn);
669 integer->data = malloc(integer->length);
670 if (integer->data == NULL) {
671 krb5_clear_error_string(context);
672 return ENOMEM;
674 BN_bn2bin(bn, integer->data);
675 integer->negative = BN_is_negative(bn);
676 return 0;
679 static krb5_error_code
680 pk_mk_pa_reply_enckey(krb5_context context,
681 krb5_kdc_configuration *config,
682 pk_client_params *client_params,
683 const KDC_REQ *req,
684 const krb5_data *req_buffer,
685 krb5_keyblock *reply_key,
686 ContentInfo *content_info)
688 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL;
689 krb5_error_code ret;
690 krb5_data buf, signed_data;
691 size_t size;
692 int do_win2k = 0;
694 krb5_data_zero(&buf);
695 krb5_data_zero(&signed_data);
698 * If the message client is a win2k-type but it send pa data
699 * 09-binding it expects a IETF (checksum) reply so there can be
700 * no replay attacks.
703 switch (client_params->type) {
704 case PKINIT_COMPAT_WIN2K: {
705 int i = 0;
706 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
707 && config->pkinit_require_binding == 0)
709 do_win2k = 1;
711 break;
713 case PKINIT_COMPAT_27:
714 break;
715 default:
716 krb5_abortx(context, "internal pkinit error");
719 if (do_win2k) {
720 ReplyKeyPack_Win2k kp;
721 memset(&kp, 0, sizeof(kp));
723 envelopedAlg = oid_id_rsadsi_des_ede3_cbc();
724 sdAlg = oid_id_pkcs7_data();
726 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
727 if (ret) {
728 krb5_clear_error_string(context);
729 goto out;
731 kp.nonce = client_params->nonce;
733 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
734 buf.data, buf.length,
735 &kp, &size,ret);
736 free_ReplyKeyPack_Win2k(&kp);
737 } else {
738 krb5_crypto ascrypto;
739 ReplyKeyPack kp;
740 memset(&kp, 0, sizeof(kp));
742 sdAlg = oid_id_pkrkeydata();
744 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
745 if (ret) {
746 krb5_clear_error_string(context);
747 goto out;
750 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
751 if (ret) {
752 krb5_clear_error_string(context);
753 goto out;
756 ret = krb5_create_checksum(context, ascrypto, 6, 0,
757 req_buffer->data, req_buffer->length,
758 &kp.asChecksum);
759 if (ret) {
760 krb5_clear_error_string(context);
761 goto out;
764 ret = krb5_crypto_destroy(context, ascrypto);
765 if (ret) {
766 krb5_clear_error_string(context);
767 goto out;
769 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
770 free_ReplyKeyPack(&kp);
772 if (ret) {
773 krb5_set_error_string(context, "ASN.1 encoding of ReplyKeyPack "
774 "failed (%d)", ret);
775 goto out;
777 if (buf.length != size)
778 krb5_abortx(context, "Internal ASN.1 encoder error");
781 hx509_query *q;
782 hx509_cert cert;
784 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
785 if (ret)
786 goto out;
788 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
789 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
791 ret = hx509_certs_find(kdc_identity->hx509ctx,
792 kdc_identity->certs,
794 &cert);
795 hx509_query_free(kdc_identity->hx509ctx, q);
796 if (ret)
797 goto out;
799 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
801 sdAlg,
802 buf.data,
803 buf.length,
804 NULL,
805 cert,
806 client_params->peer,
807 client_params->client_anchors,
808 kdc_identity->certpool,
809 &signed_data);
810 hx509_cert_free(cert);
813 krb5_data_free(&buf);
814 if (ret)
815 goto out;
817 if (client_params->type == PKINIT_COMPAT_WIN2K) {
818 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
819 &signed_data,
820 &buf);
821 if (ret)
822 goto out;
823 krb5_data_free(&signed_data);
824 signed_data = buf;
827 ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
829 client_params->cert,
830 signed_data.data, signed_data.length,
831 envelopedAlg,
832 oid_id_pkcs7_signedData(), &buf);
833 if (ret)
834 goto out;
836 ret = _krb5_pk_mk_ContentInfo(context,
837 &buf,
838 oid_id_pkcs7_envelopedData(),
839 content_info);
840 out:
841 krb5_data_free(&buf);
842 krb5_data_free(&signed_data);
843 return ret;
850 static krb5_error_code
851 pk_mk_pa_reply_dh(krb5_context context,
852 DH *kdc_dh,
853 pk_client_params *client_params,
854 krb5_keyblock *reply_key,
855 ContentInfo *content_info,
856 hx509_cert *kdc_cert)
858 KDCDHKeyInfo dh_info;
859 krb5_data signed_data, buf;
860 ContentInfo contentinfo;
861 krb5_error_code ret;
862 size_t size;
863 heim_integer i;
865 memset(&contentinfo, 0, sizeof(contentinfo));
866 memset(&dh_info, 0, sizeof(dh_info));
867 krb5_data_zero(&buf);
868 krb5_data_zero(&signed_data);
870 *kdc_cert = NULL;
872 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
873 if (ret)
874 return ret;
876 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
877 if (ret) {
878 krb5_set_error_string(context, "ASN.1 encoding of "
879 "DHPublicKey failed (%d)", ret);
880 krb5_clear_error_string(context);
881 return ret;
883 if (buf.length != size)
884 krb5_abortx(context, "Internal ASN.1 encoder error");
886 dh_info.subjectPublicKey.length = buf.length * 8;
887 dh_info.subjectPublicKey.data = buf.data;
889 dh_info.nonce = client_params->nonce;
891 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
892 ret);
893 if (ret) {
894 krb5_set_error_string(context, "ASN.1 encoding of "
895 "KdcDHKeyInfo failed (%d)", ret);
896 goto out;
898 if (buf.length != size)
899 krb5_abortx(context, "Internal ASN.1 encoder error");
902 * Create the SignedData structure and sign the KdcDHKeyInfo
903 * filled in above
907 hx509_query *q;
908 hx509_cert cert;
910 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
911 if (ret)
912 goto out;
914 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
915 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
917 ret = hx509_certs_find(kdc_identity->hx509ctx,
918 kdc_identity->certs,
920 &cert);
921 hx509_query_free(kdc_identity->hx509ctx, q);
922 if (ret)
923 goto out;
925 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
927 oid_id_pkdhkeydata(),
928 buf.data,
929 buf.length,
930 NULL,
931 cert,
932 client_params->peer,
933 client_params->client_anchors,
934 kdc_identity->certpool,
935 &signed_data);
936 *kdc_cert = cert;
938 if (ret)
939 goto out;
941 ret = _krb5_pk_mk_ContentInfo(context,
942 &signed_data,
943 oid_id_pkcs7_signedData(),
944 content_info);
945 if (ret)
946 goto out;
948 out:
949 if (ret && *kdc_cert) {
950 hx509_cert_free(*kdc_cert);
951 *kdc_cert = NULL;
954 krb5_data_free(&buf);
955 krb5_data_free(&signed_data);
956 free_KDCDHKeyInfo(&dh_info);
958 return ret;
965 krb5_error_code
966 _kdc_pk_mk_pa_reply(krb5_context context,
967 krb5_kdc_configuration *config,
968 pk_client_params *client_params,
969 const hdb_entry_ex *client,
970 const KDC_REQ *req,
971 const krb5_data *req_buffer,
972 krb5_keyblock **reply_key,
973 METHOD_DATA *md)
975 krb5_error_code ret;
976 void *buf;
977 size_t len, size;
978 krb5_enctype enctype;
979 int pa_type;
980 hx509_cert kdc_cert = NULL;
981 int i;
983 if (!config->enable_pkinit) {
984 krb5_clear_error_string(context);
985 return 0;
988 if (req->req_body.etype.len > 0) {
989 for (i = 0; i < req->req_body.etype.len; i++)
990 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
991 break;
992 if (req->req_body.etype.len <= i) {
993 ret = KRB5KRB_ERR_GENERIC;
994 krb5_set_error_string(context,
995 "No valid enctype available from client");
996 goto out;
998 enctype = req->req_body.etype.val[i];
999 } else
1000 enctype = ETYPE_DES3_CBC_SHA1;
1002 if (client_params->type == PKINIT_COMPAT_27) {
1003 PA_PK_AS_REP rep;
1004 const char *type, *other = "";
1006 memset(&rep, 0, sizeof(rep));
1008 pa_type = KRB5_PADATA_PK_AS_REP;
1010 if (client_params->dh == NULL) {
1011 ContentInfo info;
1013 type = "enckey";
1015 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1017 ret = krb5_generate_random_keyblock(context, enctype,
1018 &client_params->reply_key);
1019 if (ret) {
1020 free_PA_PK_AS_REP(&rep);
1021 goto out;
1023 ret = pk_mk_pa_reply_enckey(context,
1024 config,
1025 client_params,
1026 req,
1027 req_buffer,
1028 &client_params->reply_key,
1029 &info);
1030 if (ret) {
1031 free_PA_PK_AS_REP(&rep);
1032 goto out;
1034 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1035 rep.u.encKeyPack.length, &info, &size,
1036 ret);
1037 free_ContentInfo(&info);
1038 if (ret) {
1039 krb5_set_error_string(context, "encoding of Key ContentInfo "
1040 "failed %d", ret);
1041 free_PA_PK_AS_REP(&rep);
1042 goto out;
1044 if (rep.u.encKeyPack.length != size)
1045 krb5_abortx(context, "Internal ASN.1 encoder error");
1047 } else {
1048 ContentInfo info;
1050 type = "dh";
1051 if (client_params->dh_group_name)
1052 other = client_params->dh_group_name;
1054 rep.element = choice_PA_PK_AS_REP_dhInfo;
1056 ret = generate_dh_keyblock(context, client_params, enctype,
1057 &client_params->reply_key);
1058 if (ret)
1059 return ret;
1061 ret = pk_mk_pa_reply_dh(context, client_params->dh,
1062 client_params,
1063 &client_params->reply_key,
1064 &info,
1065 &kdc_cert);
1067 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1068 rep.u.dhInfo.dhSignedData.length, &info, &size,
1069 ret);
1070 free_ContentInfo(&info);
1071 if (ret) {
1072 krb5_set_error_string(context, "encoding of Key ContentInfo "
1073 "failed %d", ret);
1074 free_PA_PK_AS_REP(&rep);
1075 goto out;
1077 if (rep.u.encKeyPack.length != size)
1078 krb5_abortx(context, "Internal ASN.1 encoder error");
1081 if (ret) {
1082 free_PA_PK_AS_REP(&rep);
1083 goto out;
1086 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1087 free_PA_PK_AS_REP(&rep);
1088 if (ret) {
1089 krb5_set_error_string(context, "encode PA-PK-AS-REP failed %d",
1090 ret);
1091 goto out;
1093 if (len != size)
1094 krb5_abortx(context, "Internal ASN.1 encoder error");
1096 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1098 } else if (client_params->type == PKINIT_COMPAT_WIN2K) {
1099 PA_PK_AS_REP_Win2k rep;
1100 ContentInfo info;
1102 if (client_params->dh) {
1103 krb5_set_error_string(context, "Windows PK-INIT doesn't support DH");
1104 ret = KRB5KRB_ERR_GENERIC;
1105 goto out;
1108 memset(&rep, 0, sizeof(rep));
1110 pa_type = KRB5_PADATA_PK_AS_REP_19;
1111 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1113 ret = krb5_generate_random_keyblock(context, enctype,
1114 &client_params->reply_key);
1115 if (ret) {
1116 free_PA_PK_AS_REP_Win2k(&rep);
1117 goto out;
1119 ret = pk_mk_pa_reply_enckey(context,
1120 config,
1121 client_params,
1122 req,
1123 req_buffer,
1124 &client_params->reply_key,
1125 &info);
1126 if (ret) {
1127 free_PA_PK_AS_REP_Win2k(&rep);
1128 goto out;
1130 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1131 rep.u.encKeyPack.length, &info, &size,
1132 ret);
1133 free_ContentInfo(&info);
1134 if (ret) {
1135 krb5_set_error_string(context, "encoding of Key ContentInfo "
1136 "failed %d", ret);
1137 free_PA_PK_AS_REP_Win2k(&rep);
1138 goto out;
1140 if (rep.u.encKeyPack.length != size)
1141 krb5_abortx(context, "Internal ASN.1 encoder error");
1143 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1144 free_PA_PK_AS_REP_Win2k(&rep);
1145 if (ret) {
1146 krb5_set_error_string(context,
1147 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1148 goto out;
1150 if (len != size)
1151 krb5_abortx(context, "Internal ASN.1 encoder error");
1153 } else
1154 krb5_abortx(context, "PK-INIT internal error");
1157 ret = krb5_padata_add(context, md, pa_type, buf, len);
1158 if (ret) {
1159 krb5_set_error_string(context, "failed adding PA-PK-AS-REP %d", ret);
1160 free(buf);
1161 goto out;
1164 if (config->pkinit_kdc_ocsp_file) {
1166 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1167 struct stat sb;
1168 int fd;
1170 krb5_data_free(&ocsp.data);
1172 ocsp.expire = 0;
1173 ocsp.next_update = kdc_time + 60 * 5;
1175 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1176 if (fd < 0) {
1177 kdc_log(context, config, 0,
1178 "PK-INIT failed to open ocsp data file %d", errno);
1179 goto out_ocsp;
1181 ret = fstat(fd, &sb);
1182 if (ret) {
1183 ret = errno;
1184 close(fd);
1185 kdc_log(context, config, 0,
1186 "PK-INIT failed to stat ocsp data %d", ret);
1187 goto out_ocsp;
1190 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1191 if (ret) {
1192 close(fd);
1193 kdc_log(context, config, 0,
1194 "PK-INIT failed to stat ocsp data %d", ret);
1195 goto out_ocsp;
1197 ocsp.data.length = sb.st_size;
1198 ret = read(fd, ocsp.data.data, sb.st_size);
1199 close(fd);
1200 if (ret != sb.st_size) {
1201 kdc_log(context, config, 0,
1202 "PK-INIT failed to read ocsp data %d", errno);
1203 goto out_ocsp;
1206 ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
1207 kdc_time,
1208 kdc_cert,
1210 ocsp.data.data, ocsp.data.length,
1211 &ocsp.expire);
1212 if (ret) {
1213 kdc_log(context, config, 0,
1214 "PK-INIT failed to verify ocsp data %d", ret);
1215 krb5_data_free(&ocsp.data);
1216 ocsp.expire = 0;
1217 } else if (ocsp.expire > 180) {
1218 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1219 ocsp.next_update = ocsp.expire;
1220 } else {
1221 ocsp.next_update = kdc_time;
1223 out_ocsp:
1224 ret = 0;
1227 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1229 ret = krb5_padata_add(context, md,
1230 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1231 ocsp.data.data, ocsp.data.length);
1232 if (ret) {
1233 krb5_set_error_string(context,
1234 "Failed adding OCSP response %d", ret);
1235 goto out;
1240 out:
1241 if (kdc_cert)
1242 hx509_cert_free(kdc_cert);
1244 if (ret == 0)
1245 *reply_key = &client_params->reply_key;
1246 return ret;
1249 static int
1250 match_rfc_san(krb5_context context,
1251 krb5_kdc_configuration *config,
1252 hx509_context hx509ctx,
1253 hx509_cert client_cert,
1254 krb5_const_principal match)
1256 hx509_octet_string_list list;
1257 int ret, i, found = 0;
1259 memset(&list, 0 , sizeof(list));
1261 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1262 client_cert,
1263 oid_id_pkinit_san(),
1264 &list);
1265 if (ret)
1266 goto out;
1268 for (i = 0; !found && i < list.len; i++) {
1269 krb5_principal_data principal;
1270 KRB5PrincipalName kn;
1271 size_t size;
1273 ret = decode_KRB5PrincipalName(list.val[i].data,
1274 list.val[i].length,
1275 &kn, &size);
1276 if (ret) {
1277 kdc_log(context, config, 0,
1278 "Decoding kerberos name in certificate failed: %s",
1279 krb5_get_err_text(context, ret));
1280 break;
1282 if (size != list.val[i].length) {
1283 kdc_log(context, config, 0,
1284 "Decoding kerberos name have extra bits on the end");
1285 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1288 principal.name = kn.principalName;
1289 principal.realm = kn.realm;
1291 if (krb5_principal_compare(context, &principal, match) == TRUE)
1292 found = 1;
1293 free_KRB5PrincipalName(&kn);
1296 out:
1297 hx509_free_octet_string_list(&list);
1298 if (ret)
1299 return ret;
1301 if (!found)
1302 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1304 return 0;
1307 static int
1308 match_ms_upn_san(krb5_context context,
1309 krb5_kdc_configuration *config,
1310 hx509_context hx509ctx,
1311 hx509_cert client_cert,
1312 krb5_const_principal match)
1314 hx509_octet_string_list list;
1315 krb5_principal principal = NULL;
1316 int ret, found = 0;
1317 MS_UPN_SAN upn;
1318 size_t size;
1320 memset(&list, 0 , sizeof(list));
1322 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1323 client_cert,
1324 oid_id_pkinit_ms_san(),
1325 &list);
1326 if (ret)
1327 goto out;
1329 if (list.len != 1) {
1330 kdc_log(context, config, 0,
1331 "More then one PK-INIT MS UPN SAN");
1332 goto out;
1335 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1336 if (ret) {
1337 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1338 goto out;
1341 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1343 ret = krb5_parse_name(context, upn, &principal);
1344 free_MS_UPN_SAN(&upn);
1345 if (ret) {
1346 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1347 goto out;
1351 * This is very wrong, but will do for now, should really and a
1352 * plugin to the windc layer to very this ACL.
1354 strupr(principal->realm);
1356 if (krb5_principal_compare(context, principal, match) == TRUE)
1357 found = 1;
1359 out:
1360 if (principal)
1361 krb5_free_principal(context, principal);
1362 hx509_free_octet_string_list(&list);
1363 if (ret)
1364 return ret;
1366 if (!found)
1367 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1369 return 0;
1372 krb5_error_code
1373 _kdc_pk_check_client(krb5_context context,
1374 krb5_kdc_configuration *config,
1375 const hdb_entry_ex *client,
1376 pk_client_params *client_params,
1377 char **subject_name)
1379 const HDB_Ext_PKINIT_acl *acl;
1380 krb5_error_code ret;
1381 hx509_name name;
1382 int i;
1384 ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
1385 client_params->cert,
1386 &name);
1387 if (ret)
1388 return ret;
1390 ret = hx509_name_to_string(name, subject_name);
1391 hx509_name_free(&name);
1392 if (ret)
1393 return ret;
1395 kdc_log(context, config, 0,
1396 "Trying to authorize PK-INIT subject DN %s",
1397 *subject_name);
1399 if (config->pkinit_princ_in_cert) {
1400 ret = match_rfc_san(context, config,
1401 kdc_identity->hx509ctx,
1402 client_params->cert,
1403 client->entry.principal);
1404 if (ret == 0) {
1405 kdc_log(context, config, 5,
1406 "Found matching PK-INIT SAN in certificate");
1407 return 0;
1409 ret = match_ms_upn_san(context, config,
1410 kdc_identity->hx509ctx,
1411 client_params->cert,
1412 client->entry.principal);
1413 if (ret == 0) {
1414 kdc_log(context, config, 5,
1415 "Found matching MS UPN SAN in certificate");
1416 return 0;
1420 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1421 if (ret == 0 && acl != NULL) {
1423 * Cheat here and compare the generated name with the string
1424 * and not the reverse.
1426 for (i = 0; i < acl->len; i++) {
1427 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1428 continue;
1430 /* Don't support isser and anchor checking right now */
1431 if (acl->val[0].issuer)
1432 continue;
1433 if (acl->val[0].anchor)
1434 continue;
1436 kdc_log(context, config, 5,
1437 "Found matching PK-INIT database ACL");
1438 return 0;
1442 for (i = 0; i < principal_mappings.len; i++) {
1443 krb5_boolean b;
1445 b = krb5_principal_compare(context,
1446 client->entry.principal,
1447 principal_mappings.val[i].principal);
1448 if (b == FALSE)
1449 continue;
1450 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1451 continue;
1452 kdc_log(context, config, 5,
1453 "Found matching PK-INIT FILE ACL");
1454 return 0;
1457 krb5_set_error_string(context,
1458 "PKINIT no matching principals for %s",
1459 *subject_name);
1461 kdc_log(context, config, 5,
1462 "PKINIT no matching principals for %s",
1463 *subject_name);
1465 free(*subject_name);
1466 *subject_name = NULL;
1468 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1471 static krb5_error_code
1472 add_principal_mapping(krb5_context context,
1473 const char *principal_name,
1474 const char * subject)
1476 struct pk_allowed_princ *tmp;
1477 krb5_principal principal;
1478 krb5_error_code ret;
1480 tmp = realloc(principal_mappings.val,
1481 (principal_mappings.len + 1) * sizeof(*tmp));
1482 if (tmp == NULL)
1483 return ENOMEM;
1484 principal_mappings.val = tmp;
1486 ret = krb5_parse_name(context, principal_name, &principal);
1487 if (ret)
1488 return ret;
1490 principal_mappings.val[principal_mappings.len].principal = principal;
1492 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1493 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1494 krb5_free_principal(context, principal);
1495 return ENOMEM;
1497 principal_mappings.len++;
1499 return 0;
1502 krb5_error_code
1503 _kdc_add_inital_verified_cas(krb5_context context,
1504 krb5_kdc_configuration *config,
1505 pk_client_params *params,
1506 EncTicketPart *tkt)
1508 AD_INITIAL_VERIFIED_CAS cas;
1509 krb5_error_code ret;
1510 krb5_data data;
1511 size_t size;
1513 memset(&cas, 0, sizeof(cas));
1515 /* XXX add CAs to cas here */
1517 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1518 &cas, &size, ret);
1519 if (ret)
1520 return ret;
1521 if (data.length != size)
1522 krb5_abortx(context, "internal asn.1 encoder error");
1524 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1525 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1526 &data);
1527 krb5_data_free(&data);
1528 return ret;
1535 static void
1536 load_mappings(krb5_context context, const char *fn)
1538 krb5_error_code ret;
1539 char buf[1024];
1540 unsigned long lineno = 0;
1541 FILE *f;
1543 f = fopen(fn, "r");
1544 if (f == NULL)
1545 return;
1547 while (fgets(buf, sizeof(buf), f) != NULL) {
1548 char *subject_name, *p;
1550 buf[strcspn(buf, "\n")] = '\0';
1551 lineno++;
1553 p = buf + strspn(buf, " \t");
1555 if (*p == '#' || *p == '\0')
1556 continue;
1558 subject_name = strchr(p, ':');
1559 if (subject_name == NULL) {
1560 krb5_warnx(context, "pkinit mapping file line %lu "
1561 "missing \":\" :%s",
1562 lineno, buf);
1563 continue;
1565 *subject_name++ = '\0';
1567 ret = add_principal_mapping(context, p, subject_name);
1568 if (ret) {
1569 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1570 lineno, buf);
1571 continue;
1575 fclose(f);
1582 krb5_error_code
1583 _kdc_pk_initialize(krb5_context context,
1584 krb5_kdc_configuration *config,
1585 const char *user_id,
1586 const char *anchors,
1587 char **pool,
1588 char **revoke_list)
1590 const char *file;
1591 char *fn = NULL;
1592 krb5_error_code ret;
1594 file = krb5_config_get_string(context, NULL,
1595 "libdefaults", "moduli", NULL);
1597 ret = _krb5_parse_moduli(context, file, &moduli);
1598 if (ret)
1599 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1601 principal_mappings.len = 0;
1602 principal_mappings.val = NULL;
1604 ret = _krb5_pk_load_id(context,
1605 &kdc_identity,
1606 user_id,
1607 anchors,
1608 pool,
1609 revoke_list,
1610 NULL,
1611 NULL,
1612 NULL);
1613 if (ret) {
1614 krb5_warn(context, ret, "PKINIT: ");
1615 config->enable_pkinit = 0;
1616 return ret;
1620 hx509_query *q;
1621 hx509_cert cert;
1623 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
1624 if (ret) {
1625 krb5_warnx(context, "PKINIT: out of memory");
1626 return ENOMEM;
1629 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1630 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1632 ret = hx509_certs_find(kdc_identity->hx509ctx,
1633 kdc_identity->certs,
1635 &cert);
1636 hx509_query_free(kdc_identity->hx509ctx, q);
1637 if (ret == 0) {
1638 if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
1639 oid_id_pkkdcekuoid(), 0))
1640 krb5_warnx(context, "WARNING Found KDC certificate "
1641 "is missing the PK-INIT KDC EKU, this is bad for "
1642 "interoperability.");
1643 hx509_cert_free(cert);
1644 } else
1645 krb5_warnx(context, "PKINIT: failed to find a signing "
1646 "certifiate with a public key");
1649 ret = krb5_config_get_bool_default(context,
1650 NULL,
1651 FALSE,
1652 "kdc",
1653 "pkinit_allow_proxy_certificate",
1654 NULL);
1655 _krb5_pk_allow_proxy_certificate(kdc_identity, ret);
1657 file = krb5_config_get_string(context,
1658 NULL,
1659 "kdc",
1660 "pkinit_mappings_file",
1661 NULL);
1662 if (file == NULL) {
1663 asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
1664 file = fn;
1667 load_mappings(context, file);
1668 if (fn)
1669 free(fn);
1671 return 0;
1674 #endif /* PKINIT */