etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / kdc / pkinit.c
blob348909ec08aaa4714b35b066704e0724a9779867
1 /* $NetBSD: pkinit.c,v 1.1.1.2 2014/04/24 12:45:27 pettai Exp $ */
3 /*
4 * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "kdc_locl.h"
40 #ifdef PKINIT
42 #include <krb5/heim_asn1.h>
43 #include <krb5/rfc2459_asn1.h>
44 #include <krb5/cms_asn1.h>
45 #include <krb5/pkinit_asn1.h>
47 #include <krb5/hx509.h>
48 #include "crypto-headers.h"
50 struct pk_client_params {
51 enum krb5_pk_type type;
52 enum { USE_RSA, USE_DH, USE_ECDH } keyex;
53 union {
54 struct {
55 BIGNUM *public_key;
56 DH *key;
57 } dh;
58 #ifdef HAVE_OPENSSL
59 struct {
60 EC_KEY *public_key;
61 EC_KEY *key;
62 } ecdh;
63 #endif
64 } u;
65 hx509_cert cert;
66 unsigned nonce;
67 EncryptionKey reply_key;
68 char *dh_group_name;
69 hx509_peer_info peer;
70 hx509_certs client_anchors;
71 hx509_verify_ctx verify_ctx;
74 struct pk_principal_mapping {
75 unsigned int len;
76 struct pk_allowed_princ {
77 krb5_principal principal;
78 char *subject;
79 } *val;
82 static struct krb5_pk_identity *kdc_identity;
83 static struct pk_principal_mapping principal_mappings;
84 static struct krb5_dh_moduli **moduli;
86 static struct {
87 krb5_data data;
88 time_t expire;
89 time_t next_update;
90 } ocsp;
96 static krb5_error_code
97 pk_check_pkauthenticator_win2k(krb5_context context,
98 PKAuthenticator_Win2k *a,
99 const KDC_REQ *req)
101 krb5_timestamp now;
103 krb5_timeofday (context, &now);
105 /* XXX cusec */
106 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
107 krb5_clear_error_message(context);
108 return KRB5KRB_AP_ERR_SKEW;
110 return 0;
113 static krb5_error_code
114 pk_check_pkauthenticator(krb5_context context,
115 PKAuthenticator *a,
116 const KDC_REQ *req)
118 u_char *buf = NULL;
119 size_t buf_size;
120 krb5_error_code ret;
121 size_t len = 0;
122 krb5_timestamp now;
123 Checksum checksum;
125 krb5_timeofday (context, &now);
127 /* XXX cusec */
128 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
129 krb5_clear_error_message(context);
130 return KRB5KRB_AP_ERR_SKEW;
133 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
134 if (ret) {
135 krb5_clear_error_message(context);
136 return ret;
138 if (buf_size != len)
139 krb5_abortx(context, "Internal error in ASN.1 encoder");
141 ret = krb5_create_checksum(context,
142 NULL,
144 CKSUMTYPE_SHA1,
145 buf,
146 len,
147 &checksum);
148 free(buf);
149 if (ret) {
150 krb5_clear_error_message(context);
151 return ret;
154 if (a->paChecksum == NULL) {
155 krb5_clear_error_message(context);
156 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
157 goto out;
160 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
161 krb5_clear_error_message(context);
162 ret = KRB5KRB_ERR_GENERIC;
165 out:
166 free_Checksum(&checksum);
168 return ret;
171 void
172 _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
174 if (cp == NULL)
175 return;
176 if (cp->cert)
177 hx509_cert_free(cp->cert);
178 if (cp->verify_ctx)
179 hx509_verify_destroy_ctx(cp->verify_ctx);
180 if (cp->keyex == USE_DH) {
181 if (cp->u.dh.key)
182 DH_free(cp->u.dh.key);
183 if (cp->u.dh.public_key)
184 BN_free(cp->u.dh.public_key);
186 #ifdef HAVE_OPENSSL
187 if (cp->keyex == USE_ECDH) {
188 if (cp->u.ecdh.key)
189 EC_KEY_free(cp->u.ecdh.key);
190 if (cp->u.ecdh.public_key)
191 EC_KEY_free(cp->u.ecdh.public_key);
193 #endif
194 krb5_free_keyblock_contents(context, &cp->reply_key);
195 if (cp->dh_group_name)
196 free(cp->dh_group_name);
197 if (cp->peer)
198 hx509_peer_info_free(cp->peer);
199 if (cp->client_anchors)
200 hx509_certs_free(&cp->client_anchors);
201 memset(cp, 0, sizeof(*cp));
202 free(cp);
205 static krb5_error_code
206 generate_dh_keyblock(krb5_context context,
207 pk_client_params *client_params,
208 krb5_enctype enctype)
210 unsigned char *dh_gen_key = NULL;
211 krb5_keyblock key;
212 krb5_error_code ret;
213 size_t dh_gen_keylen, size;
215 memset(&key, 0, sizeof(key));
217 if (client_params->keyex == USE_DH) {
219 if (client_params->u.dh.public_key == NULL) {
220 ret = KRB5KRB_ERR_GENERIC;
221 krb5_set_error_message(context, ret, "public_key");
222 goto out;
225 if (!DH_generate_key(client_params->u.dh.key)) {
226 ret = KRB5KRB_ERR_GENERIC;
227 krb5_set_error_message(context, ret,
228 "Can't generate Diffie-Hellman keys");
229 goto out;
232 size = DH_size(client_params->u.dh.key);
234 dh_gen_key = malloc(size);
235 if (dh_gen_key == NULL) {
236 ret = ENOMEM;
237 krb5_set_error_message(context, ret, "malloc: out of memory");
238 goto out;
241 dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
242 if (dh_gen_keylen == (size_t)-1) {
243 ret = KRB5KRB_ERR_GENERIC;
244 krb5_set_error_message(context, ret,
245 "Can't compute Diffie-Hellman key");
246 goto out;
248 if (dh_gen_keylen < size) {
249 size -= dh_gen_keylen;
250 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
251 memset(dh_gen_key, 0, size);
254 ret = 0;
255 #ifdef HAVE_OPENSSL
256 } else if (client_params->keyex == USE_ECDH) {
258 if (client_params->u.ecdh.public_key == NULL) {
259 ret = KRB5KRB_ERR_GENERIC;
260 krb5_set_error_message(context, ret, "public_key");
261 goto out;
264 client_params->u.ecdh.key = EC_KEY_new();
265 if (client_params->u.ecdh.key == NULL) {
266 ret = ENOMEM;
267 goto out;
269 EC_KEY_set_group(client_params->u.ecdh.key,
270 EC_KEY_get0_group(client_params->u.ecdh.public_key));
272 if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) {
273 ret = ENOMEM;
274 goto out;
277 size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8;
278 dh_gen_key = malloc(size);
279 if (dh_gen_key == NULL) {
280 ret = ENOMEM;
281 krb5_set_error_message(context, ret,
282 N_("malloc: out of memory", ""));
283 goto out;
286 dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
287 EC_KEY_get0_public_key(client_params->u.ecdh.public_key),
288 client_params->u.ecdh.key, NULL);
290 #endif /* HAVE_OPENSSL */
291 } else {
292 ret = KRB5KRB_ERR_GENERIC;
293 krb5_set_error_message(context, ret,
294 "Diffie-Hellman not selected keys");
295 goto out;
298 ret = _krb5_pk_octetstring2key(context,
299 enctype,
300 dh_gen_key, dh_gen_keylen,
301 NULL, NULL,
302 &client_params->reply_key);
304 out:
305 if (dh_gen_key)
306 free(dh_gen_key);
307 if (key.keyvalue.data)
308 krb5_free_keyblock_contents(context, &key);
310 return ret;
313 static BIGNUM *
314 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
316 BIGNUM *bn;
318 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
319 if (bn == NULL) {
320 krb5_set_error_message(context, KRB5_BADMSGTYPE,
321 "PKINIT: parsing BN failed %s", field);
322 return NULL;
324 BN_set_negative(bn, f->negative);
325 return bn;
328 static krb5_error_code
329 get_dh_param(krb5_context context,
330 krb5_kdc_configuration *config,
331 SubjectPublicKeyInfo *dh_key_info,
332 pk_client_params *client_params)
334 DomainParameters dhparam;
335 DH *dh = NULL;
336 krb5_error_code ret;
338 memset(&dhparam, 0, sizeof(dhparam));
340 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
341 ret = KRB5_BADMSGTYPE;
342 krb5_set_error_message(context, ret,
343 "PKINIT: subjectPublicKey not aligned "
344 "to 8 bit boundary");
345 goto out;
348 if (dh_key_info->algorithm.parameters == NULL) {
349 krb5_set_error_message(context, KRB5_BADMSGTYPE,
350 "PKINIT missing algorithm parameter "
351 "in clientPublicValue");
352 return KRB5_BADMSGTYPE;
355 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
356 dh_key_info->algorithm.parameters->length,
357 &dhparam,
358 NULL);
359 if (ret) {
360 krb5_set_error_message(context, ret, "Can't decode algorithm "
361 "parameters in clientPublicValue");
362 goto out;
365 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
366 &dhparam.p, &dhparam.g, dhparam.q, moduli,
367 &client_params->dh_group_name);
368 if (ret) {
369 /* XXX send back proposal of better group */
370 goto out;
373 dh = DH_new();
374 if (dh == NULL) {
375 ret = ENOMEM;
376 krb5_set_error_message(context, ret, "Cannot create DH structure");
377 goto out;
379 ret = KRB5_BADMSGTYPE;
380 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
381 if (dh->p == NULL)
382 goto out;
383 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
384 if (dh->g == NULL)
385 goto out;
387 if (dhparam.q) {
388 dh->q = integer_to_BN(context, "DH p-1 factor", dhparam.q);
389 if (dh->g == NULL)
390 goto out;
394 heim_integer glue;
395 size_t size;
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;
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;
416 client_params->u.dh.key = dh;
417 dh = NULL;
418 ret = 0;
420 out:
421 if (dh)
422 DH_free(dh);
423 free_DomainParameters(&dhparam);
424 return ret;
427 #ifdef HAVE_OPENSSL
429 static krb5_error_code
430 get_ecdh_param(krb5_context context,
431 krb5_kdc_configuration *config,
432 SubjectPublicKeyInfo *dh_key_info,
433 pk_client_params *client_params)
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;
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;
449 memset(&ecp, 0, sizeof(ecp));
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;
456 if (ecp.element != choice_ECParameters_namedCurve) {
457 ret = KRB5_BADMSGTYPE;
458 goto out;
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;
468 /* XXX verify group is ok */
470 public = EC_KEY_new_by_curve_name(nid);
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;
480 client_params->u.ecdh.public_key = public;
481 public = NULL;
483 out:
484 if (public)
485 EC_KEY_free(public);
486 free_ECParameters(&ecp);
487 return ret;
490 #endif /* HAVE_OPENSSL */
492 krb5_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)
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;
510 *ret_params = NULL;
512 if (!config->enable_pkinit) {
513 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
514 krb5_clear_error_message(context);
515 return 0;
518 cp = calloc(1, sizeof(*cp));
519 if (cp == NULL) {
520 krb5_clear_error_message(context);
521 ret = ENOMEM;
522 goto out;
525 ret = hx509_certs_init(context->hx509ctx,
526 "MEMORY:trust-anchors",
527 0, NULL, &trust_anchors);
528 if (ret) {
529 krb5_set_error_message(context, ret, "failed to create trust anchors");
530 goto out;
533 ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
534 kdc_identity->anchors);
535 if (ret) {
536 hx509_certs_free(&trust_anchors);
537 krb5_set_error_message(context, ret, "failed to create verify context");
538 goto out;
541 /* Add any registered certificates for this client as trust anchors */
542 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
543 if (ret == 0 && pc != NULL) {
544 hx509_cert cert;
545 unsigned int i;
547 for (i = 0; i < pc->len; i++) {
548 ret = hx509_cert_init_data(context->hx509ctx,
549 pc->val[i].cert.data,
550 pc->val[i].cert.length,
551 &cert);
552 if (ret)
553 continue;
554 hx509_certs_add(context->hx509ctx, trust_anchors, cert);
555 hx509_cert_free(cert);
559 ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
560 if (ret) {
561 hx509_certs_free(&trust_anchors);
562 krb5_set_error_message(context, ret, "failed to create verify context");
563 goto out;
566 hx509_verify_set_time(cp->verify_ctx, kdc_time);
567 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
568 hx509_certs_free(&trust_anchors);
570 if (config->pkinit_allow_proxy_certs)
571 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
573 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
574 PA_PK_AS_REQ_Win2k r;
576 type = "PK-INIT-Win2k";
578 if (req->req_body.kdc_options.request_anonymous) {
579 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
580 krb5_set_error_message(context, ret,
581 "Anon not supported in RSA mode");
582 goto out;
585 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
586 pa->padata_value.length,
588 NULL);
589 if (ret) {
590 krb5_set_error_message(context, ret, "Can't decode "
591 "PK-AS-REQ-Win2k: %d", ret);
592 goto out;
595 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
596 &contentInfoOid,
597 &signed_content,
598 &have_data);
599 free_PA_PK_AS_REQ_Win2k(&r);
600 if (ret) {
601 krb5_set_error_message(context, ret,
602 "Can't unwrap ContentInfo(win): %d", ret);
603 goto out;
606 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
607 PA_PK_AS_REQ r;
609 type = "PK-INIT-IETF";
611 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
612 pa->padata_value.length,
614 NULL);
615 if (ret) {
616 krb5_set_error_message(context, ret,
617 "Can't decode PK-AS-REQ: %d", ret);
618 goto out;
621 /* XXX look at r.kdcPkId */
622 if (r.trustedCertifiers) {
623 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
624 unsigned int i, maxedi;
626 ret = hx509_certs_init(context->hx509ctx,
627 "MEMORY:client-anchors",
628 0, NULL,
629 &cp->client_anchors);
630 if (ret) {
631 krb5_set_error_message(context, ret,
632 "Can't allocate client anchors: %d",
633 ret);
634 goto out;
638 * If the client sent more then 10 EDI, don't bother
639 * looking more then 10 of performance reasons.
641 maxedi = edi->len;
642 if (maxedi > 10)
643 maxedi = 10;
644 for (i = 0; i < maxedi; i++) {
645 IssuerAndSerialNumber iasn;
646 hx509_query *q;
647 hx509_cert cert;
648 size_t size;
650 if (edi->val[i].issuerAndSerialNumber == NULL)
651 continue;
653 ret = hx509_query_alloc(context->hx509ctx, &q);
654 if (ret) {
655 krb5_set_error_message(context, ret,
656 "Failed to allocate hx509_query");
657 goto out;
660 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
661 edi->val[i].issuerAndSerialNumber->length,
662 &iasn,
663 &size);
664 if (ret) {
665 hx509_query_free(context->hx509ctx, q);
666 continue;
668 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
669 free_IssuerAndSerialNumber(&iasn);
670 if (ret) {
671 hx509_query_free(context->hx509ctx, q);
672 continue;
675 ret = hx509_certs_find(context->hx509ctx,
676 kdc_identity->certs,
678 &cert);
679 hx509_query_free(context->hx509ctx, q);
680 if (ret)
681 continue;
682 hx509_certs_add(context->hx509ctx,
683 cp->client_anchors, cert);
684 hx509_cert_free(cert);
688 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
689 &contentInfoOid,
690 &signed_content,
691 &have_data);
692 free_PA_PK_AS_REQ(&r);
693 if (ret) {
694 krb5_set_error_message(context, ret,
695 "Can't unwrap ContentInfo: %d", ret);
696 goto out;
699 } else {
700 krb5_clear_error_message(context);
701 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
702 goto out;
705 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
706 if (ret != 0) {
707 ret = KRB5KRB_ERR_GENERIC;
708 krb5_set_error_message(context, ret,
709 "PK-AS-REQ-Win2k invalid content type oid");
710 goto out;
713 if (!have_data) {
714 ret = KRB5KRB_ERR_GENERIC;
715 krb5_set_error_message(context, ret,
716 "PK-AS-REQ-Win2k no signed auth pack");
717 goto out;
721 hx509_certs signer_certs;
722 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
724 if (req->req_body.kdc_options.request_anonymous)
725 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
727 ret = hx509_cms_verify_signed(context->hx509ctx,
728 cp->verify_ctx,
729 flags,
730 signed_content.data,
731 signed_content.length,
732 NULL,
733 kdc_identity->certpool,
734 &eContentType,
735 &eContent,
736 &signer_certs);
737 if (ret) {
738 char *s = hx509_get_error_string(context->hx509ctx, ret);
739 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
740 s, ret);
741 free(s);
742 goto out;
745 if (signer_certs) {
746 ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
747 &cp->cert);
748 hx509_certs_free(&signer_certs);
750 if (ret)
751 goto out;
754 /* Signature is correct, now verify the signed message */
755 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
756 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
758 ret = KRB5_BADMSGTYPE;
759 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
760 goto out;
763 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
764 AuthPack_Win2k ap;
766 ret = decode_AuthPack_Win2k(eContent.data,
767 eContent.length,
768 &ap,
769 NULL);
770 if (ret) {
771 krb5_set_error_message(context, ret,
772 "Can't decode AuthPack: %d", ret);
773 goto out;
776 ret = pk_check_pkauthenticator_win2k(context,
777 &ap.pkAuthenticator,
778 req);
779 if (ret) {
780 free_AuthPack_Win2k(&ap);
781 goto out;
784 cp->type = PKINIT_WIN2K;
785 cp->nonce = ap.pkAuthenticator.nonce;
787 if (ap.clientPublicValue) {
788 ret = KRB5KRB_ERR_GENERIC;
789 krb5_set_error_message(context, ret,
790 "DH not supported for windows");
791 goto out;
793 free_AuthPack_Win2k(&ap);
795 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
796 AuthPack ap;
798 ret = decode_AuthPack(eContent.data,
799 eContent.length,
800 &ap,
801 NULL);
802 if (ret) {
803 krb5_set_error_message(context, ret,
804 "Can't decode AuthPack: %d", ret);
805 free_AuthPack(&ap);
806 goto out;
809 if (req->req_body.kdc_options.request_anonymous &&
810 ap.clientPublicValue == NULL) {
811 free_AuthPack(&ap);
812 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
813 krb5_set_error_message(context, ret,
814 "Anon not supported in RSA mode");
815 goto out;
818 ret = pk_check_pkauthenticator(context,
819 &ap.pkAuthenticator,
820 req);
821 if (ret) {
822 free_AuthPack(&ap);
823 goto out;
826 cp->type = PKINIT_27;
827 cp->nonce = ap.pkAuthenticator.nonce;
829 if (ap.clientPublicValue) {
830 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
831 cp->keyex = USE_DH;
832 ret = get_dh_param(context, config,
833 ap.clientPublicValue, cp);
834 #ifdef HAVE_OPENSSL
835 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
836 cp->keyex = USE_ECDH;
837 ret = get_ecdh_param(context, config,
838 ap.clientPublicValue, cp);
839 #endif /* HAVE_OPENSSL */
840 } else {
841 ret = KRB5_BADMSGTYPE;
842 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
844 if (ret) {
845 free_AuthPack(&ap);
846 goto out;
848 } else
849 cp->keyex = USE_RSA;
851 ret = hx509_peer_info_alloc(context->hx509ctx,
852 &cp->peer);
853 if (ret) {
854 free_AuthPack(&ap);
855 goto out;
858 if (ap.supportedCMSTypes) {
859 ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
860 cp->peer,
861 ap.supportedCMSTypes->val,
862 ap.supportedCMSTypes->len);
863 if (ret) {
864 free_AuthPack(&ap);
865 goto out;
867 } else {
868 /* assume old client */
869 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
870 hx509_crypto_des_rsdi_ede3_cbc());
871 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
872 hx509_signature_rsa_with_sha1());
873 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
874 hx509_signature_sha1());
876 free_AuthPack(&ap);
877 } else
878 krb5_abortx(context, "internal pkinit error");
880 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
882 out:
883 if (ret)
884 krb5_warn(context, ret, "PKINIT");
886 if (signed_content.data)
887 free(signed_content.data);
888 krb5_data_free(&eContent);
889 der_free_oid(&eContentType);
890 der_free_oid(&contentInfoOid);
891 if (ret) {
892 _kdc_pk_free_client_param(context, cp);
893 } else
894 *ret_params = cp;
895 return ret;
902 static krb5_error_code
903 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
905 integer->length = BN_num_bytes(bn);
906 integer->data = malloc(integer->length);
907 if (integer->data == NULL) {
908 krb5_clear_error_message(context);
909 return ENOMEM;
911 BN_bn2bin(bn, integer->data);
912 integer->negative = BN_is_negative(bn);
913 return 0;
916 static krb5_error_code
917 pk_mk_pa_reply_enckey(krb5_context context,
918 krb5_kdc_configuration *config,
919 pk_client_params *cp,
920 const KDC_REQ *req,
921 const krb5_data *req_buffer,
922 krb5_keyblock *reply_key,
923 ContentInfo *content_info,
924 hx509_cert *kdc_cert)
926 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
927 krb5_error_code ret;
928 krb5_data buf, signed_data;
929 size_t size = 0;
930 int do_win2k = 0;
932 krb5_data_zero(&buf);
933 krb5_data_zero(&signed_data);
935 *kdc_cert = NULL;
938 * If the message client is a win2k-type but it send pa data
939 * 09-binding it expects a IETF (checksum) reply so there can be
940 * no replay attacks.
943 switch (cp->type) {
944 case PKINIT_WIN2K: {
945 int i = 0;
946 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
947 && config->pkinit_require_binding == 0)
949 do_win2k = 1;
951 sdAlg = &asn1_oid_id_pkcs7_data;
952 evAlg = &asn1_oid_id_pkcs7_data;
953 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
954 break;
956 case PKINIT_27:
957 sdAlg = &asn1_oid_id_pkrkeydata;
958 evAlg = &asn1_oid_id_pkcs7_signedData;
959 break;
960 default:
961 krb5_abortx(context, "internal pkinit error");
964 if (do_win2k) {
965 ReplyKeyPack_Win2k kp;
966 memset(&kp, 0, sizeof(kp));
968 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
969 if (ret) {
970 krb5_clear_error_message(context);
971 goto out;
973 kp.nonce = cp->nonce;
975 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
976 buf.data, buf.length,
977 &kp, &size,ret);
978 free_ReplyKeyPack_Win2k(&kp);
979 } else {
980 krb5_crypto ascrypto;
981 ReplyKeyPack kp;
982 memset(&kp, 0, sizeof(kp));
984 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
985 if (ret) {
986 krb5_clear_error_message(context);
987 goto out;
990 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
991 if (ret) {
992 krb5_clear_error_message(context);
993 goto out;
996 ret = krb5_create_checksum(context, ascrypto, 6, 0,
997 req_buffer->data, req_buffer->length,
998 &kp.asChecksum);
999 if (ret) {
1000 krb5_clear_error_message(context);
1001 goto out;
1004 ret = krb5_crypto_destroy(context, ascrypto);
1005 if (ret) {
1006 krb5_clear_error_message(context);
1007 goto out;
1009 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
1010 free_ReplyKeyPack(&kp);
1012 if (ret) {
1013 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
1014 "failed (%d)", ret);
1015 goto out;
1017 if (buf.length != size)
1018 krb5_abortx(context, "Internal ASN.1 encoder error");
1021 hx509_query *q;
1022 hx509_cert cert;
1024 ret = hx509_query_alloc(context->hx509ctx, &q);
1025 if (ret)
1026 goto out;
1028 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1029 if (config->pkinit_kdc_friendly_name)
1030 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1032 ret = hx509_certs_find(context->hx509ctx,
1033 kdc_identity->certs,
1035 &cert);
1036 hx509_query_free(context->hx509ctx, q);
1037 if (ret)
1038 goto out;
1040 ret = hx509_cms_create_signed_1(context->hx509ctx,
1042 sdAlg,
1043 buf.data,
1044 buf.length,
1045 NULL,
1046 cert,
1047 cp->peer,
1048 cp->client_anchors,
1049 kdc_identity->certpool,
1050 &signed_data);
1051 *kdc_cert = cert;
1054 krb5_data_free(&buf);
1055 if (ret)
1056 goto out;
1058 if (cp->type == PKINIT_WIN2K) {
1059 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
1060 &signed_data,
1061 &buf);
1062 if (ret)
1063 goto out;
1064 krb5_data_free(&signed_data);
1065 signed_data = buf;
1068 ret = hx509_cms_envelope_1(context->hx509ctx,
1069 HX509_CMS_EV_NO_KU_CHECK,
1070 cp->cert,
1071 signed_data.data, signed_data.length,
1072 envelopedAlg,
1073 evAlg, &buf);
1074 if (ret)
1075 goto out;
1077 ret = _krb5_pk_mk_ContentInfo(context,
1078 &buf,
1079 &asn1_oid_id_pkcs7_envelopedData,
1080 content_info);
1081 out:
1082 if (ret && *kdc_cert) {
1083 hx509_cert_free(*kdc_cert);
1084 *kdc_cert = NULL;
1087 krb5_data_free(&buf);
1088 krb5_data_free(&signed_data);
1089 return ret;
1096 static krb5_error_code
1097 pk_mk_pa_reply_dh(krb5_context context,
1098 krb5_kdc_configuration *config,
1099 pk_client_params *cp,
1100 ContentInfo *content_info,
1101 hx509_cert *kdc_cert)
1103 KDCDHKeyInfo dh_info;
1104 krb5_data signed_data, buf;
1105 ContentInfo contentinfo;
1106 krb5_error_code ret;
1107 hx509_cert cert;
1108 hx509_query *q;
1109 size_t size = 0;
1111 memset(&contentinfo, 0, sizeof(contentinfo));
1112 memset(&dh_info, 0, sizeof(dh_info));
1113 krb5_data_zero(&signed_data);
1114 krb5_data_zero(&buf);
1116 *kdc_cert = NULL;
1118 if (cp->keyex == USE_DH) {
1119 DH *kdc_dh = cp->u.dh.key;
1120 heim_integer i;
1122 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1123 if (ret)
1124 return ret;
1126 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1127 der_free_heim_integer(&i);
1128 if (ret) {
1129 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1130 "DHPublicKey failed (%d)", ret);
1131 return ret;
1133 if (buf.length != size)
1134 krb5_abortx(context, "Internal ASN.1 encoder error");
1136 dh_info.subjectPublicKey.length = buf.length * 8;
1137 dh_info.subjectPublicKey.data = buf.data;
1138 krb5_data_zero(&buf);
1139 #ifdef HAVE_OPENSSL
1140 } else if (cp->keyex == USE_ECDH) {
1141 unsigned char *p;
1142 int len;
1144 len = i2o_ECPublicKey(cp->u.ecdh.key, NULL);
1145 if (len <= 0)
1146 abort();
1148 p = malloc(len);
1149 if (p == NULL)
1150 abort();
1152 dh_info.subjectPublicKey.length = len * 8;
1153 dh_info.subjectPublicKey.data = p;
1155 len = i2o_ECPublicKey(cp->u.ecdh.key, &p);
1156 if (len <= 0)
1157 abort();
1158 #endif
1159 } else
1160 krb5_abortx(context, "no keyex selected ?");
1163 dh_info.nonce = cp->nonce;
1165 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1166 ret);
1167 if (ret) {
1168 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1169 "KdcDHKeyInfo failed (%d)", ret);
1170 goto out;
1172 if (buf.length != size)
1173 krb5_abortx(context, "Internal ASN.1 encoder error");
1176 * Create the SignedData structure and sign the KdcDHKeyInfo
1177 * filled in above
1180 ret = hx509_query_alloc(context->hx509ctx, &q);
1181 if (ret)
1182 goto out;
1184 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1185 if (config->pkinit_kdc_friendly_name)
1186 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1188 ret = hx509_certs_find(context->hx509ctx,
1189 kdc_identity->certs,
1191 &cert);
1192 hx509_query_free(context->hx509ctx, q);
1193 if (ret)
1194 goto out;
1196 ret = hx509_cms_create_signed_1(context->hx509ctx,
1198 &asn1_oid_id_pkdhkeydata,
1199 buf.data,
1200 buf.length,
1201 NULL,
1202 cert,
1203 cp->peer,
1204 cp->client_anchors,
1205 kdc_identity->certpool,
1206 &signed_data);
1207 if (ret) {
1208 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1209 goto out;
1211 *kdc_cert = cert;
1213 ret = _krb5_pk_mk_ContentInfo(context,
1214 &signed_data,
1215 &asn1_oid_id_pkcs7_signedData,
1216 content_info);
1217 if (ret)
1218 goto out;
1220 out:
1221 if (ret && *kdc_cert) {
1222 hx509_cert_free(*kdc_cert);
1223 *kdc_cert = NULL;
1226 krb5_data_free(&buf);
1227 krb5_data_free(&signed_data);
1228 free_KDCDHKeyInfo(&dh_info);
1230 return ret;
1237 krb5_error_code
1238 _kdc_pk_mk_pa_reply(krb5_context context,
1239 krb5_kdc_configuration *config,
1240 pk_client_params *cp,
1241 const hdb_entry_ex *client,
1242 krb5_enctype sessionetype,
1243 const KDC_REQ *req,
1244 const krb5_data *req_buffer,
1245 krb5_keyblock **reply_key,
1246 krb5_keyblock *sessionkey,
1247 METHOD_DATA *md)
1249 krb5_error_code ret;
1250 void *buf = NULL;
1251 size_t len = 0, size = 0;
1252 krb5_enctype enctype;
1253 int pa_type;
1254 hx509_cert kdc_cert = NULL;
1255 size_t i;
1257 if (!config->enable_pkinit) {
1258 krb5_clear_error_message(context);
1259 return 0;
1262 if (req->req_body.etype.len > 0) {
1263 for (i = 0; i < req->req_body.etype.len; i++)
1264 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1265 break;
1266 if (req->req_body.etype.len <= i) {
1267 ret = KRB5KRB_ERR_GENERIC;
1268 krb5_set_error_message(context, ret,
1269 "No valid enctype available from client");
1270 goto out;
1272 enctype = req->req_body.etype.val[i];
1273 } else
1274 enctype = ETYPE_DES3_CBC_SHA1;
1276 if (cp->type == PKINIT_27) {
1277 PA_PK_AS_REP rep;
1278 const char *type, *other = "";
1280 memset(&rep, 0, sizeof(rep));
1282 pa_type = KRB5_PADATA_PK_AS_REP;
1284 if (cp->keyex == USE_RSA) {
1285 ContentInfo info;
1287 type = "enckey";
1289 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1291 ret = krb5_generate_random_keyblock(context, enctype,
1292 &cp->reply_key);
1293 if (ret) {
1294 free_PA_PK_AS_REP(&rep);
1295 goto out;
1297 ret = pk_mk_pa_reply_enckey(context,
1298 config,
1300 req,
1301 req_buffer,
1302 &cp->reply_key,
1303 &info,
1304 &kdc_cert);
1305 if (ret) {
1306 free_PA_PK_AS_REP(&rep);
1307 goto out;
1309 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1310 rep.u.encKeyPack.length, &info, &size,
1311 ret);
1312 free_ContentInfo(&info);
1313 if (ret) {
1314 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1315 "failed %d", ret);
1316 free_PA_PK_AS_REP(&rep);
1317 goto out;
1319 if (rep.u.encKeyPack.length != size)
1320 krb5_abortx(context, "Internal ASN.1 encoder error");
1322 ret = krb5_generate_random_keyblock(context, sessionetype,
1323 sessionkey);
1324 if (ret) {
1325 free_PA_PK_AS_REP(&rep);
1326 goto out;
1329 } else {
1330 ContentInfo info;
1332 switch (cp->keyex) {
1333 case USE_DH: type = "dh"; break;
1334 #ifdef HAVE_OPENSSL
1335 case USE_ECDH: type = "ecdh"; break;
1336 #endif
1337 default: krb5_abortx(context, "unknown keyex"); break;
1340 if (cp->dh_group_name)
1341 other = cp->dh_group_name;
1343 rep.element = choice_PA_PK_AS_REP_dhInfo;
1345 ret = generate_dh_keyblock(context, cp, enctype);
1346 if (ret)
1347 return ret;
1349 ret = pk_mk_pa_reply_dh(context, config,
1351 &info,
1352 &kdc_cert);
1353 if (ret) {
1354 free_PA_PK_AS_REP(&rep);
1355 krb5_set_error_message(context, ret,
1356 "create pa-reply-dh "
1357 "failed %d", ret);
1358 goto out;
1361 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1362 rep.u.dhInfo.dhSignedData.length, &info, &size,
1363 ret);
1364 free_ContentInfo(&info);
1365 if (ret) {
1366 krb5_set_error_message(context, ret,
1367 "encoding of Key ContentInfo "
1368 "failed %d", ret);
1369 free_PA_PK_AS_REP(&rep);
1370 goto out;
1372 if (rep.u.encKeyPack.length != size)
1373 krb5_abortx(context, "Internal ASN.1 encoder error");
1375 /* XXX KRB-FX-CF2 */
1376 ret = krb5_generate_random_keyblock(context, sessionetype,
1377 sessionkey);
1378 if (ret) {
1379 free_PA_PK_AS_REP(&rep);
1380 goto out;
1383 /* XXX Add PA-PKINIT-KX */
1387 #define use_btmm_with_enckey 0
1388 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1389 PA_PK_AS_REP_BTMM btmm;
1390 heim_any any;
1392 any.data = rep.u.encKeyPack.data;
1393 any.length = rep.u.encKeyPack.length;
1395 btmm.dhSignedData = NULL;
1396 btmm.encKeyPack = &any;
1398 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1399 } else {
1400 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1403 free_PA_PK_AS_REP(&rep);
1404 if (ret) {
1405 krb5_set_error_message(context, ret,
1406 "encode PA-PK-AS-REP failed %d", ret);
1407 goto out;
1409 if (len != size)
1410 krb5_abortx(context, "Internal ASN.1 encoder error");
1412 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1414 } else if (cp->type == PKINIT_WIN2K) {
1415 PA_PK_AS_REP_Win2k rep;
1416 ContentInfo info;
1418 if (cp->keyex != USE_RSA) {
1419 ret = KRB5KRB_ERR_GENERIC;
1420 krb5_set_error_message(context, ret,
1421 "Windows PK-INIT doesn't support DH");
1422 goto out;
1425 memset(&rep, 0, sizeof(rep));
1427 pa_type = KRB5_PADATA_PK_AS_REP_19;
1428 rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1430 ret = krb5_generate_random_keyblock(context, enctype,
1431 &cp->reply_key);
1432 if (ret) {
1433 free_PA_PK_AS_REP_Win2k(&rep);
1434 goto out;
1436 ret = pk_mk_pa_reply_enckey(context,
1437 config,
1439 req,
1440 req_buffer,
1441 &cp->reply_key,
1442 &info,
1443 &kdc_cert);
1444 if (ret) {
1445 free_PA_PK_AS_REP_Win2k(&rep);
1446 goto out;
1448 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1449 rep.u.encKeyPack.length, &info, &size,
1450 ret);
1451 free_ContentInfo(&info);
1452 if (ret) {
1453 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1454 "failed %d", ret);
1455 free_PA_PK_AS_REP_Win2k(&rep);
1456 goto out;
1458 if (rep.u.encKeyPack.length != size)
1459 krb5_abortx(context, "Internal ASN.1 encoder error");
1461 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1462 free_PA_PK_AS_REP_Win2k(&rep);
1463 if (ret) {
1464 krb5_set_error_message(context, ret,
1465 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1466 goto out;
1468 if (len != size)
1469 krb5_abortx(context, "Internal ASN.1 encoder error");
1471 ret = krb5_generate_random_keyblock(context, sessionetype,
1472 sessionkey);
1473 if (ret) {
1474 free(buf);
1475 goto out;
1478 } else
1479 krb5_abortx(context, "PK-INIT internal error");
1482 ret = krb5_padata_add(context, md, pa_type, buf, len);
1483 if (ret) {
1484 krb5_set_error_message(context, ret,
1485 "Failed adding PA-PK-AS-REP %d", ret);
1486 free(buf);
1487 goto out;
1490 if (config->pkinit_kdc_ocsp_file) {
1492 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1493 struct stat sb;
1494 int fd;
1496 krb5_data_free(&ocsp.data);
1498 ocsp.expire = 0;
1499 ocsp.next_update = kdc_time + 60 * 5;
1501 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1502 if (fd < 0) {
1503 kdc_log(context, config, 0,
1504 "PK-INIT failed to open ocsp data file %d", errno);
1505 goto out_ocsp;
1507 ret = fstat(fd, &sb);
1508 if (ret) {
1509 ret = errno;
1510 close(fd);
1511 kdc_log(context, config, 0,
1512 "PK-INIT failed to stat ocsp data %d", ret);
1513 goto out_ocsp;
1516 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1517 if (ret) {
1518 close(fd);
1519 kdc_log(context, config, 0,
1520 "PK-INIT failed to stat ocsp data %d", ret);
1521 goto out_ocsp;
1523 ocsp.data.length = sb.st_size;
1524 ret = read(fd, ocsp.data.data, sb.st_size);
1525 close(fd);
1526 if (ret != sb.st_size) {
1527 kdc_log(context, config, 0,
1528 "PK-INIT failed to read ocsp data %d", errno);
1529 goto out_ocsp;
1532 ret = hx509_ocsp_verify(context->hx509ctx,
1533 kdc_time,
1534 kdc_cert,
1536 ocsp.data.data, ocsp.data.length,
1537 &ocsp.expire);
1538 if (ret) {
1539 kdc_log(context, config, 0,
1540 "PK-INIT failed to verify ocsp data %d", ret);
1541 krb5_data_free(&ocsp.data);
1542 ocsp.expire = 0;
1543 } else if (ocsp.expire > 180) {
1544 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1545 ocsp.next_update = ocsp.expire;
1546 } else {
1547 ocsp.next_update = kdc_time;
1549 out_ocsp:
1550 ret = 0;
1553 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1555 ret = krb5_padata_add(context, md,
1556 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1557 ocsp.data.data, ocsp.data.length);
1558 if (ret) {
1559 krb5_set_error_message(context, ret,
1560 "Failed adding OCSP response %d", ret);
1561 goto out;
1566 out:
1567 if (kdc_cert)
1568 hx509_cert_free(kdc_cert);
1570 if (ret == 0)
1571 *reply_key = &cp->reply_key;
1572 return ret;
1575 static int
1576 match_rfc_san(krb5_context context,
1577 krb5_kdc_configuration *config,
1578 hx509_context hx509ctx,
1579 hx509_cert client_cert,
1580 krb5_const_principal match)
1582 hx509_octet_string_list list;
1583 int ret, found = 0;
1584 size_t i;
1586 memset(&list, 0 , sizeof(list));
1588 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1589 client_cert,
1590 &asn1_oid_id_pkinit_san,
1591 &list);
1592 if (ret)
1593 goto out;
1595 for (i = 0; !found && i < list.len; i++) {
1596 krb5_principal_data principal;
1597 KRB5PrincipalName kn;
1598 size_t size;
1600 ret = decode_KRB5PrincipalName(list.val[i].data,
1601 list.val[i].length,
1602 &kn, &size);
1603 if (ret) {
1604 const char *msg = krb5_get_error_message(context, ret);
1605 kdc_log(context, config, 0,
1606 "Decoding kerberos name in certificate failed: %s", msg);
1607 krb5_free_error_message(context, msg);
1608 break;
1610 if (size != list.val[i].length) {
1611 kdc_log(context, config, 0,
1612 "Decoding kerberos name have extra bits on the end");
1613 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1616 principal.name = kn.principalName;
1617 principal.realm = kn.realm;
1619 if (krb5_principal_compare(context, &principal, match) == TRUE)
1620 found = 1;
1621 free_KRB5PrincipalName(&kn);
1624 out:
1625 hx509_free_octet_string_list(&list);
1626 if (ret)
1627 return ret;
1629 if (!found)
1630 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1632 return 0;
1635 static int
1636 match_ms_upn_san(krb5_context context,
1637 krb5_kdc_configuration *config,
1638 hx509_context hx509ctx,
1639 hx509_cert client_cert,
1640 HDB *clientdb,
1641 hdb_entry_ex *client)
1643 hx509_octet_string_list list;
1644 krb5_principal principal = NULL;
1645 int ret;
1646 MS_UPN_SAN upn;
1647 size_t size;
1649 memset(&list, 0 , sizeof(list));
1651 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1652 client_cert,
1653 &asn1_oid_id_pkinit_ms_san,
1654 &list);
1655 if (ret)
1656 goto out;
1658 if (list.len != 1) {
1659 kdc_log(context, config, 0,
1660 "More then one PK-INIT MS UPN SAN");
1661 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1662 goto out;
1665 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1666 if (ret) {
1667 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1668 goto out;
1670 if (size != list.val[0].length) {
1671 free_MS_UPN_SAN(&upn);
1672 kdc_log(context, config, 0, "Trailing data in ");
1673 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1674 goto out;
1677 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1679 ret = krb5_parse_name(context, upn, &principal);
1680 free_MS_UPN_SAN(&upn);
1681 if (ret) {
1682 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1683 goto out;
1686 if (clientdb->hdb_check_pkinit_ms_upn_match) {
1687 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1688 } else {
1691 * This is very wrong, but will do for a fallback
1693 strupr(principal->realm);
1695 if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1696 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1699 out:
1700 if (principal)
1701 krb5_free_principal(context, principal);
1702 hx509_free_octet_string_list(&list);
1704 return ret;
1707 krb5_error_code
1708 _kdc_pk_check_client(krb5_context context,
1709 krb5_kdc_configuration *config,
1710 HDB *clientdb,
1711 hdb_entry_ex *client,
1712 pk_client_params *cp,
1713 char **subject_name)
1715 const HDB_Ext_PKINIT_acl *acl;
1716 const HDB_Ext_PKINIT_cert *pc;
1717 krb5_error_code ret;
1718 hx509_name name;
1719 size_t i;
1721 if (cp->cert == NULL) {
1723 *subject_name = strdup("anonymous client client");
1724 if (*subject_name == NULL)
1725 return ENOMEM;
1726 return 0;
1729 ret = hx509_cert_get_base_subject(context->hx509ctx,
1730 cp->cert,
1731 &name);
1732 if (ret)
1733 return ret;
1735 ret = hx509_name_to_string(name, subject_name);
1736 hx509_name_free(&name);
1737 if (ret)
1738 return ret;
1740 kdc_log(context, config, 0,
1741 "Trying to authorize PK-INIT subject DN %s",
1742 *subject_name);
1744 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1745 if (ret == 0 && pc) {
1746 hx509_cert cert;
1747 size_t j;
1749 for (j = 0; j < pc->len; j++) {
1750 ret = hx509_cert_init_data(context->hx509ctx,
1751 pc->val[j].cert.data,
1752 pc->val[j].cert.length,
1753 &cert);
1754 if (ret)
1755 continue;
1756 ret = hx509_cert_cmp(cert, cp->cert);
1757 hx509_cert_free(cert);
1758 if (ret == 0) {
1759 kdc_log(context, config, 5,
1760 "Found matching PK-INIT cert in hdb");
1761 return 0;
1767 if (config->pkinit_princ_in_cert) {
1768 ret = match_rfc_san(context, config,
1769 context->hx509ctx,
1770 cp->cert,
1771 client->entry.principal);
1772 if (ret == 0) {
1773 kdc_log(context, config, 5,
1774 "Found matching PK-INIT SAN in certificate");
1775 return 0;
1777 ret = match_ms_upn_san(context, config,
1778 context->hx509ctx,
1779 cp->cert,
1780 clientdb,
1781 client);
1782 if (ret == 0) {
1783 kdc_log(context, config, 5,
1784 "Found matching MS UPN SAN in certificate");
1785 return 0;
1789 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1790 if (ret == 0 && acl != NULL) {
1792 * Cheat here and compare the generated name with the string
1793 * and not the reverse.
1795 for (i = 0; i < acl->len; i++) {
1796 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1797 continue;
1799 /* Don't support isser and anchor checking right now */
1800 if (acl->val[0].issuer)
1801 continue;
1802 if (acl->val[0].anchor)
1803 continue;
1805 kdc_log(context, config, 5,
1806 "Found matching PK-INIT database ACL");
1807 return 0;
1811 for (i = 0; i < principal_mappings.len; i++) {
1812 krb5_boolean b;
1814 b = krb5_principal_compare(context,
1815 client->entry.principal,
1816 principal_mappings.val[i].principal);
1817 if (b == FALSE)
1818 continue;
1819 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1820 continue;
1821 kdc_log(context, config, 5,
1822 "Found matching PK-INIT FILE ACL");
1823 return 0;
1826 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1827 krb5_set_error_message(context, ret,
1828 "PKINIT no matching principals for %s",
1829 *subject_name);
1831 kdc_log(context, config, 5,
1832 "PKINIT no matching principals for %s",
1833 *subject_name);
1835 free(*subject_name);
1836 *subject_name = NULL;
1838 return ret;
1841 static krb5_error_code
1842 add_principal_mapping(krb5_context context,
1843 const char *principal_name,
1844 const char * subject)
1846 struct pk_allowed_princ *tmp;
1847 krb5_principal principal;
1848 krb5_error_code ret;
1850 tmp = realloc(principal_mappings.val,
1851 (principal_mappings.len + 1) * sizeof(*tmp));
1852 if (tmp == NULL)
1853 return ENOMEM;
1854 principal_mappings.val = tmp;
1856 ret = krb5_parse_name(context, principal_name, &principal);
1857 if (ret)
1858 return ret;
1860 principal_mappings.val[principal_mappings.len].principal = principal;
1862 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1863 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1864 krb5_free_principal(context, principal);
1865 return ENOMEM;
1867 principal_mappings.len++;
1869 return 0;
1872 krb5_error_code
1873 _kdc_add_inital_verified_cas(krb5_context context,
1874 krb5_kdc_configuration *config,
1875 pk_client_params *cp,
1876 EncTicketPart *tkt)
1878 AD_INITIAL_VERIFIED_CAS cas;
1879 krb5_error_code ret;
1880 krb5_data data;
1881 size_t size = 0;
1883 memset(&cas, 0, sizeof(cas));
1885 /* XXX add CAs to cas here */
1887 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1888 &cas, &size, ret);
1889 if (ret)
1890 return ret;
1891 if (data.length != size)
1892 krb5_abortx(context, "internal asn.1 encoder error");
1894 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1895 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1896 &data);
1897 krb5_data_free(&data);
1898 return ret;
1905 static void
1906 load_mappings(krb5_context context, const char *fn)
1908 krb5_error_code ret;
1909 char buf[1024];
1910 unsigned long lineno = 0;
1911 FILE *f;
1913 f = fopen(fn, "r");
1914 if (f == NULL)
1915 return;
1917 while (fgets(buf, sizeof(buf), f) != NULL) {
1918 char *subject_name, *p;
1920 buf[strcspn(buf, "\n")] = '\0';
1921 lineno++;
1923 p = buf + strspn(buf, " \t");
1925 if (*p == '#' || *p == '\0')
1926 continue;
1928 subject_name = strchr(p, ':');
1929 if (subject_name == NULL) {
1930 krb5_warnx(context, "pkinit mapping file line %lu "
1931 "missing \":\" :%s",
1932 lineno, buf);
1933 continue;
1935 *subject_name++ = '\0';
1937 ret = add_principal_mapping(context, p, subject_name);
1938 if (ret) {
1939 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1940 lineno, buf);
1941 continue;
1945 fclose(f);
1952 krb5_error_code
1953 krb5_kdc_pk_initialize(krb5_context context,
1954 krb5_kdc_configuration *config,
1955 const char *user_id,
1956 const char *anchors,
1957 char **pool,
1958 char **revoke_list)
1960 const char *file;
1961 char *fn = NULL;
1962 krb5_error_code ret;
1964 file = krb5_config_get_string(context, NULL,
1965 "libdefaults", "moduli", NULL);
1967 ret = _krb5_parse_moduli(context, file, &moduli);
1968 if (ret)
1969 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1971 principal_mappings.len = 0;
1972 principal_mappings.val = NULL;
1974 ret = _krb5_pk_load_id(context,
1975 &kdc_identity,
1976 user_id,
1977 anchors,
1978 pool,
1979 revoke_list,
1980 NULL,
1981 NULL,
1982 NULL);
1983 if (ret) {
1984 krb5_warn(context, ret, "PKINIT: ");
1985 config->enable_pkinit = 0;
1986 return ret;
1990 hx509_query *q;
1991 hx509_cert cert;
1993 ret = hx509_query_alloc(context->hx509ctx, &q);
1994 if (ret) {
1995 krb5_warnx(context, "PKINIT: out of memory");
1996 return ENOMEM;
1999 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2000 if (config->pkinit_kdc_friendly_name)
2001 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2003 ret = hx509_certs_find(context->hx509ctx,
2004 kdc_identity->certs,
2006 &cert);
2007 hx509_query_free(context->hx509ctx, q);
2008 if (ret == 0) {
2009 if (hx509_cert_check_eku(context->hx509ctx, cert,
2010 &asn1_oid_id_pkkdcekuoid, 0)) {
2011 hx509_name name;
2012 char *str;
2013 ret = hx509_cert_get_subject(cert, &name);
2014 if (ret == 0) {
2015 hx509_name_to_string(name, &str);
2016 krb5_warnx(context, "WARNING Found KDC certificate (%s)"
2017 "is missing the PK-INIT KDC EKU, this is bad for "
2018 "interoperability.", str);
2019 hx509_name_free(&name);
2020 free(str);
2023 hx509_cert_free(cert);
2024 } else
2025 krb5_warnx(context, "PKINIT: failed to find a signing "
2026 "certifiate with a public key");
2029 if (krb5_config_get_bool_default(context,
2030 NULL,
2031 FALSE,
2032 "kdc",
2033 "pkinit_allow_proxy_certificate",
2034 NULL))
2035 config->pkinit_allow_proxy_certs = 1;
2037 file = krb5_config_get_string(context,
2038 NULL,
2039 "kdc",
2040 "pkinit_mappings_file",
2041 NULL);
2042 if (file == NULL) {
2043 asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2044 file = fn;
2047 load_mappings(context, file);
2048 if (fn)
2049 free(fn);
2051 return 0;
2054 #endif /* PKINIT */