kdc: unused pid element is (pid_t)-1 not zero
[heimdal.git] / kdc / pkinit.c
blob28333fc565b41b6d2f3802a51d0190400f7c1cd5
1 /*
2 * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "kdc_locl.h"
38 #ifdef PKINIT
40 #include <heim_asn1.h>
41 #include <rfc2459_asn1.h>
42 #include <cms_asn1.h>
43 #include <pkinit_asn1.h>
45 #include <hx509.h>
46 #include "crypto-headers.h"
48 struct pk_client_params {
49 enum krb5_pk_type type;
50 enum keyex_enum keyex;
51 union {
52 struct {
53 BIGNUM *public_key;
54 DH *key;
55 } dh;
56 struct {
57 void *public_key;
58 void *key;
59 } ecdh;
60 } u;
61 hx509_cert cert;
62 unsigned nonce;
63 EncryptionKey reply_key;
64 char *dh_group_name;
65 hx509_peer_info peer;
66 hx509_certs client_anchors;
67 hx509_verify_ctx verify_ctx;
70 struct pk_principal_mapping {
71 unsigned int len;
72 struct pk_allowed_princ {
73 krb5_principal principal;
74 char *subject;
75 } *val;
78 static struct krb5_pk_identity *kdc_identity;
79 static struct pk_principal_mapping principal_mappings;
80 static struct krb5_dh_moduli **moduli;
82 static struct {
83 krb5_data data;
84 time_t expire;
85 time_t next_update;
86 } ocsp;
92 static krb5_error_code
93 pk_check_pkauthenticator_win2k(krb5_context context,
94 PKAuthenticator_Win2k *a,
95 const KDC_REQ *req)
97 krb5_timestamp now;
99 krb5_timeofday (context, &now);
101 /* XXX cusec */
102 if (a->ctime == 0 || labs(a->ctime - now) > context->max_skew) {
103 krb5_clear_error_message(context);
104 return KRB5KRB_AP_ERR_SKEW;
106 return 0;
109 static krb5_error_code
110 pk_check_pkauthenticator(krb5_context context,
111 PKAuthenticator *a,
112 const KDC_REQ *req)
114 u_char *buf = NULL;
115 size_t buf_size;
116 krb5_error_code ret;
117 size_t len = 0;
118 krb5_timestamp now;
119 Checksum checksum;
121 krb5_timeofday (context, &now);
123 /* XXX cusec */
124 if (a->ctime == 0 || labs(a->ctime - now) > context->max_skew) {
125 krb5_clear_error_message(context);
126 return KRB5KRB_AP_ERR_SKEW;
129 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
130 if (ret) {
131 krb5_clear_error_message(context);
132 return ret;
134 if (buf_size != len)
135 krb5_abortx(context, "Internal error in ASN.1 encoder");
137 ret = krb5_create_checksum(context,
138 NULL,
140 CKSUMTYPE_SHA1,
141 buf,
142 len,
143 &checksum);
144 free(buf);
145 if (ret) {
146 krb5_clear_error_message(context);
147 return ret;
150 if (a->paChecksum == NULL) {
151 krb5_clear_error_message(context);
152 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
153 goto out;
156 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
157 krb5_clear_error_message(context);
158 ret = KRB5KRB_ERR_GENERIC;
161 out:
162 free_Checksum(&checksum);
164 return ret;
167 void
168 _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
170 if (cp == NULL)
171 return;
172 if (cp->cert)
173 hx509_cert_free(cp->cert);
174 if (cp->verify_ctx)
175 hx509_verify_destroy_ctx(cp->verify_ctx);
176 if (cp->keyex == USE_DH) {
177 if (cp->u.dh.key)
178 DH_free(cp->u.dh.key);
179 if (cp->u.dh.public_key)
180 BN_free(cp->u.dh.public_key);
182 if (cp->keyex == USE_ECDH)
183 _kdc_pk_free_client_ec_param(context, cp->u.ecdh.key,
184 cp->u.ecdh.public_key);
185 krb5_free_keyblock_contents(context, &cp->reply_key);
186 if (cp->dh_group_name)
187 free(cp->dh_group_name);
188 if (cp->peer)
189 hx509_peer_info_free(cp->peer);
190 if (cp->client_anchors)
191 hx509_certs_free(&cp->client_anchors);
192 memset(cp, 0, sizeof(*cp));
193 free(cp);
196 static krb5_error_code
197 generate_dh_keyblock(krb5_context context,
198 pk_client_params *client_params,
199 krb5_enctype enctype)
201 unsigned char *dh_gen_key = NULL;
202 krb5_keyblock key;
203 krb5_error_code ret;
204 size_t dh_gen_keylen, size;
206 memset(&key, 0, sizeof(key));
208 if (client_params->keyex == USE_DH) {
210 if (client_params->u.dh.public_key == NULL) {
211 ret = KRB5KRB_ERR_GENERIC;
212 krb5_set_error_message(context, ret, "missing DH public_key");
213 goto out;
216 if (!DH_generate_key(client_params->u.dh.key)) {
217 ret = KRB5KRB_ERR_GENERIC;
218 krb5_set_error_message(context, ret,
219 "Can't generate Diffie-Hellman keys");
220 goto out;
223 size = DH_size(client_params->u.dh.key);
225 dh_gen_key = malloc(size);
226 if (dh_gen_key == NULL) {
227 ret = ENOMEM;
228 krb5_set_error_message(context, ret, "malloc: out of memory");
229 goto out;
232 dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
233 if (dh_gen_keylen == (size_t)-1) {
234 ret = KRB5KRB_ERR_GENERIC;
235 krb5_set_error_message(context, ret,
236 "Can't compute Diffie-Hellman key");
237 goto out;
239 if (dh_gen_keylen < size) {
240 size -= dh_gen_keylen;
241 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
242 memset(dh_gen_key, 0, size);
245 ret = 0;
246 } else if (client_params->keyex == USE_ECDH) {
247 if (client_params->u.ecdh.public_key == NULL) {
248 ret = KRB5KRB_ERR_GENERIC;
249 krb5_set_error_message(context, ret, "missing ECDH public_key");
250 goto out;
252 ret = _kdc_generate_ecdh_keyblock(context,
253 client_params->u.ecdh.public_key,
254 &client_params->u.ecdh.key,
255 &dh_gen_key, &dh_gen_keylen);
256 if (ret)
257 goto out;
258 } else {
259 ret = KRB5KRB_ERR_GENERIC;
260 krb5_set_error_message(context, ret,
261 "Diffie-Hellman not selected keys");
262 goto out;
265 ret = _krb5_pk_octetstring2key(context,
266 enctype,
267 dh_gen_key, dh_gen_keylen,
268 NULL, NULL,
269 &client_params->reply_key);
271 out:
272 if (dh_gen_key)
273 free(dh_gen_key);
274 if (key.keyvalue.data)
275 krb5_free_keyblock_contents(context, &key);
277 return ret;
280 static BIGNUM *
281 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
283 BIGNUM *bn;
285 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
286 if (bn == NULL) {
287 krb5_set_error_message(context, KRB5_BADMSGTYPE,
288 "PKINIT: parsing BN failed %s", field);
289 return NULL;
291 BN_set_negative(bn, f->negative);
292 return bn;
295 static krb5_error_code
296 get_dh_param(krb5_context context,
297 krb5_kdc_configuration *config,
298 SubjectPublicKeyInfo *dh_key_info,
299 pk_client_params *client_params)
301 DomainParameters dhparam;
302 DH *dh = NULL;
303 krb5_error_code ret;
305 memset(&dhparam, 0, sizeof(dhparam));
307 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
308 ret = KRB5_BADMSGTYPE;
309 krb5_set_error_message(context, ret,
310 "PKINIT: subjectPublicKey not aligned "
311 "to 8 bit boundary");
312 goto out;
315 if (dh_key_info->algorithm.parameters == NULL) {
316 krb5_set_error_message(context, KRB5_BADMSGTYPE,
317 "PKINIT missing algorithm parameter "
318 "in clientPublicValue");
319 return KRB5_BADMSGTYPE;
322 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
323 dh_key_info->algorithm.parameters->length,
324 &dhparam,
325 NULL);
326 if (ret) {
327 krb5_set_error_message(context, ret, "Can't decode algorithm "
328 "parameters in clientPublicValue");
329 goto out;
332 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
333 &dhparam.p, &dhparam.g, dhparam.q, moduli,
334 &client_params->dh_group_name);
335 if (ret) {
336 /* XXX send back proposal of better group */
337 goto out;
340 dh = DH_new();
341 if (dh == NULL) {
342 ret = ENOMEM;
343 krb5_set_error_message(context, ret, "Cannot create DH structure");
344 goto out;
346 ret = KRB5_BADMSGTYPE;
347 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
348 if (dh->p == NULL)
349 goto out;
350 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
351 if (dh->g == NULL)
352 goto out;
354 if (dhparam.q) {
355 dh->q = integer_to_BN(context, "DH p-1 factor", dhparam.q);
356 if (dh->g == NULL)
357 goto out;
361 heim_integer glue;
362 size_t size;
364 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
365 dh_key_info->subjectPublicKey.length / 8,
366 &glue,
367 &size);
368 if (ret) {
369 krb5_clear_error_message(context);
370 return ret;
373 client_params->u.dh.public_key = integer_to_BN(context,
374 "subjectPublicKey",
375 &glue);
376 der_free_heim_integer(&glue);
377 if (client_params->u.dh.public_key == NULL) {
378 ret = KRB5_BADMSGTYPE;
379 goto out;
383 client_params->u.dh.key = dh;
384 dh = NULL;
385 ret = 0;
387 out:
388 if (dh)
389 DH_free(dh);
390 free_DomainParameters(&dhparam);
391 return ret;
394 krb5_error_code
395 _kdc_pk_rd_padata(krb5_context context,
396 krb5_kdc_configuration *config,
397 const KDC_REQ *req,
398 const PA_DATA *pa,
399 hdb_entry_ex *client,
400 pk_client_params **ret_params)
402 pk_client_params *cp;
403 krb5_error_code ret;
404 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
405 krb5_data eContent = { 0, NULL };
406 krb5_data signed_content = { 0, NULL };
407 const char *type = "unknown type";
408 hx509_certs trust_anchors;
409 int have_data = 0;
410 const HDB_Ext_PKINIT_cert *pc;
412 *ret_params = NULL;
414 if (!config->enable_pkinit) {
415 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
416 krb5_clear_error_message(context);
417 return 0;
420 cp = calloc(1, sizeof(*cp));
421 if (cp == NULL) {
422 krb5_clear_error_message(context);
423 ret = ENOMEM;
424 goto out;
427 ret = hx509_certs_init(context->hx509ctx,
428 "MEMORY:trust-anchors",
429 0, NULL, &trust_anchors);
430 if (ret) {
431 krb5_set_error_message(context, ret, "failed to create trust anchors");
432 goto out;
435 ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
436 kdc_identity->anchors);
437 if (ret) {
438 hx509_certs_free(&trust_anchors);
439 krb5_set_error_message(context, ret, "failed to create verify context");
440 goto out;
443 /* Add any registered certificates for this client as trust anchors */
444 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
445 if (ret == 0 && pc != NULL) {
446 hx509_cert cert;
447 unsigned int i;
449 for (i = 0; i < pc->len; i++) {
450 cert = hx509_cert_init_data(context->hx509ctx,
451 pc->val[i].cert.data,
452 pc->val[i].cert.length,
453 NULL);
454 if (cert == NULL)
455 continue;
456 hx509_certs_add(context->hx509ctx, trust_anchors, cert);
457 hx509_cert_free(cert);
461 ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
462 if (ret) {
463 hx509_certs_free(&trust_anchors);
464 krb5_set_error_message(context, ret, "failed to create verify context");
465 goto out;
468 hx509_verify_set_time(cp->verify_ctx, kdc_time);
469 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
470 hx509_certs_free(&trust_anchors);
472 if (config->pkinit_allow_proxy_certs)
473 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
475 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
476 PA_PK_AS_REQ_Win2k r;
478 type = "PK-INIT-Win2k";
480 if (_kdc_is_anon_request(&req->req_body)) {
481 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
482 krb5_set_error_message(context, ret,
483 "Anon not supported in RSA mode");
484 goto out;
487 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
488 pa->padata_value.length,
490 NULL);
491 if (ret) {
492 krb5_set_error_message(context, ret, "Can't decode "
493 "PK-AS-REQ-Win2k: %d", ret);
494 goto out;
497 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
498 &contentInfoOid,
499 &signed_content,
500 &have_data);
501 free_PA_PK_AS_REQ_Win2k(&r);
502 if (ret) {
503 krb5_set_error_message(context, ret,
504 "Can't unwrap ContentInfo(win): %d", ret);
505 goto out;
508 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
509 PA_PK_AS_REQ r;
511 type = "PK-INIT-IETF";
513 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
514 pa->padata_value.length,
516 NULL);
517 if (ret) {
518 krb5_set_error_message(context, ret,
519 "Can't decode PK-AS-REQ: %d", ret);
520 goto out;
523 /* XXX look at r.kdcPkId */
524 if (r.trustedCertifiers) {
525 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
526 unsigned int i, maxedi;
528 ret = hx509_certs_init(context->hx509ctx,
529 "MEMORY:client-anchors",
530 0, NULL,
531 &cp->client_anchors);
532 if (ret) {
533 krb5_set_error_message(context, ret,
534 "Can't allocate client anchors: %d",
535 ret);
536 goto out;
540 * If the client sent more then 10 EDI, don't bother
541 * looking more then 10 of performance reasons.
543 maxedi = edi->len;
544 if (maxedi > 10)
545 maxedi = 10;
546 for (i = 0; i < maxedi; i++) {
547 IssuerAndSerialNumber iasn;
548 hx509_query *q;
549 hx509_cert cert;
550 size_t size;
552 if (edi->val[i].issuerAndSerialNumber == NULL)
553 continue;
555 ret = hx509_query_alloc(context->hx509ctx, &q);
556 if (ret) {
557 krb5_set_error_message(context, ret,
558 "Failed to allocate hx509_query");
559 goto out;
562 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
563 edi->val[i].issuerAndSerialNumber->length,
564 &iasn,
565 &size);
566 if (ret) {
567 hx509_query_free(context->hx509ctx, q);
568 continue;
570 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
571 free_IssuerAndSerialNumber(&iasn);
572 if (ret) {
573 hx509_query_free(context->hx509ctx, q);
574 continue;
577 ret = hx509_certs_find(context->hx509ctx,
578 kdc_identity->certs,
580 &cert);
581 hx509_query_free(context->hx509ctx, q);
582 if (ret)
583 continue;
584 hx509_certs_add(context->hx509ctx,
585 cp->client_anchors, cert);
586 hx509_cert_free(cert);
590 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
591 &contentInfoOid,
592 &signed_content,
593 &have_data);
594 free_PA_PK_AS_REQ(&r);
595 if (ret) {
596 krb5_set_error_message(context, ret,
597 "Can't unwrap ContentInfo: %d", ret);
598 goto out;
601 } else {
602 krb5_clear_error_message(context);
603 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
604 goto out;
607 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
608 if (ret != 0) {
609 ret = KRB5KRB_ERR_GENERIC;
610 krb5_set_error_message(context, ret,
611 "PK-AS-REQ-Win2k invalid content type oid");
612 goto out;
615 if (!have_data) {
616 ret = KRB5KRB_ERR_GENERIC;
617 krb5_set_error_message(context, ret,
618 "PK-AS-REQ-Win2k no signed auth pack");
619 goto out;
623 hx509_certs signer_certs;
624 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
626 if (_kdc_is_anon_request(&req->req_body))
627 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
629 ret = hx509_cms_verify_signed(context->hx509ctx,
630 cp->verify_ctx,
631 flags,
632 signed_content.data,
633 signed_content.length,
634 NULL,
635 kdc_identity->certpool,
636 &eContentType,
637 &eContent,
638 &signer_certs);
639 if (ret) {
640 char *s = hx509_get_error_string(context->hx509ctx, ret);
641 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
642 s, ret);
643 free(s);
644 goto out;
647 if (signer_certs) {
648 ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
649 &cp->cert);
650 hx509_certs_free(&signer_certs);
652 if (ret)
653 goto out;
656 /* Signature is correct, now verify the signed message */
657 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
658 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
660 ret = KRB5_BADMSGTYPE;
661 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
662 goto out;
665 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
666 AuthPack_Win2k ap;
668 ret = decode_AuthPack_Win2k(eContent.data,
669 eContent.length,
670 &ap,
671 NULL);
672 if (ret) {
673 krb5_set_error_message(context, ret,
674 "Can't decode AuthPack: %d", ret);
675 goto out;
678 ret = pk_check_pkauthenticator_win2k(context,
679 &ap.pkAuthenticator,
680 req);
681 if (ret) {
682 free_AuthPack_Win2k(&ap);
683 goto out;
686 cp->type = PKINIT_WIN2K;
687 cp->nonce = ap.pkAuthenticator.nonce;
689 if (ap.clientPublicValue) {
690 ret = KRB5KRB_ERR_GENERIC;
691 krb5_set_error_message(context, ret,
692 "DH not supported for windows");
693 goto out;
695 free_AuthPack_Win2k(&ap);
697 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
698 AuthPack ap;
700 ret = decode_AuthPack(eContent.data,
701 eContent.length,
702 &ap,
703 NULL);
704 if (ret) {
705 krb5_set_error_message(context, ret,
706 "Can't decode AuthPack: %d", ret);
707 free_AuthPack(&ap);
708 goto out;
711 if (_kdc_is_anon_request(&req->req_body) &&
712 ap.clientPublicValue == NULL) {
713 free_AuthPack(&ap);
714 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
715 krb5_set_error_message(context, ret,
716 "Anon not supported in RSA mode");
717 goto out;
720 ret = pk_check_pkauthenticator(context,
721 &ap.pkAuthenticator,
722 req);
723 if (ret) {
724 free_AuthPack(&ap);
725 goto out;
728 cp->type = PKINIT_27;
729 cp->nonce = ap.pkAuthenticator.nonce;
731 if (ap.clientPublicValue) {
732 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
733 cp->keyex = USE_DH;
734 ret = get_dh_param(context, config,
735 ap.clientPublicValue, cp);
736 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
737 cp->keyex = USE_ECDH;
738 ret = _kdc_get_ecdh_param(context, config,
739 ap.clientPublicValue,
740 &cp->u.ecdh.public_key);
741 } else {
742 ret = KRB5_BADMSGTYPE;
743 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
745 if (ret) {
746 free_AuthPack(&ap);
747 goto out;
749 } else
750 cp->keyex = USE_RSA;
752 ret = hx509_peer_info_alloc(context->hx509ctx,
753 &cp->peer);
754 if (ret) {
755 free_AuthPack(&ap);
756 goto out;
759 if (ap.supportedCMSTypes) {
760 ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
761 cp->peer,
762 ap.supportedCMSTypes->val,
763 ap.supportedCMSTypes->len);
764 if (ret) {
765 free_AuthPack(&ap);
766 goto out;
768 } else {
769 /* assume old client */
770 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
771 hx509_crypto_des_rsdi_ede3_cbc());
772 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
773 hx509_signature_rsa_with_sha1());
774 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
775 hx509_signature_sha1());
777 free_AuthPack(&ap);
778 } else
779 krb5_abortx(context, "internal pkinit error");
781 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
783 out:
784 if (ret)
785 krb5_warn(context, ret, "PKINIT");
787 if (signed_content.data)
788 free(signed_content.data);
789 krb5_data_free(&eContent);
790 der_free_oid(&eContentType);
791 der_free_oid(&contentInfoOid);
792 if (ret) {
793 _kdc_pk_free_client_param(context, cp);
794 } else
795 *ret_params = cp;
796 return ret;
803 static krb5_error_code
804 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
806 integer->length = BN_num_bytes(bn);
807 integer->data = malloc(integer->length);
808 if (integer->data == NULL) {
809 krb5_clear_error_message(context);
810 return ENOMEM;
812 BN_bn2bin(bn, integer->data);
813 integer->negative = BN_is_negative(bn);
814 return 0;
817 static krb5_error_code
818 pk_mk_pa_reply_enckey(krb5_context context,
819 krb5_kdc_configuration *config,
820 pk_client_params *cp,
821 const KDC_REQ *req,
822 const krb5_data *req_buffer,
823 krb5_keyblock *reply_key,
824 ContentInfo *content_info,
825 hx509_cert *kdc_cert)
827 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
828 krb5_error_code ret;
829 krb5_data buf, signed_data;
830 size_t size = 0;
831 int do_win2k = 0;
833 krb5_data_zero(&buf);
834 krb5_data_zero(&signed_data);
836 *kdc_cert = NULL;
839 * If the message client is a win2k-type but it send pa data
840 * 09-binding it expects a IETF (checksum) reply so there can be
841 * no replay attacks.
844 switch (cp->type) {
845 case PKINIT_WIN2K: {
846 int i = 0;
847 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
848 && config->pkinit_require_binding == 0)
850 do_win2k = 1;
852 sdAlg = &asn1_oid_id_pkcs7_data;
853 evAlg = &asn1_oid_id_pkcs7_data;
854 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
855 break;
857 case PKINIT_27:
858 sdAlg = &asn1_oid_id_pkrkeydata;
859 evAlg = &asn1_oid_id_pkcs7_signedData;
860 break;
861 default:
862 krb5_abortx(context, "internal pkinit error");
865 if (do_win2k) {
866 ReplyKeyPack_Win2k kp;
867 memset(&kp, 0, sizeof(kp));
869 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
870 if (ret) {
871 krb5_clear_error_message(context);
872 goto out;
874 kp.nonce = cp->nonce;
876 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
877 buf.data, buf.length,
878 &kp, &size,ret);
879 free_ReplyKeyPack_Win2k(&kp);
880 } else {
881 krb5_crypto ascrypto;
882 ReplyKeyPack kp;
883 memset(&kp, 0, sizeof(kp));
885 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
886 if (ret) {
887 krb5_clear_error_message(context);
888 goto out;
891 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
892 if (ret) {
893 krb5_clear_error_message(context);
894 goto out;
897 ret = krb5_create_checksum(context, ascrypto, 6, 0,
898 req_buffer->data, req_buffer->length,
899 &kp.asChecksum);
900 if (ret) {
901 krb5_clear_error_message(context);
902 goto out;
905 ret = krb5_crypto_destroy(context, ascrypto);
906 if (ret) {
907 krb5_clear_error_message(context);
908 goto out;
910 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
911 free_ReplyKeyPack(&kp);
913 if (ret) {
914 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
915 "failed (%d)", ret);
916 goto out;
918 if (buf.length != size)
919 krb5_abortx(context, "Internal ASN.1 encoder error");
922 hx509_query *q;
923 hx509_cert cert;
925 ret = hx509_query_alloc(context->hx509ctx, &q);
926 if (ret)
927 goto out;
929 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
930 if (config->pkinit_kdc_friendly_name)
931 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
933 ret = hx509_certs_find(context->hx509ctx,
934 kdc_identity->certs,
936 &cert);
937 hx509_query_free(context->hx509ctx, q);
938 if (ret)
939 goto out;
941 ret = hx509_cms_create_signed_1(context->hx509ctx,
943 sdAlg,
944 buf.data,
945 buf.length,
946 NULL,
947 cert,
948 cp->peer,
949 cp->client_anchors,
950 kdc_identity->certpool,
951 &signed_data);
952 *kdc_cert = cert;
955 krb5_data_free(&buf);
956 if (ret)
957 goto out;
959 if (cp->type == PKINIT_WIN2K) {
960 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
961 &signed_data,
962 &buf);
963 if (ret)
964 goto out;
965 krb5_data_free(&signed_data);
966 signed_data = buf;
969 ret = hx509_cms_envelope_1(context->hx509ctx,
970 HX509_CMS_EV_NO_KU_CHECK,
971 cp->cert,
972 signed_data.data, signed_data.length,
973 envelopedAlg,
974 evAlg, &buf);
975 if (ret)
976 goto out;
978 ret = _krb5_pk_mk_ContentInfo(context,
979 &buf,
980 &asn1_oid_id_pkcs7_envelopedData,
981 content_info);
982 out:
983 if (ret && *kdc_cert) {
984 hx509_cert_free(*kdc_cert);
985 *kdc_cert = NULL;
988 krb5_data_free(&buf);
989 krb5_data_free(&signed_data);
990 return ret;
997 static krb5_error_code
998 pk_mk_pa_reply_dh(krb5_context context,
999 krb5_kdc_configuration *config,
1000 pk_client_params *cp,
1001 ContentInfo *content_info,
1002 hx509_cert *kdc_cert)
1004 KDCDHKeyInfo dh_info;
1005 krb5_data signed_data, buf;
1006 ContentInfo contentinfo;
1007 krb5_error_code ret;
1008 hx509_cert cert;
1009 hx509_query *q;
1010 size_t size = 0;
1012 memset(&contentinfo, 0, sizeof(contentinfo));
1013 memset(&dh_info, 0, sizeof(dh_info));
1014 krb5_data_zero(&signed_data);
1015 krb5_data_zero(&buf);
1017 *kdc_cert = NULL;
1019 if (cp->keyex == USE_DH) {
1020 DH *kdc_dh = cp->u.dh.key;
1021 heim_integer i;
1023 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1024 if (ret)
1025 return ret;
1027 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1028 der_free_heim_integer(&i);
1029 if (ret) {
1030 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1031 "DHPublicKey failed (%d)", ret);
1032 return ret;
1034 if (buf.length != size)
1035 krb5_abortx(context, "Internal ASN.1 encoder error");
1037 dh_info.subjectPublicKey.length = buf.length * 8;
1038 dh_info.subjectPublicKey.data = buf.data;
1039 krb5_data_zero(&buf);
1040 } else if (cp->keyex == USE_ECDH) {
1041 unsigned char *p;
1042 ret = _kdc_serialize_ecdh_key(context, cp->u.ecdh.key, &p,
1043 &dh_info.subjectPublicKey.length);
1044 dh_info.subjectPublicKey.data = p;
1045 if (ret)
1046 goto out;
1047 } else
1048 krb5_abortx(context, "no keyex selected ?");
1051 dh_info.nonce = cp->nonce;
1053 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1054 ret);
1055 if (ret) {
1056 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1057 "KdcDHKeyInfo failed (%d)", ret);
1058 goto out;
1060 if (buf.length != size)
1061 krb5_abortx(context, "Internal ASN.1 encoder error");
1064 * Create the SignedData structure and sign the KdcDHKeyInfo
1065 * filled in above
1068 ret = hx509_query_alloc(context->hx509ctx, &q);
1069 if (ret)
1070 goto out;
1072 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1073 if (config->pkinit_kdc_friendly_name)
1074 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1076 ret = hx509_certs_find(context->hx509ctx,
1077 kdc_identity->certs,
1079 &cert);
1080 hx509_query_free(context->hx509ctx, q);
1081 if (ret)
1082 goto out;
1084 ret = hx509_cms_create_signed_1(context->hx509ctx,
1086 &asn1_oid_id_pkdhkeydata,
1087 buf.data,
1088 buf.length,
1089 NULL,
1090 cert,
1091 cp->peer,
1092 cp->client_anchors,
1093 kdc_identity->certpool,
1094 &signed_data);
1095 if (ret) {
1096 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1097 goto out;
1099 *kdc_cert = cert;
1101 ret = _krb5_pk_mk_ContentInfo(context,
1102 &signed_data,
1103 &asn1_oid_id_pkcs7_signedData,
1104 content_info);
1105 if (ret)
1106 goto out;
1108 out:
1109 if (ret && *kdc_cert) {
1110 hx509_cert_free(*kdc_cert);
1111 *kdc_cert = NULL;
1114 krb5_data_free(&buf);
1115 krb5_data_free(&signed_data);
1116 free_KDCDHKeyInfo(&dh_info);
1118 return ret;
1125 krb5_error_code
1126 _kdc_pk_mk_pa_reply(krb5_context context,
1127 krb5_kdc_configuration *config,
1128 pk_client_params *cp,
1129 const hdb_entry_ex *client,
1130 krb5_enctype sessionetype,
1131 const KDC_REQ *req,
1132 const krb5_data *req_buffer,
1133 krb5_keyblock *reply_key,
1134 krb5_keyblock *sessionkey,
1135 METHOD_DATA *md)
1137 krb5_error_code ret;
1138 void *buf = NULL;
1139 size_t len = 0, size = 0;
1140 krb5_enctype enctype;
1141 int pa_type;
1142 hx509_cert kdc_cert = NULL;
1143 size_t i;
1145 if (!config->enable_pkinit) {
1146 krb5_clear_error_message(context);
1147 return 0;
1150 if (req->req_body.etype.len > 0) {
1151 for (i = 0; i < req->req_body.etype.len; i++)
1152 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1153 break;
1154 if (req->req_body.etype.len <= i) {
1155 ret = KRB5KRB_ERR_GENERIC;
1156 krb5_set_error_message(context, ret,
1157 "No valid enctype available from client");
1158 goto out;
1160 enctype = req->req_body.etype.val[i];
1161 } else
1162 enctype = ETYPE_DES3_CBC_SHA1;
1164 if (cp->type == PKINIT_27) {
1165 PA_PK_AS_REP rep;
1166 const char *type, *other = "";
1168 memset(&rep, 0, sizeof(rep));
1170 pa_type = KRB5_PADATA_PK_AS_REP;
1172 if (cp->keyex == USE_RSA) {
1173 ContentInfo info;
1175 type = "enckey";
1177 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1179 ret = krb5_generate_random_keyblock(context, enctype,
1180 &cp->reply_key);
1181 if (ret) {
1182 free_PA_PK_AS_REP(&rep);
1183 goto out;
1185 ret = pk_mk_pa_reply_enckey(context,
1186 config,
1188 req,
1189 req_buffer,
1190 &cp->reply_key,
1191 &info,
1192 &kdc_cert);
1193 if (ret) {
1194 free_PA_PK_AS_REP(&rep);
1195 goto out;
1197 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1198 rep.u.encKeyPack.length, &info, &size,
1199 ret);
1200 free_ContentInfo(&info);
1201 if (ret) {
1202 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1203 "failed %d", ret);
1204 free_PA_PK_AS_REP(&rep);
1205 goto out;
1207 if (rep.u.encKeyPack.length != size)
1208 krb5_abortx(context, "Internal ASN.1 encoder error");
1210 ret = krb5_generate_random_keyblock(context, sessionetype,
1211 sessionkey);
1212 if (ret) {
1213 free_PA_PK_AS_REP(&rep);
1214 goto out;
1217 } else {
1218 ContentInfo info;
1220 switch (cp->keyex) {
1221 case USE_DH: type = "dh"; break;
1222 case USE_ECDH: type = "ecdh"; break;
1223 default: krb5_abortx(context, "unknown keyex"); break;
1226 if (cp->dh_group_name)
1227 other = cp->dh_group_name;
1229 rep.element = choice_PA_PK_AS_REP_dhInfo;
1231 ret = generate_dh_keyblock(context, cp, enctype);
1232 if (ret)
1233 return ret;
1235 ret = pk_mk_pa_reply_dh(context, config,
1237 &info,
1238 &kdc_cert);
1239 if (ret) {
1240 free_PA_PK_AS_REP(&rep);
1241 krb5_set_error_message(context, ret,
1242 "create pa-reply-dh "
1243 "failed %d", ret);
1244 goto out;
1247 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1248 rep.u.dhInfo.dhSignedData.length, &info, &size,
1249 ret);
1250 free_ContentInfo(&info);
1251 if (ret) {
1252 krb5_set_error_message(context, ret,
1253 "encoding of Key ContentInfo "
1254 "failed %d", ret);
1255 free_PA_PK_AS_REP(&rep);
1256 goto out;
1258 if (rep.u.encKeyPack.length != size)
1259 krb5_abortx(context, "Internal ASN.1 encoder error");
1261 /* generate the session key using the method from RFC6112 */
1263 krb5_keyblock kdc_contribution_key;
1264 krb5_crypto reply_crypto;
1265 krb5_crypto kdccont_crypto;
1266 krb5_data p1 = { strlen("PKINIT"), "PKINIT"};
1267 krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"};
1268 void *kckdata;
1269 size_t kcklen;
1270 EncryptedData kx;
1271 void *kxdata;
1272 size_t kxlen;
1274 ret = krb5_generate_random_keyblock(context, sessionetype,
1275 &kdc_contribution_key);
1276 if (ret) {
1277 free_PA_PK_AS_REP(&rep);
1278 goto out;
1280 ret = krb5_crypto_init(context, &cp->reply_key, enctype, &reply_crypto);
1281 if (ret) {
1282 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1283 free_PA_PK_AS_REP(&rep);
1284 goto out;
1286 ret = krb5_crypto_init(context, &kdc_contribution_key, sessionetype, &kdccont_crypto);
1287 if (ret) {
1288 krb5_crypto_destroy(context, reply_crypto);
1289 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1290 free_PA_PK_AS_REP(&rep);
1291 goto out;
1293 /* KRB-FX-CF2 */
1294 ret = krb5_crypto_fx_cf2(context, kdccont_crypto, reply_crypto,
1295 &p1, &p2, sessionetype, sessionkey);
1296 krb5_crypto_destroy(context, kdccont_crypto);
1297 if (ret) {
1298 krb5_crypto_destroy(context, reply_crypto);
1299 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1300 free_PA_PK_AS_REP(&rep);
1301 goto out;
1303 ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen,
1304 &kdc_contribution_key, &size, ret);
1305 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1306 if (ret) {
1307 krb5_set_error_message(context, ret, "encoding of PKINIT-KX Key failed %d", ret);
1308 krb5_crypto_destroy(context, reply_crypto);
1309 free_PA_PK_AS_REP(&rep);
1310 goto out;
1312 if (kcklen != size)
1313 krb5_abortx(context, "Internal ASN.1 encoder error");
1314 ret = krb5_encrypt_EncryptedData(context, reply_crypto, KRB5_KU_PA_PKINIT_KX,
1315 kckdata, kcklen, 0, &kx);
1316 krb5_crypto_destroy(context, reply_crypto);
1317 free(kckdata);
1318 if (ret) {
1319 free_PA_PK_AS_REP(&rep);
1320 goto out;
1322 ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen,
1323 &kx, &size, ret);
1324 free_EncryptedData(&kx);
1325 if (ret) {
1326 krb5_set_error_message(context, ret, "encoding of PKINIT-KX failed %d", ret);
1327 free_PA_PK_AS_REP(&rep);
1328 goto out;
1330 if (kxlen != size)
1331 krb5_abortx(context, "Internal ASN.1 encoder error");
1332 /* Add PA-PKINIT-KX */
1333 ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen);
1334 if (ret) {
1335 krb5_set_error_message(context, ret,
1336 "Failed adding PKINIT-KX %d", ret);
1337 free(buf);
1338 goto out;
1343 #define use_btmm_with_enckey 0
1344 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1345 PA_PK_AS_REP_BTMM btmm;
1346 heim_any any;
1348 any.data = rep.u.encKeyPack.data;
1349 any.length = rep.u.encKeyPack.length;
1351 btmm.dhSignedData = NULL;
1352 btmm.encKeyPack = &any;
1354 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1355 } else {
1356 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1359 free_PA_PK_AS_REP(&rep);
1360 if (ret) {
1361 krb5_set_error_message(context, ret,
1362 "encode PA-PK-AS-REP failed %d", ret);
1363 goto out;
1365 if (len != size)
1366 krb5_abortx(context, "Internal ASN.1 encoder error");
1368 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1370 } else if (cp->type == PKINIT_WIN2K) {
1371 PA_PK_AS_REP_Win2k rep;
1372 ContentInfo info;
1374 if (cp->keyex != USE_RSA) {
1375 ret = KRB5KRB_ERR_GENERIC;
1376 krb5_set_error_message(context, ret,
1377 "Windows PK-INIT doesn't support DH");
1378 goto out;
1381 memset(&rep, 0, sizeof(rep));
1383 pa_type = KRB5_PADATA_PK_AS_REP_19;
1384 rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1386 ret = krb5_generate_random_keyblock(context, enctype,
1387 &cp->reply_key);
1388 if (ret) {
1389 free_PA_PK_AS_REP_Win2k(&rep);
1390 goto out;
1392 ret = pk_mk_pa_reply_enckey(context,
1393 config,
1395 req,
1396 req_buffer,
1397 &cp->reply_key,
1398 &info,
1399 &kdc_cert);
1400 if (ret) {
1401 free_PA_PK_AS_REP_Win2k(&rep);
1402 goto out;
1404 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1405 rep.u.encKeyPack.length, &info, &size,
1406 ret);
1407 free_ContentInfo(&info);
1408 if (ret) {
1409 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1410 "failed %d", ret);
1411 free_PA_PK_AS_REP_Win2k(&rep);
1412 goto out;
1414 if (rep.u.encKeyPack.length != size)
1415 krb5_abortx(context, "Internal ASN.1 encoder error");
1417 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1418 free_PA_PK_AS_REP_Win2k(&rep);
1419 if (ret) {
1420 krb5_set_error_message(context, ret,
1421 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1422 goto out;
1424 if (len != size)
1425 krb5_abortx(context, "Internal ASN.1 encoder error");
1427 ret = krb5_generate_random_keyblock(context, sessionetype,
1428 sessionkey);
1429 if (ret) {
1430 free(buf);
1431 goto out;
1434 } else
1435 krb5_abortx(context, "PK-INIT internal error");
1438 ret = krb5_padata_add(context, md, pa_type, buf, len);
1439 if (ret) {
1440 krb5_set_error_message(context, ret,
1441 "Failed adding PA-PK-AS-REP %d", ret);
1442 free(buf);
1443 goto out;
1446 if (config->pkinit_kdc_ocsp_file) {
1448 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1449 struct stat sb;
1450 int fd;
1452 krb5_data_free(&ocsp.data);
1454 ocsp.expire = 0;
1455 ocsp.next_update = kdc_time + 60 * 5;
1457 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1458 if (fd < 0) {
1459 kdc_log(context, config, 0,
1460 "PK-INIT failed to open ocsp data file %d", errno);
1461 goto out_ocsp;
1463 ret = fstat(fd, &sb);
1464 if (ret) {
1465 ret = errno;
1466 close(fd);
1467 kdc_log(context, config, 0,
1468 "PK-INIT failed to stat ocsp data %d", ret);
1469 goto out_ocsp;
1472 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1473 if (ret) {
1474 close(fd);
1475 kdc_log(context, config, 0,
1476 "PK-INIT failed to stat ocsp data %d", ret);
1477 goto out_ocsp;
1479 ocsp.data.length = sb.st_size;
1480 ret = read(fd, ocsp.data.data, sb.st_size);
1481 close(fd);
1482 if (ret != sb.st_size) {
1483 kdc_log(context, config, 0,
1484 "PK-INIT failed to read ocsp data %d", errno);
1485 goto out_ocsp;
1488 ret = hx509_ocsp_verify(context->hx509ctx,
1489 kdc_time,
1490 kdc_cert,
1492 ocsp.data.data, ocsp.data.length,
1493 &ocsp.expire);
1494 if (ret) {
1495 kdc_log(context, config, 0,
1496 "PK-INIT failed to verify ocsp data %d", ret);
1497 krb5_data_free(&ocsp.data);
1498 ocsp.expire = 0;
1499 } else if (ocsp.expire > 180) {
1500 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1501 ocsp.next_update = ocsp.expire;
1502 } else {
1503 ocsp.next_update = kdc_time;
1505 out_ocsp:
1506 ret = 0;
1509 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1511 ret = krb5_padata_add(context, md,
1512 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1513 ocsp.data.data, ocsp.data.length);
1514 if (ret) {
1515 krb5_set_error_message(context, ret,
1516 "Failed adding OCSP response %d", ret);
1517 goto out;
1522 out:
1523 if (kdc_cert)
1524 hx509_cert_free(kdc_cert);
1526 if (ret == 0)
1527 ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key);
1528 return ret;
1531 static int
1532 match_rfc_san(krb5_context context,
1533 krb5_kdc_configuration *config,
1534 hx509_context hx509ctx,
1535 hx509_cert client_cert,
1536 krb5_const_principal match)
1538 hx509_octet_string_list list;
1539 int ret, found = 0;
1540 size_t i;
1542 memset(&list, 0 , sizeof(list));
1544 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1545 client_cert,
1546 &asn1_oid_id_pkinit_san,
1547 &list);
1548 if (ret)
1549 goto out;
1551 for (i = 0; !found && i < list.len; i++) {
1552 krb5_principal_data principal;
1553 KRB5PrincipalName kn;
1554 size_t size;
1556 ret = decode_KRB5PrincipalName(list.val[i].data,
1557 list.val[i].length,
1558 &kn, &size);
1559 if (ret) {
1560 const char *msg = krb5_get_error_message(context, ret);
1561 kdc_log(context, config, 0,
1562 "Decoding kerberos name in certificate failed: %s", msg);
1563 krb5_free_error_message(context, msg);
1564 break;
1566 if (size != list.val[i].length) {
1567 kdc_log(context, config, 0,
1568 "Decoding kerberos name have extra bits on the end");
1569 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1572 memset(&principal, 0, sizeof (principal));
1573 principal.name = kn.principalName;
1574 principal.realm = kn.realm;
1576 if (krb5_principal_compare(context, &principal, match) == TRUE)
1577 found = 1;
1578 free_KRB5PrincipalName(&kn);
1581 out:
1582 hx509_free_octet_string_list(&list);
1583 if (ret)
1584 return ret;
1586 if (!found)
1587 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1589 return 0;
1592 static int
1593 match_ms_upn_san(krb5_context context,
1594 krb5_kdc_configuration *config,
1595 hx509_context hx509ctx,
1596 hx509_cert client_cert,
1597 HDB *clientdb,
1598 hdb_entry_ex *client)
1600 hx509_octet_string_list list;
1601 krb5_principal principal = NULL;
1602 int ret;
1603 MS_UPN_SAN upn;
1604 size_t size;
1606 memset(&list, 0 , sizeof(list));
1608 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1609 client_cert,
1610 &asn1_oid_id_pkinit_ms_san,
1611 &list);
1612 if (ret)
1613 goto out;
1615 if (list.len != 1) {
1616 kdc_log(context, config, 0,
1617 "More then one PK-INIT MS UPN SAN");
1618 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1619 goto out;
1622 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1623 if (ret) {
1624 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1625 goto out;
1627 if (size != list.val[0].length) {
1628 free_MS_UPN_SAN(&upn);
1629 kdc_log(context, config, 0, "Trailing data in ");
1630 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1631 goto out;
1634 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1636 ret = krb5_parse_name(context, upn, &principal);
1637 free_MS_UPN_SAN(&upn);
1638 if (ret) {
1639 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1640 goto out;
1643 if (clientdb->hdb_check_pkinit_ms_upn_match) {
1644 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1645 } else {
1648 * This is very wrong, but will do for a fallback
1650 strupr(principal->realm);
1652 if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1653 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1656 out:
1657 if (principal)
1658 krb5_free_principal(context, principal);
1659 hx509_free_octet_string_list(&list);
1661 return ret;
1664 krb5_error_code
1665 _kdc_pk_check_client(krb5_context context,
1666 krb5_kdc_configuration *config,
1667 HDB *clientdb,
1668 hdb_entry_ex *client,
1669 pk_client_params *cp,
1670 char **subject_name)
1672 const HDB_Ext_PKINIT_acl *acl;
1673 const HDB_Ext_PKINIT_cert *pc;
1674 krb5_error_code ret;
1675 hx509_name name;
1676 size_t i;
1678 if (cp->cert == NULL) {
1680 *subject_name = strdup("anonymous client client");
1681 if (*subject_name == NULL)
1682 return ENOMEM;
1683 return 0;
1686 ret = hx509_cert_get_base_subject(context->hx509ctx,
1687 cp->cert,
1688 &name);
1689 if (ret)
1690 return ret;
1692 ret = hx509_name_to_string(name, subject_name);
1693 hx509_name_free(&name);
1694 if (ret)
1695 return ret;
1697 kdc_log(context, config, 0,
1698 "Trying to authorize PK-INIT subject DN %s",
1699 *subject_name);
1701 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1702 if (ret == 0 && pc) {
1703 hx509_cert cert;
1704 size_t j;
1706 for (j = 0; j < pc->len; j++) {
1707 cert = hx509_cert_init_data(context->hx509ctx,
1708 pc->val[j].cert.data,
1709 pc->val[j].cert.length,
1710 NULL);
1711 if (cert == NULL)
1712 continue;
1713 ret = hx509_cert_cmp(cert, cp->cert);
1714 hx509_cert_free(cert);
1715 if (ret == 0) {
1716 kdc_log(context, config, 5,
1717 "Found matching PK-INIT cert in hdb");
1718 return 0;
1724 if (config->pkinit_princ_in_cert) {
1725 ret = match_rfc_san(context, config,
1726 context->hx509ctx,
1727 cp->cert,
1728 client->entry.principal);
1729 if (ret == 0) {
1730 kdc_log(context, config, 5,
1731 "Found matching PK-INIT SAN in certificate");
1732 return 0;
1734 ret = match_ms_upn_san(context, config,
1735 context->hx509ctx,
1736 cp->cert,
1737 clientdb,
1738 client);
1739 if (ret == 0) {
1740 kdc_log(context, config, 5,
1741 "Found matching MS UPN SAN in certificate");
1742 return 0;
1746 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1747 if (ret == 0 && acl != NULL) {
1749 * Cheat here and compare the generated name with the string
1750 * and not the reverse.
1752 for (i = 0; i < acl->len; i++) {
1753 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1754 continue;
1756 /* Don't support isser and anchor checking right now */
1757 if (acl->val[0].issuer)
1758 continue;
1759 if (acl->val[0].anchor)
1760 continue;
1762 kdc_log(context, config, 5,
1763 "Found matching PK-INIT database ACL");
1764 return 0;
1768 for (i = 0; i < principal_mappings.len; i++) {
1769 krb5_boolean b;
1771 b = krb5_principal_compare(context,
1772 client->entry.principal,
1773 principal_mappings.val[i].principal);
1774 if (b == FALSE)
1775 continue;
1776 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1777 continue;
1778 kdc_log(context, config, 5,
1779 "Found matching PK-INIT FILE ACL");
1780 return 0;
1783 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1784 krb5_set_error_message(context, ret,
1785 "PKINIT no matching principals for %s",
1786 *subject_name);
1788 kdc_log(context, config, 5,
1789 "PKINIT no matching principals for %s",
1790 *subject_name);
1792 free(*subject_name);
1793 *subject_name = NULL;
1795 return ret;
1798 static krb5_error_code
1799 add_principal_mapping(krb5_context context,
1800 const char *principal_name,
1801 const char * subject)
1803 struct pk_allowed_princ *tmp;
1804 krb5_principal principal;
1805 krb5_error_code ret;
1807 tmp = realloc(principal_mappings.val,
1808 (principal_mappings.len + 1) * sizeof(*tmp));
1809 if (tmp == NULL)
1810 return ENOMEM;
1811 principal_mappings.val = tmp;
1813 ret = krb5_parse_name(context, principal_name, &principal);
1814 if (ret)
1815 return ret;
1817 principal_mappings.val[principal_mappings.len].principal = principal;
1819 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1820 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1821 krb5_free_principal(context, principal);
1822 return ENOMEM;
1824 principal_mappings.len++;
1826 return 0;
1829 krb5_error_code
1830 _kdc_add_inital_verified_cas(krb5_context context,
1831 krb5_kdc_configuration *config,
1832 pk_client_params *cp,
1833 EncTicketPart *tkt)
1835 AD_INITIAL_VERIFIED_CAS cas;
1836 krb5_error_code ret;
1837 krb5_data data;
1838 size_t size = 0;
1840 memset(&cas, 0, sizeof(cas));
1842 /* XXX add CAs to cas here */
1844 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1845 &cas, &size, ret);
1846 if (ret)
1847 return ret;
1848 if (data.length != size)
1849 krb5_abortx(context, "internal asn.1 encoder error");
1851 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1852 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1853 &data);
1854 krb5_data_free(&data);
1855 return ret;
1862 static void
1863 load_mappings(krb5_context context, const char *fn)
1865 krb5_error_code ret;
1866 char buf[1024];
1867 unsigned long lineno = 0;
1868 FILE *f;
1870 f = fopen(fn, "r");
1871 if (f == NULL)
1872 return;
1874 while (fgets(buf, sizeof(buf), f) != NULL) {
1875 char *subject_name, *p;
1877 buf[strcspn(buf, "\n")] = '\0';
1878 lineno++;
1880 p = buf + strspn(buf, " \t");
1882 if (*p == '#' || *p == '\0')
1883 continue;
1885 subject_name = strchr(p, ':');
1886 if (subject_name == NULL) {
1887 krb5_warnx(context, "pkinit mapping file line %lu "
1888 "missing \":\" :%s",
1889 lineno, buf);
1890 continue;
1892 *subject_name++ = '\0';
1894 ret = add_principal_mapping(context, p, subject_name);
1895 if (ret) {
1896 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1897 lineno, buf);
1898 continue;
1902 fclose(f);
1909 krb5_error_code
1910 krb5_kdc_pk_initialize(krb5_context context,
1911 krb5_kdc_configuration *config,
1912 const char *user_id,
1913 const char *anchors,
1914 char **pool,
1915 char **revoke_list)
1917 const char *file;
1918 char *fn = NULL;
1919 krb5_error_code ret;
1921 file = krb5_config_get_string(context, NULL,
1922 "libdefaults", "moduli", NULL);
1924 ret = _krb5_parse_moduli(context, file, &moduli);
1925 if (ret)
1926 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1928 principal_mappings.len = 0;
1929 principal_mappings.val = NULL;
1931 ret = _krb5_pk_load_id(context,
1932 &kdc_identity,
1933 user_id,
1934 anchors,
1935 pool,
1936 revoke_list,
1937 NULL,
1938 NULL,
1939 NULL);
1940 if (ret) {
1941 krb5_warn(context, ret, "PKINIT: ");
1942 config->enable_pkinit = 0;
1943 return ret;
1947 hx509_query *q;
1948 hx509_cert cert;
1950 ret = hx509_query_alloc(context->hx509ctx, &q);
1951 if (ret) {
1952 krb5_warnx(context, "PKINIT: out of memory");
1953 return ENOMEM;
1956 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1957 if (config->pkinit_kdc_friendly_name)
1958 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1960 ret = hx509_certs_find(context->hx509ctx,
1961 kdc_identity->certs,
1963 &cert);
1964 hx509_query_free(context->hx509ctx, q);
1965 if (ret == 0) {
1966 if (hx509_cert_check_eku(context->hx509ctx, cert,
1967 &asn1_oid_id_pkkdcekuoid, 0)) {
1968 hx509_name name;
1969 char *str;
1970 ret = hx509_cert_get_subject(cert, &name);
1971 if (ret == 0) {
1972 hx509_name_to_string(name, &str);
1973 krb5_warnx(context, "WARNING Found KDC certificate (%s)"
1974 "is missing the PK-INIT KDC EKU, this is bad for "
1975 "interoperability.", str);
1976 hx509_name_free(&name);
1977 free(str);
1980 hx509_cert_free(cert);
1981 } else
1982 krb5_warnx(context, "PKINIT: failed to find a signing "
1983 "certifiate with a public key");
1986 if (krb5_config_get_bool_default(context,
1987 NULL,
1988 FALSE,
1989 "kdc",
1990 "pkinit_allow_proxy_certificate",
1991 NULL))
1992 config->pkinit_allow_proxy_certs = 1;
1994 file = krb5_config_get_string(context,
1995 NULL,
1996 "kdc",
1997 "pkinit_mappings_file",
1998 NULL);
1999 if (file == NULL) {
2000 int aret;
2002 aret = asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2003 if (aret == -1) {
2004 krb5_warnx(context, "PKINIT: out of memory");
2005 return ENOMEM;
2008 file = fn;
2011 load_mappings(context, file);
2012 if (fn)
2013 free(fn);
2015 return 0;
2018 #endif /* PKINIT */