gss: Sometimes set KRB5CCNAME when not overwriting
[heimdal.git] / kdc / pkinit.c
blob9fe316f4630da4b4af6c0e3d17dee30b3184cf56
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->q == 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(astgs_request_t priv,
396 const PA_DATA *pa,
397 pk_client_params **ret_params)
399 /* XXXrcd: we use priv vs r due to a conflict */
400 krb5_context context = priv->context;
401 krb5_kdc_configuration *config = priv->config;
402 const KDC_REQ *req = &priv->req;
403 hdb_entry_ex *client = priv->client;
404 pk_client_params *cp;
405 krb5_error_code ret;
406 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
407 krb5_data eContent = { 0, NULL };
408 krb5_data signed_content = { 0, NULL };
409 const char *type = "unknown type";
410 hx509_certs trust_anchors;
411 int have_data = 0;
412 const HDB_Ext_PKINIT_cert *pc;
414 *ret_params = NULL;
416 if (!config->enable_pkinit) {
417 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
418 krb5_clear_error_message(context);
419 return 0;
422 cp = calloc(1, sizeof(*cp));
423 if (cp == NULL) {
424 krb5_clear_error_message(context);
425 ret = ENOMEM;
426 goto out;
429 ret = hx509_certs_init(context->hx509ctx,
430 "MEMORY:trust-anchors",
431 0, NULL, &trust_anchors);
432 if (ret) {
433 krb5_set_error_message(context, ret, "failed to create trust anchors");
434 goto out;
437 ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
438 kdc_identity->anchors);
439 if (ret) {
440 hx509_certs_free(&trust_anchors);
441 krb5_set_error_message(context, ret, "failed to create verify context");
442 goto out;
445 /* Add any registered certificates for this client as trust anchors */
446 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
447 if (ret == 0 && pc != NULL) {
448 hx509_cert cert;
449 unsigned int i;
451 for (i = 0; i < pc->len; i++) {
452 cert = hx509_cert_init_data(context->hx509ctx,
453 pc->val[i].cert.data,
454 pc->val[i].cert.length,
455 NULL);
456 if (cert == NULL)
457 continue;
458 hx509_certs_add(context->hx509ctx, trust_anchors, cert);
459 hx509_cert_free(cert);
463 ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
464 if (ret) {
465 hx509_certs_free(&trust_anchors);
466 krb5_set_error_message(context, ret, "failed to create verify context");
467 goto out;
470 hx509_verify_set_time(cp->verify_ctx, kdc_time);
471 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
472 hx509_certs_free(&trust_anchors);
474 if (config->pkinit_allow_proxy_certs)
475 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
477 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
478 PA_PK_AS_REQ_Win2k r;
480 type = "PK-INIT-Win2k";
482 if (_kdc_is_anonymous(context, client->entry.principal)) {
483 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
484 krb5_set_error_message(context, ret,
485 "Anon not supported in RSA mode");
486 goto out;
489 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
490 pa->padata_value.length,
492 NULL);
493 if (ret) {
494 krb5_set_error_message(context, ret, "Can't decode "
495 "PK-AS-REQ-Win2k: %d", ret);
496 goto out;
499 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
500 &contentInfoOid,
501 &signed_content,
502 &have_data);
503 free_PA_PK_AS_REQ_Win2k(&r);
504 if (ret) {
505 krb5_set_error_message(context, ret,
506 "Can't unwrap ContentInfo(win): %d", ret);
507 goto out;
510 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
511 PA_PK_AS_REQ r;
513 type = "PK-INIT-IETF";
515 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
516 pa->padata_value.length,
518 NULL);
519 if (ret) {
520 krb5_set_error_message(context, ret,
521 "Can't decode PK-AS-REQ: %d", ret);
522 goto out;
525 /* XXX look at r.kdcPkId */
526 if (r.trustedCertifiers) {
527 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
528 unsigned int i, maxedi;
530 ret = hx509_certs_init(context->hx509ctx,
531 "MEMORY:client-anchors",
532 0, NULL,
533 &cp->client_anchors);
534 if (ret) {
535 krb5_set_error_message(context, ret,
536 "Can't allocate client anchors: %d",
537 ret);
538 goto out;
542 * If the client sent more then 10 EDI, don't bother
543 * looking more then 10 of performance reasons.
545 maxedi = edi->len;
546 if (maxedi > 10)
547 maxedi = 10;
548 for (i = 0; i < maxedi; i++) {
549 IssuerAndSerialNumber iasn;
550 hx509_query *q;
551 hx509_cert cert;
552 size_t size;
554 if (edi->val[i].issuerAndSerialNumber == NULL)
555 continue;
557 ret = hx509_query_alloc(context->hx509ctx, &q);
558 if (ret) {
559 krb5_set_error_message(context, ret,
560 "Failed to allocate hx509_query");
561 goto out;
564 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
565 edi->val[i].issuerAndSerialNumber->length,
566 &iasn,
567 &size);
568 if (ret) {
569 hx509_query_free(context->hx509ctx, q);
570 continue;
572 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
573 free_IssuerAndSerialNumber(&iasn);
574 if (ret) {
575 hx509_query_free(context->hx509ctx, q);
576 continue;
579 ret = hx509_certs_find(context->hx509ctx,
580 kdc_identity->certs,
582 &cert);
583 hx509_query_free(context->hx509ctx, q);
584 if (ret)
585 continue;
586 hx509_certs_add(context->hx509ctx,
587 cp->client_anchors, cert);
588 hx509_cert_free(cert);
592 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
593 &contentInfoOid,
594 &signed_content,
595 &have_data);
596 free_PA_PK_AS_REQ(&r);
597 if (ret) {
598 krb5_set_error_message(context, ret,
599 "Can't unwrap ContentInfo: %d", ret);
600 goto out;
603 } else {
604 krb5_clear_error_message(context);
605 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
606 goto out;
609 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
610 if (ret != 0) {
611 ret = KRB5KRB_ERR_GENERIC;
612 krb5_set_error_message(context, ret,
613 "PK-AS-REQ-Win2k invalid content type oid");
614 goto out;
617 if (!have_data) {
618 ret = KRB5KRB_ERR_GENERIC;
619 krb5_set_error_message(context, ret,
620 "PK-AS-REQ-Win2k no signed auth pack");
621 goto out;
625 hx509_certs signer_certs;
626 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
628 if (_kdc_is_anonymous(context, client->entry.principal)
629 || (config->historical_anon_realm && _kdc_is_anon_request(req)))
630 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
632 ret = hx509_cms_verify_signed(context->hx509ctx,
633 cp->verify_ctx,
634 flags,
635 signed_content.data,
636 signed_content.length,
637 NULL,
638 kdc_identity->certpool,
639 &eContentType,
640 &eContent,
641 &signer_certs);
642 if (ret) {
643 char *s = hx509_get_error_string(context->hx509ctx, ret);
644 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
645 s, ret);
646 free(s);
647 goto out;
650 if (signer_certs) {
651 ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
652 &cp->cert);
653 hx509_certs_free(&signer_certs);
655 if (ret)
656 goto out;
659 /* Signature is correct, now verify the signed message */
660 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
661 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
663 ret = KRB5_BADMSGTYPE;
664 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
665 goto out;
668 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
669 AuthPack_Win2k ap;
671 ret = decode_AuthPack_Win2k(eContent.data,
672 eContent.length,
673 &ap,
674 NULL);
675 if (ret) {
676 krb5_set_error_message(context, ret,
677 "Can't decode AuthPack: %d", ret);
678 goto out;
681 ret = pk_check_pkauthenticator_win2k(context,
682 &ap.pkAuthenticator,
683 req);
684 if (ret) {
685 free_AuthPack_Win2k(&ap);
686 goto out;
689 cp->type = PKINIT_WIN2K;
690 cp->nonce = ap.pkAuthenticator.nonce;
692 if (ap.clientPublicValue) {
693 ret = KRB5KRB_ERR_GENERIC;
694 krb5_set_error_message(context, ret,
695 "DH not supported for windows");
696 goto out;
698 free_AuthPack_Win2k(&ap);
700 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
701 AuthPack ap;
703 ret = decode_AuthPack(eContent.data,
704 eContent.length,
705 &ap,
706 NULL);
707 if (ret) {
708 krb5_set_error_message(context, ret,
709 "Can't decode AuthPack: %d", ret);
710 free_AuthPack(&ap);
711 goto out;
714 if (_kdc_is_anonymous(context, client->entry.principal) &&
715 ap.clientPublicValue == NULL) {
716 free_AuthPack(&ap);
717 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
718 krb5_set_error_message(context, ret,
719 "Anon not supported in RSA mode");
720 goto out;
723 ret = pk_check_pkauthenticator(context,
724 &ap.pkAuthenticator,
725 req);
726 if (ret) {
727 free_AuthPack(&ap);
728 goto out;
731 cp->type = PKINIT_27;
732 cp->nonce = ap.pkAuthenticator.nonce;
734 if (ap.clientPublicValue) {
735 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
736 cp->keyex = USE_DH;
737 ret = get_dh_param(context, config,
738 ap.clientPublicValue, cp);
739 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
740 cp->keyex = USE_ECDH;
741 ret = _kdc_get_ecdh_param(context, config,
742 ap.clientPublicValue,
743 &cp->u.ecdh.public_key);
744 } else {
745 ret = KRB5_BADMSGTYPE;
746 krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism");
748 if (ret) {
749 free_AuthPack(&ap);
750 goto out;
752 } else
753 cp->keyex = USE_RSA;
755 ret = hx509_peer_info_alloc(context->hx509ctx,
756 &cp->peer);
757 if (ret) {
758 free_AuthPack(&ap);
759 goto out;
762 if (ap.supportedCMSTypes) {
763 ret = hx509_peer_info_set_cms_algs(context->hx509ctx,
764 cp->peer,
765 ap.supportedCMSTypes->val,
766 ap.supportedCMSTypes->len);
767 if (ret) {
768 free_AuthPack(&ap);
769 goto out;
771 } else {
772 /* assume old client */
773 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
774 hx509_crypto_des_rsdi_ede3_cbc());
775 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
776 hx509_signature_rsa_with_sha1());
777 hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer,
778 hx509_signature_sha1());
780 free_AuthPack(&ap);
781 } else
782 krb5_abortx(context, "internal pkinit error");
784 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
786 out:
787 if (ret)
788 krb5_warn(context, ret, "PKINIT");
790 if (signed_content.data)
791 free(signed_content.data);
792 krb5_data_free(&eContent);
793 der_free_oid(&eContentType);
794 der_free_oid(&contentInfoOid);
795 if (ret) {
796 _kdc_pk_free_client_param(context, cp);
797 } else
798 *ret_params = cp;
799 return ret;
806 static krb5_error_code
807 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
809 integer->length = BN_num_bytes(bn);
810 integer->data = malloc(integer->length);
811 if (integer->data == NULL) {
812 krb5_clear_error_message(context);
813 return ENOMEM;
815 BN_bn2bin(bn, integer->data);
816 integer->negative = BN_is_negative(bn);
817 return 0;
820 static krb5_error_code
821 pk_mk_pa_reply_enckey(krb5_context context,
822 krb5_kdc_configuration *config,
823 pk_client_params *cp,
824 const KDC_REQ *req,
825 const krb5_data *req_buffer,
826 krb5_keyblock *reply_key,
827 ContentInfo *content_info,
828 hx509_cert *kdc_cert)
830 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
831 krb5_error_code ret;
832 krb5_data buf, signed_data;
833 size_t size = 0;
834 int do_win2k = 0;
836 krb5_data_zero(&buf);
837 krb5_data_zero(&signed_data);
839 *kdc_cert = NULL;
842 * If the message client is a win2k-type but it send pa data
843 * 09-binding it expects a IETF (checksum) reply so there can be
844 * no replay attacks.
847 switch (cp->type) {
848 case PKINIT_WIN2K: {
849 int i = 0;
850 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
851 && config->pkinit_require_binding == 0)
853 do_win2k = 1;
855 sdAlg = &asn1_oid_id_pkcs7_data;
856 evAlg = &asn1_oid_id_pkcs7_data;
857 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
858 break;
860 case PKINIT_27:
861 sdAlg = &asn1_oid_id_pkrkeydata;
862 evAlg = &asn1_oid_id_pkcs7_signedData;
863 break;
864 default:
865 krb5_abortx(context, "internal pkinit error");
868 if (do_win2k) {
869 ReplyKeyPack_Win2k kp;
870 memset(&kp, 0, sizeof(kp));
872 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
873 if (ret) {
874 krb5_clear_error_message(context);
875 goto out;
877 kp.nonce = cp->nonce;
879 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
880 buf.data, buf.length,
881 &kp, &size,ret);
882 free_ReplyKeyPack_Win2k(&kp);
883 } else {
884 krb5_crypto ascrypto;
885 ReplyKeyPack kp;
886 memset(&kp, 0, sizeof(kp));
888 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
889 if (ret) {
890 krb5_clear_error_message(context);
891 goto out;
894 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
895 if (ret) {
896 krb5_clear_error_message(context);
897 goto out;
900 ret = krb5_create_checksum(context, ascrypto, 6, 0,
901 req_buffer->data, req_buffer->length,
902 &kp.asChecksum);
903 if (ret) {
904 krb5_clear_error_message(context);
905 goto out;
908 ret = krb5_crypto_destroy(context, ascrypto);
909 if (ret) {
910 krb5_clear_error_message(context);
911 goto out;
913 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
914 free_ReplyKeyPack(&kp);
916 if (ret) {
917 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
918 "failed (%d)", ret);
919 goto out;
921 if (buf.length != size)
922 krb5_abortx(context, "Internal ASN.1 encoder error");
925 hx509_query *q;
926 hx509_cert cert;
928 ret = hx509_query_alloc(context->hx509ctx, &q);
929 if (ret)
930 goto out;
932 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
933 if (config->pkinit_kdc_friendly_name)
934 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
936 ret = hx509_certs_find(context->hx509ctx,
937 kdc_identity->certs,
939 &cert);
940 hx509_query_free(context->hx509ctx, q);
941 if (ret)
942 goto out;
944 ret = hx509_cms_create_signed_1(context->hx509ctx,
946 sdAlg,
947 buf.data,
948 buf.length,
949 NULL,
950 cert,
951 cp->peer,
952 cp->client_anchors,
953 kdc_identity->certpool,
954 &signed_data);
955 *kdc_cert = cert;
958 krb5_data_free(&buf);
959 if (ret)
960 goto out;
962 if (cp->type == PKINIT_WIN2K) {
963 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
964 &signed_data,
965 &buf);
966 if (ret)
967 goto out;
968 krb5_data_free(&signed_data);
969 signed_data = buf;
972 ret = hx509_cms_envelope_1(context->hx509ctx,
973 HX509_CMS_EV_NO_KU_CHECK,
974 cp->cert,
975 signed_data.data, signed_data.length,
976 envelopedAlg,
977 evAlg, &buf);
978 if (ret)
979 goto out;
981 ret = _krb5_pk_mk_ContentInfo(context,
982 &buf,
983 &asn1_oid_id_pkcs7_envelopedData,
984 content_info);
985 out:
986 if (ret && *kdc_cert) {
987 hx509_cert_free(*kdc_cert);
988 *kdc_cert = NULL;
991 krb5_data_free(&buf);
992 krb5_data_free(&signed_data);
993 return ret;
1000 static krb5_error_code
1001 pk_mk_pa_reply_dh(krb5_context context,
1002 krb5_kdc_configuration *config,
1003 pk_client_params *cp,
1004 ContentInfo *content_info,
1005 hx509_cert *kdc_cert)
1007 KDCDHKeyInfo dh_info;
1008 krb5_data signed_data, buf;
1009 ContentInfo contentinfo;
1010 krb5_error_code ret;
1011 hx509_cert cert;
1012 hx509_query *q;
1013 size_t size = 0;
1015 memset(&contentinfo, 0, sizeof(contentinfo));
1016 memset(&dh_info, 0, sizeof(dh_info));
1017 krb5_data_zero(&signed_data);
1018 krb5_data_zero(&buf);
1020 *kdc_cert = NULL;
1022 if (cp->keyex == USE_DH) {
1023 DH *kdc_dh = cp->u.dh.key;
1024 heim_integer i;
1026 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1027 if (ret)
1028 return ret;
1030 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1031 der_free_heim_integer(&i);
1032 if (ret) {
1033 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1034 "DHPublicKey failed (%d)", ret);
1035 return ret;
1037 if (buf.length != size)
1038 krb5_abortx(context, "Internal ASN.1 encoder error");
1040 dh_info.subjectPublicKey.length = buf.length * 8;
1041 dh_info.subjectPublicKey.data = buf.data;
1042 krb5_data_zero(&buf);
1043 } else if (cp->keyex == USE_ECDH) {
1044 unsigned char *p;
1045 ret = _kdc_serialize_ecdh_key(context, cp->u.ecdh.key, &p,
1046 &dh_info.subjectPublicKey.length);
1047 dh_info.subjectPublicKey.data = p;
1048 if (ret)
1049 goto out;
1050 } else
1051 krb5_abortx(context, "no keyex selected ?");
1054 dh_info.nonce = cp->nonce;
1056 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1057 ret);
1058 if (ret) {
1059 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1060 "KdcDHKeyInfo failed (%d)", ret);
1061 goto out;
1063 if (buf.length != size)
1064 krb5_abortx(context, "Internal ASN.1 encoder error");
1067 * Create the SignedData structure and sign the KdcDHKeyInfo
1068 * filled in above
1071 ret = hx509_query_alloc(context->hx509ctx, &q);
1072 if (ret)
1073 goto out;
1075 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1076 if (config->pkinit_kdc_friendly_name)
1077 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1079 ret = hx509_certs_find(context->hx509ctx,
1080 kdc_identity->certs,
1082 &cert);
1083 hx509_query_free(context->hx509ctx, q);
1084 if (ret)
1085 goto out;
1087 ret = hx509_cms_create_signed_1(context->hx509ctx,
1089 &asn1_oid_id_pkdhkeydata,
1090 buf.data,
1091 buf.length,
1092 NULL,
1093 cert,
1094 cp->peer,
1095 cp->client_anchors,
1096 kdc_identity->certpool,
1097 &signed_data);
1098 if (ret) {
1099 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1100 goto out;
1102 *kdc_cert = cert;
1104 ret = _krb5_pk_mk_ContentInfo(context,
1105 &signed_data,
1106 &asn1_oid_id_pkcs7_signedData,
1107 content_info);
1108 if (ret)
1109 goto out;
1111 out:
1112 if (ret && *kdc_cert) {
1113 hx509_cert_free(*kdc_cert);
1114 *kdc_cert = NULL;
1117 krb5_data_free(&buf);
1118 krb5_data_free(&signed_data);
1119 free_KDCDHKeyInfo(&dh_info);
1121 return ret;
1128 krb5_error_code
1129 _kdc_pk_mk_pa_reply(astgs_request_t r, pk_client_params *cp)
1131 krb5_context context = r->context;
1132 krb5_kdc_configuration *config = r->config;
1133 krb5_enctype sessionetype = r->sessionetype;
1134 const KDC_REQ *req = &r->req;
1135 const krb5_data *req_buffer = &r->request;
1136 krb5_keyblock *reply_key = &r->reply_key;
1137 krb5_keyblock *sessionkey = &r->session_key;
1138 METHOD_DATA *md = &r->outpadata;
1139 krb5_error_code ret;
1140 void *buf = NULL;
1141 size_t len = 0, size = 0;
1142 krb5_enctype enctype;
1143 int pa_type;
1144 hx509_cert kdc_cert = NULL;
1145 size_t i;
1147 if (!config->enable_pkinit) {
1148 krb5_clear_error_message(context);
1149 return 0;
1152 if (req->req_body.etype.len > 0) {
1153 for (i = 0; i < req->req_body.etype.len; i++)
1154 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
1155 break;
1156 if (req->req_body.etype.len <= i) {
1157 ret = KRB5KRB_ERR_GENERIC;
1158 krb5_set_error_message(context, ret,
1159 "No valid enctype available from client");
1160 goto out;
1162 enctype = req->req_body.etype.val[i];
1163 } else
1164 enctype = ETYPE_DES3_CBC_SHA1;
1166 if (cp->type == PKINIT_27) {
1167 PA_PK_AS_REP rep;
1168 const char *type, *other = "";
1170 memset(&rep, 0, sizeof(rep));
1172 pa_type = KRB5_PADATA_PK_AS_REP;
1174 if (cp->keyex == USE_RSA) {
1175 ContentInfo info;
1177 type = "enckey";
1179 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1181 ret = krb5_generate_random_keyblock(context, enctype,
1182 &cp->reply_key);
1183 if (ret) {
1184 free_PA_PK_AS_REP(&rep);
1185 goto out;
1187 ret = pk_mk_pa_reply_enckey(context,
1188 config,
1190 req,
1191 req_buffer,
1192 &cp->reply_key,
1193 &info,
1194 &kdc_cert);
1195 if (ret) {
1196 free_PA_PK_AS_REP(&rep);
1197 goto out;
1199 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1200 rep.u.encKeyPack.length, &info, &size,
1201 ret);
1202 free_ContentInfo(&info);
1203 if (ret) {
1204 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1205 "failed %d", ret);
1206 free_PA_PK_AS_REP(&rep);
1207 goto out;
1209 if (rep.u.encKeyPack.length != size)
1210 krb5_abortx(context, "Internal ASN.1 encoder error");
1212 ret = krb5_generate_random_keyblock(context, sessionetype,
1213 sessionkey);
1214 if (ret) {
1215 free_PA_PK_AS_REP(&rep);
1216 goto out;
1219 } else {
1220 ContentInfo info;
1222 switch (cp->keyex) {
1223 case USE_DH: type = "dh"; break;
1224 case USE_ECDH: type = "ecdh"; break;
1225 default: krb5_abortx(context, "unknown keyex"); break;
1228 if (cp->dh_group_name)
1229 other = cp->dh_group_name;
1231 rep.element = choice_PA_PK_AS_REP_dhInfo;
1233 ret = generate_dh_keyblock(context, cp, enctype);
1234 if (ret)
1235 return ret;
1237 ret = pk_mk_pa_reply_dh(context, config,
1239 &info,
1240 &kdc_cert);
1241 if (ret) {
1242 free_PA_PK_AS_REP(&rep);
1243 krb5_set_error_message(context, ret,
1244 "create pa-reply-dh "
1245 "failed %d", ret);
1246 goto out;
1249 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1250 rep.u.dhInfo.dhSignedData.length, &info, &size,
1251 ret);
1252 free_ContentInfo(&info);
1253 if (ret) {
1254 krb5_set_error_message(context, ret,
1255 "encoding of Key ContentInfo "
1256 "failed %d", ret);
1257 free_PA_PK_AS_REP(&rep);
1258 goto out;
1260 if (rep.u.encKeyPack.length != size)
1261 krb5_abortx(context, "Internal ASN.1 encoder error");
1263 /* generate the session key using the method from RFC6112 */
1265 krb5_keyblock kdc_contribution_key;
1266 krb5_crypto reply_crypto;
1267 krb5_crypto kdccont_crypto;
1268 krb5_data p1 = { strlen("PKINIT"), "PKINIT"};
1269 krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"};
1270 void *kckdata;
1271 size_t kcklen;
1272 EncryptedData kx;
1273 void *kxdata;
1274 size_t kxlen;
1276 ret = krb5_generate_random_keyblock(context, sessionetype,
1277 &kdc_contribution_key);
1278 if (ret) {
1279 free_PA_PK_AS_REP(&rep);
1280 goto out;
1282 ret = krb5_crypto_init(context, &cp->reply_key, enctype, &reply_crypto);
1283 if (ret) {
1284 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1285 free_PA_PK_AS_REP(&rep);
1286 goto out;
1288 ret = krb5_crypto_init(context, &kdc_contribution_key, sessionetype, &kdccont_crypto);
1289 if (ret) {
1290 krb5_crypto_destroy(context, reply_crypto);
1291 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1292 free_PA_PK_AS_REP(&rep);
1293 goto out;
1295 /* KRB-FX-CF2 */
1296 ret = krb5_crypto_fx_cf2(context, kdccont_crypto, reply_crypto,
1297 &p1, &p2, sessionetype, sessionkey);
1298 krb5_crypto_destroy(context, kdccont_crypto);
1299 if (ret) {
1300 krb5_crypto_destroy(context, reply_crypto);
1301 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1302 free_PA_PK_AS_REP(&rep);
1303 goto out;
1305 ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen,
1306 &kdc_contribution_key, &size, ret);
1307 krb5_free_keyblock_contents(context, &kdc_contribution_key);
1308 if (ret) {
1309 krb5_set_error_message(context, ret, "encoding of PKINIT-KX Key failed %d", ret);
1310 krb5_crypto_destroy(context, reply_crypto);
1311 free_PA_PK_AS_REP(&rep);
1312 goto out;
1314 if (kcklen != size)
1315 krb5_abortx(context, "Internal ASN.1 encoder error");
1316 ret = krb5_encrypt_EncryptedData(context, reply_crypto, KRB5_KU_PA_PKINIT_KX,
1317 kckdata, kcklen, 0, &kx);
1318 krb5_crypto_destroy(context, reply_crypto);
1319 free(kckdata);
1320 if (ret) {
1321 free_PA_PK_AS_REP(&rep);
1322 goto out;
1324 ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen,
1325 &kx, &size, ret);
1326 free_EncryptedData(&kx);
1327 if (ret) {
1328 krb5_set_error_message(context, ret, "encoding of PKINIT-KX failed %d", ret);
1329 free_PA_PK_AS_REP(&rep);
1330 goto out;
1332 if (kxlen != size)
1333 krb5_abortx(context, "Internal ASN.1 encoder error");
1334 /* Add PA-PKINIT-KX */
1335 ret = krb5_padata_add(context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen);
1336 if (ret) {
1337 krb5_set_error_message(context, ret,
1338 "Failed adding PKINIT-KX %d", ret);
1339 free(buf);
1340 goto out;
1345 #define use_btmm_with_enckey 0
1346 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1347 PA_PK_AS_REP_BTMM btmm;
1348 heim_any any;
1350 any.data = rep.u.encKeyPack.data;
1351 any.length = rep.u.encKeyPack.length;
1353 btmm.dhSignedData = NULL;
1354 btmm.encKeyPack = &any;
1356 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1357 } else {
1358 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1361 free_PA_PK_AS_REP(&rep);
1362 if (ret) {
1363 krb5_set_error_message(context, ret,
1364 "encode PA-PK-AS-REP failed %d", ret);
1365 goto out;
1367 if (len != size)
1368 krb5_abortx(context, "Internal ASN.1 encoder error");
1370 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1372 } else if (cp->type == PKINIT_WIN2K) {
1373 PA_PK_AS_REP_Win2k rep;
1374 ContentInfo info;
1376 if (cp->keyex != USE_RSA) {
1377 ret = KRB5KRB_ERR_GENERIC;
1378 krb5_set_error_message(context, ret,
1379 "Windows PK-INIT doesn't support DH");
1380 goto out;
1383 memset(&rep, 0, sizeof(rep));
1385 pa_type = KRB5_PADATA_PK_AS_REP_19;
1386 rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1388 ret = krb5_generate_random_keyblock(context, enctype,
1389 &cp->reply_key);
1390 if (ret) {
1391 free_PA_PK_AS_REP_Win2k(&rep);
1392 goto out;
1394 ret = pk_mk_pa_reply_enckey(context,
1395 config,
1397 req,
1398 req_buffer,
1399 &cp->reply_key,
1400 &info,
1401 &kdc_cert);
1402 if (ret) {
1403 free_PA_PK_AS_REP_Win2k(&rep);
1404 goto out;
1406 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1407 rep.u.encKeyPack.length, &info, &size,
1408 ret);
1409 free_ContentInfo(&info);
1410 if (ret) {
1411 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1412 "failed %d", ret);
1413 free_PA_PK_AS_REP_Win2k(&rep);
1414 goto out;
1416 if (rep.u.encKeyPack.length != size)
1417 krb5_abortx(context, "Internal ASN.1 encoder error");
1419 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1420 free_PA_PK_AS_REP_Win2k(&rep);
1421 if (ret) {
1422 krb5_set_error_message(context, ret,
1423 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1424 goto out;
1426 if (len != size)
1427 krb5_abortx(context, "Internal ASN.1 encoder error");
1429 ret = krb5_generate_random_keyblock(context, sessionetype,
1430 sessionkey);
1431 if (ret) {
1432 free(buf);
1433 goto out;
1436 } else
1437 krb5_abortx(context, "PK-INIT internal error");
1440 ret = krb5_padata_add(context, md, pa_type, buf, len);
1441 if (ret) {
1442 krb5_set_error_message(context, ret,
1443 "Failed adding PA-PK-AS-REP %d", ret);
1444 free(buf);
1445 goto out;
1448 if (config->pkinit_kdc_ocsp_file) {
1450 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1451 struct stat sb;
1452 int fd;
1454 krb5_data_free(&ocsp.data);
1456 ocsp.expire = 0;
1457 ocsp.next_update = kdc_time + 60 * 5;
1459 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1460 if (fd < 0) {
1461 kdc_log(context, config, 0,
1462 "PK-INIT failed to open ocsp data file %d", errno);
1463 goto out_ocsp;
1465 ret = fstat(fd, &sb);
1466 if (ret) {
1467 ret = errno;
1468 close(fd);
1469 kdc_log(context, config, 0,
1470 "PK-INIT failed to stat ocsp data %d", ret);
1471 goto out_ocsp;
1474 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1475 if (ret) {
1476 close(fd);
1477 kdc_log(context, config, 0,
1478 "PK-INIT failed to stat ocsp data %d", ret);
1479 goto out_ocsp;
1481 ocsp.data.length = sb.st_size;
1482 ret = read(fd, ocsp.data.data, sb.st_size);
1483 close(fd);
1484 if (ret != sb.st_size) {
1485 kdc_log(context, config, 0,
1486 "PK-INIT failed to read ocsp data %d", errno);
1487 goto out_ocsp;
1490 ret = hx509_ocsp_verify(context->hx509ctx,
1491 kdc_time,
1492 kdc_cert,
1494 ocsp.data.data, ocsp.data.length,
1495 &ocsp.expire);
1496 if (ret) {
1497 kdc_log(context, config, 0,
1498 "PK-INIT failed to verify ocsp data %d", ret);
1499 krb5_data_free(&ocsp.data);
1500 ocsp.expire = 0;
1501 } else if (ocsp.expire > 180) {
1502 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1503 ocsp.next_update = ocsp.expire;
1504 } else {
1505 ocsp.next_update = kdc_time;
1507 out_ocsp:
1508 ret = 0;
1511 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1513 ret = krb5_padata_add(context, md,
1514 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1515 ocsp.data.data, ocsp.data.length);
1516 if (ret) {
1517 krb5_set_error_message(context, ret,
1518 "Failed adding OCSP response %d", ret);
1519 goto out;
1524 out:
1525 if (kdc_cert)
1526 hx509_cert_free(kdc_cert);
1528 if (ret == 0)
1529 ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key);
1530 return ret;
1533 static int
1534 match_rfc_san(krb5_context context,
1535 krb5_kdc_configuration *config,
1536 hx509_context hx509ctx,
1537 hx509_cert client_cert,
1538 krb5_const_principal match)
1540 hx509_octet_string_list list;
1541 int ret, found = 0;
1542 size_t i;
1544 memset(&list, 0 , sizeof(list));
1546 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1547 client_cert,
1548 &asn1_oid_id_pkinit_san,
1549 &list);
1550 if (ret)
1551 goto out;
1553 for (i = 0; !found && i < list.len; i++) {
1554 krb5_principal_data principal;
1555 KRB5PrincipalName kn;
1556 size_t size;
1558 ret = decode_KRB5PrincipalName(list.val[i].data,
1559 list.val[i].length,
1560 &kn, &size);
1561 if (ret) {
1562 const char *msg = krb5_get_error_message(context, ret);
1563 kdc_log(context, config, 0,
1564 "Decoding kerberos name in certificate failed: %s", msg);
1565 krb5_free_error_message(context, msg);
1566 break;
1568 if (size != list.val[i].length) {
1569 kdc_log(context, config, 0,
1570 "Decoding kerberos name have extra bits on the end");
1571 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1574 memset(&principal, 0, sizeof (principal));
1575 principal.name = kn.principalName;
1576 principal.realm = kn.realm;
1578 if (krb5_principal_compare(context, &principal, match) == TRUE)
1579 found = 1;
1580 free_KRB5PrincipalName(&kn);
1583 out:
1584 hx509_free_octet_string_list(&list);
1585 if (ret)
1586 return ret;
1588 if (!found)
1589 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1591 return 0;
1594 static int
1595 match_ms_upn_san(krb5_context context,
1596 krb5_kdc_configuration *config,
1597 hx509_context hx509ctx,
1598 hx509_cert client_cert,
1599 HDB *clientdb,
1600 hdb_entry_ex *client)
1602 hx509_octet_string_list list;
1603 krb5_principal principal = NULL;
1604 int ret;
1605 MS_UPN_SAN upn;
1606 size_t size;
1608 memset(&list, 0 , sizeof(list));
1610 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1611 client_cert,
1612 &asn1_oid_id_pkinit_ms_san,
1613 &list);
1614 if (ret)
1615 goto out;
1617 if (list.len != 1) {
1618 kdc_log(context, config, 0,
1619 "More then one PK-INIT MS UPN SAN");
1620 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1621 goto out;
1624 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1625 if (ret) {
1626 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1627 goto out;
1629 if (size != list.val[0].length) {
1630 free_MS_UPN_SAN(&upn);
1631 kdc_log(context, config, 0, "Trailing data in ");
1632 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1633 goto out;
1636 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1638 ret = krb5_parse_name(context, upn, &principal);
1639 free_MS_UPN_SAN(&upn);
1640 if (ret) {
1641 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1642 goto out;
1645 if (clientdb->hdb_check_pkinit_ms_upn_match) {
1646 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1647 } else {
1650 * This is very wrong, but will do for a fallback
1652 strupr(principal->realm);
1654 if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE)
1655 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1658 out:
1659 if (principal)
1660 krb5_free_principal(context, principal);
1661 hx509_free_octet_string_list(&list);
1663 return ret;
1666 krb5_error_code
1667 _kdc_pk_check_client(astgs_request_t r,
1668 pk_client_params *cp,
1669 char **subject_name)
1671 krb5_context context = r->context;
1672 krb5_kdc_configuration *config = r->config;
1673 HDB *clientdb = r->clientdb;
1674 hdb_entry_ex *client = r->client;
1675 const HDB_Ext_PKINIT_acl *acl;
1676 const HDB_Ext_PKINIT_cert *pc;
1677 krb5_error_code ret;
1678 hx509_name name;
1679 size_t i;
1681 if (cp->cert == NULL) {
1682 if (!_kdc_is_anonymous(context, client->entry.principal)
1683 && !config->historical_anon_realm)
1684 return KRB5KDC_ERR_BADOPTION;
1686 *subject_name = strdup("<unauthenticated anonymous client>");
1687 if (*subject_name == NULL)
1688 return ENOMEM;
1689 return 0;
1692 ret = hx509_cert_get_base_subject(context->hx509ctx,
1693 cp->cert,
1694 &name);
1695 if (ret)
1696 return ret;
1698 ret = hx509_name_to_string(name, subject_name);
1699 hx509_name_free(&name);
1700 if (ret)
1701 return ret;
1703 kdc_log(context, config, 0,
1704 "Trying to authorize PK-INIT subject DN %s",
1705 *subject_name);
1707 ret = hdb_entry_get_pkinit_cert(&client->entry, &pc);
1708 if (ret == 0 && pc) {
1709 hx509_cert cert;
1710 size_t j;
1712 for (j = 0; j < pc->len; j++) {
1713 cert = hx509_cert_init_data(context->hx509ctx,
1714 pc->val[j].cert.data,
1715 pc->val[j].cert.length,
1716 NULL);
1717 if (cert == NULL)
1718 continue;
1719 ret = hx509_cert_cmp(cert, cp->cert);
1720 hx509_cert_free(cert);
1721 if (ret == 0) {
1722 kdc_log(context, config, 5,
1723 "Found matching PK-INIT cert in hdb");
1724 return 0;
1730 if (config->pkinit_princ_in_cert) {
1731 ret = match_rfc_san(context, config,
1732 context->hx509ctx,
1733 cp->cert,
1734 client->entry.principal);
1735 if (ret == 0) {
1736 kdc_log(context, config, 5,
1737 "Found matching PK-INIT SAN in certificate");
1738 return 0;
1740 ret = match_ms_upn_san(context, config,
1741 context->hx509ctx,
1742 cp->cert,
1743 clientdb,
1744 client);
1745 if (ret == 0) {
1746 kdc_log(context, config, 5,
1747 "Found matching MS UPN SAN in certificate");
1748 return 0;
1752 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1753 if (ret == 0 && acl != NULL) {
1755 * Cheat here and compare the generated name with the string
1756 * and not the reverse.
1758 for (i = 0; i < acl->len; i++) {
1759 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1760 continue;
1762 /* Don't support isser and anchor checking right now */
1763 if (acl->val[0].issuer)
1764 continue;
1765 if (acl->val[0].anchor)
1766 continue;
1768 kdc_log(context, config, 5,
1769 "Found matching PK-INIT database ACL");
1770 return 0;
1774 for (i = 0; i < principal_mappings.len; i++) {
1775 krb5_boolean b;
1777 b = krb5_principal_compare(context,
1778 client->entry.principal,
1779 principal_mappings.val[i].principal);
1780 if (b == FALSE)
1781 continue;
1782 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1783 continue;
1784 kdc_log(context, config, 5,
1785 "Found matching PK-INIT FILE ACL");
1786 return 0;
1789 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1790 krb5_set_error_message(context, ret,
1791 "PKINIT no matching principals for %s",
1792 *subject_name);
1794 kdc_log(context, config, 5,
1795 "PKINIT no matching principals for %s",
1796 *subject_name);
1798 free(*subject_name);
1799 *subject_name = NULL;
1801 return ret;
1804 static krb5_error_code
1805 add_principal_mapping(krb5_context context,
1806 const char *principal_name,
1807 const char * subject)
1809 struct pk_allowed_princ *tmp;
1810 krb5_principal principal;
1811 krb5_error_code ret;
1813 tmp = realloc(principal_mappings.val,
1814 (principal_mappings.len + 1) * sizeof(*tmp));
1815 if (tmp == NULL)
1816 return ENOMEM;
1817 principal_mappings.val = tmp;
1819 ret = krb5_parse_name(context, principal_name, &principal);
1820 if (ret)
1821 return ret;
1823 principal_mappings.val[principal_mappings.len].principal = principal;
1825 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1826 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1827 krb5_free_principal(context, principal);
1828 return ENOMEM;
1830 principal_mappings.len++;
1832 return 0;
1835 krb5_error_code
1836 _kdc_add_initial_verified_cas(krb5_context context,
1837 krb5_kdc_configuration *config,
1838 pk_client_params *cp,
1839 EncTicketPart *tkt)
1841 AD_INITIAL_VERIFIED_CAS cas;
1842 krb5_error_code ret;
1843 krb5_data data;
1844 size_t size = 0;
1846 memset(&cas, 0, sizeof(cas));
1848 /* XXX add CAs to cas here */
1850 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1851 &cas, &size, ret);
1852 if (ret)
1853 return ret;
1854 if (data.length != size)
1855 krb5_abortx(context, "internal asn.1 encoder error");
1857 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1858 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1859 &data);
1860 krb5_data_free(&data);
1861 return ret;
1868 static void
1869 load_mappings(krb5_context context, const char *fn)
1871 krb5_error_code ret;
1872 char buf[1024];
1873 unsigned long lineno = 0;
1874 FILE *f;
1876 f = fopen(fn, "r");
1877 if (f == NULL)
1878 return;
1880 while (fgets(buf, sizeof(buf), f) != NULL) {
1881 char *subject_name, *p;
1883 buf[strcspn(buf, "\n")] = '\0';
1884 lineno++;
1886 p = buf + strspn(buf, " \t");
1888 if (*p == '#' || *p == '\0')
1889 continue;
1891 subject_name = strchr(p, ':');
1892 if (subject_name == NULL) {
1893 krb5_warnx(context, "pkinit mapping file line %lu "
1894 "missing \":\" :%s",
1895 lineno, buf);
1896 continue;
1898 *subject_name++ = '\0';
1900 ret = add_principal_mapping(context, p, subject_name);
1901 if (ret) {
1902 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1903 lineno, buf);
1904 continue;
1908 fclose(f);
1915 krb5_error_code
1916 krb5_kdc_pk_initialize(krb5_context context,
1917 krb5_kdc_configuration *config,
1918 const char *user_id,
1919 const char *anchors,
1920 char **pool,
1921 char **revoke_list)
1923 const char *file;
1924 char *fn = NULL;
1925 krb5_error_code ret;
1927 file = krb5_config_get_string(context, NULL,
1928 "libdefaults", "moduli", NULL);
1930 ret = _krb5_parse_moduli(context, file, &moduli);
1931 if (ret)
1932 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1934 principal_mappings.len = 0;
1935 principal_mappings.val = NULL;
1937 ret = _krb5_pk_load_id(context,
1938 &kdc_identity,
1939 user_id,
1940 anchors,
1941 pool,
1942 revoke_list,
1943 NULL,
1944 NULL,
1945 NULL);
1946 if (ret) {
1947 krb5_warn(context, ret, "PKINIT: ");
1948 config->enable_pkinit = 0;
1949 return ret;
1953 hx509_query *q;
1954 hx509_cert cert;
1956 ret = hx509_query_alloc(context->hx509ctx, &q);
1957 if (ret) {
1958 krb5_warnx(context, "PKINIT: out of memory");
1959 return ENOMEM;
1962 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1963 if (config->pkinit_kdc_friendly_name)
1964 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1966 ret = hx509_certs_find(context->hx509ctx,
1967 kdc_identity->certs,
1969 &cert);
1970 hx509_query_free(context->hx509ctx, q);
1971 if (ret == 0) {
1972 if (hx509_cert_check_eku(context->hx509ctx, cert,
1973 &asn1_oid_id_pkkdcekuoid, 0)) {
1974 hx509_name name;
1975 char *str;
1976 ret = hx509_cert_get_subject(cert, &name);
1977 if (ret == 0) {
1978 hx509_name_to_string(name, &str);
1979 krb5_warnx(context, "WARNING Found KDC certificate (%s)"
1980 "is missing the PK-INIT KDC EKU, this is bad for "
1981 "interoperability.", str);
1982 hx509_name_free(&name);
1983 free(str);
1986 hx509_cert_free(cert);
1987 } else
1988 krb5_warnx(context, "PKINIT: failed to find a signing "
1989 "certifiate with a public key");
1992 if (krb5_config_get_bool_default(context,
1993 NULL,
1994 FALSE,
1995 "kdc",
1996 "pkinit_allow_proxy_certificate",
1997 NULL))
1998 config->pkinit_allow_proxy_certs = 1;
2000 file = krb5_config_get_string(context,
2001 NULL,
2002 "kdc",
2003 "pkinit_mappings_file",
2004 NULL);
2005 if (file == NULL) {
2006 int aret;
2008 aret = asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2009 if (aret == -1) {
2010 krb5_warnx(context, "PKINIT: out of memory");
2011 return ENOMEM;
2014 file = fn;
2017 load_mappings(context, file);
2018 if (fn)
2019 free(fn);
2021 return 0;
2024 #endif /* PKINIT */