s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / third_party / heimdal / kdc / pkinit.c
blob9f1f4b106f0939578098f7600dbef68b9e8d19b9
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 krb5_timestamp endtime;
63 krb5_timestamp max_life;
64 unsigned nonce;
65 EncryptionKey reply_key;
66 char *dh_group_name;
67 hx509_peer_info peer;
68 hx509_certs client_anchors;
69 hx509_verify_ctx verify_ctx;
70 heim_octet_string *freshness_token;
73 struct pk_principal_mapping {
74 unsigned int len;
75 struct pk_allowed_princ {
76 krb5_principal principal;
77 char *subject;
78 } *val;
81 static struct krb5_pk_identity *kdc_identity;
82 static struct pk_principal_mapping principal_mappings;
83 static struct krb5_dh_moduli **moduli;
85 static struct {
86 krb5_data data;
87 time_t expire;
88 time_t next_update;
89 } ocsp;
95 static krb5_error_code
96 pk_check_pkauthenticator_win2k(krb5_context context,
97 PKAuthenticator_Win2k *a,
98 const KDC_REQ *req)
100 krb5_timestamp now;
102 krb5_timeofday (context, &now);
104 /* XXX cusec */
105 if (a->ctime == 0 || labs(a->ctime - now) > context->max_skew) {
106 krb5_clear_error_message(context);
107 return KRB5KRB_AP_ERR_SKEW;
109 return 0;
112 static krb5_error_code
113 pk_check_pkauthenticator(krb5_context context,
114 const PKAuthenticator *a,
115 const KDC_REQ *req)
117 krb5_error_code ret;
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 ret = krb5_create_checksum(context,
130 NULL,
132 CKSUMTYPE_SHA1,
133 req->req_body._save.data,
134 req->req_body._save.length,
135 &checksum);
136 if (ret) {
137 krb5_clear_error_message(context);
138 return ret;
141 if (a->paChecksum == NULL) {
142 krb5_clear_error_message(context);
143 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
144 goto out;
147 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
148 krb5_clear_error_message(context);
149 ret = KRB5KRB_ERR_GENERIC;
152 out:
153 free_Checksum(&checksum);
155 return ret;
158 void
159 _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp)
161 if (cp == NULL)
162 return;
163 if (cp->cert)
164 hx509_cert_free(cp->cert);
165 if (cp->verify_ctx)
166 hx509_verify_destroy_ctx(cp->verify_ctx);
167 if (cp->keyex == USE_DH) {
168 if (cp->u.dh.key)
169 DH_free(cp->u.dh.key);
170 if (cp->u.dh.public_key)
171 BN_free(cp->u.dh.public_key);
173 if (cp->keyex == USE_ECDH)
174 _kdc_pk_free_client_ec_param(context, cp->u.ecdh.key,
175 cp->u.ecdh.public_key);
176 krb5_free_keyblock_contents(context, &cp->reply_key);
177 if (cp->dh_group_name)
178 free(cp->dh_group_name);
179 if (cp->peer)
180 hx509_peer_info_free(cp->peer);
181 if (cp->client_anchors)
182 hx509_certs_free(&cp->client_anchors);
183 if (cp->freshness_token)
184 der_free_octet_string(cp->freshness_token);
185 free(cp->freshness_token);
186 memset(cp, 0, sizeof(*cp));
187 free(cp);
190 static krb5_error_code
191 generate_dh_keyblock(krb5_context context,
192 pk_client_params *client_params,
193 krb5_enctype enctype)
195 unsigned char *dh_gen_key = NULL;
196 krb5_keyblock key;
197 krb5_error_code ret;
198 size_t dh_gen_keylen, size;
200 memset(&key, 0, sizeof(key));
202 if (client_params->keyex == USE_DH) {
204 if (client_params->u.dh.public_key == NULL) {
205 ret = KRB5KRB_ERR_GENERIC;
206 krb5_set_error_message(context, ret, "missing DH public_key");
207 goto out;
210 if (!DH_generate_key(client_params->u.dh.key)) {
211 ret = KRB5KRB_ERR_GENERIC;
212 krb5_set_error_message(context, ret,
213 "Can't generate Diffie-Hellman keys");
214 goto out;
217 size = DH_size(client_params->u.dh.key);
219 dh_gen_key = malloc(size);
220 if (dh_gen_key == NULL) {
221 ret = ENOMEM;
222 krb5_set_error_message(context, ret, "malloc: out of memory");
223 goto out;
226 dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key);
227 if (dh_gen_keylen == (size_t)-1) {
228 ret = KRB5KRB_ERR_GENERIC;
229 krb5_set_error_message(context, ret,
230 "Can't compute Diffie-Hellman key");
231 goto out;
233 if (dh_gen_keylen < size) {
234 size -= dh_gen_keylen;
235 memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
236 memset(dh_gen_key, 0, size);
237 dh_gen_keylen += size;
239 } else if (client_params->keyex == USE_ECDH) {
240 if (client_params->u.ecdh.public_key == NULL) {
241 ret = KRB5KRB_ERR_GENERIC;
242 krb5_set_error_message(context, ret, "missing ECDH public_key");
243 goto out;
245 ret = _kdc_generate_ecdh_keyblock(context,
246 client_params->u.ecdh.public_key,
247 &client_params->u.ecdh.key,
248 &dh_gen_key, &dh_gen_keylen);
249 if (ret)
250 goto out;
251 } else {
252 ret = KRB5KRB_ERR_GENERIC;
253 krb5_set_error_message(context, ret,
254 "Diffie-Hellman not selected keys");
255 goto out;
258 ret = _krb5_pk_octetstring2key(context,
259 enctype,
260 dh_gen_key, dh_gen_keylen,
261 NULL, NULL,
262 &client_params->reply_key);
264 out:
265 if (dh_gen_key)
266 free(dh_gen_key);
267 if (key.keyvalue.data)
268 krb5_free_keyblock_contents(context, &key);
270 return ret;
273 static BIGNUM *
274 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
276 BIGNUM *bn;
278 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
279 if (bn == NULL) {
280 krb5_set_error_message(context, KRB5_BADMSGTYPE,
281 "PKINIT: parsing BN failed %s", field);
282 return NULL;
284 BN_set_negative(bn, f->negative);
285 return bn;
288 static krb5_error_code
289 get_dh_param(krb5_context context,
290 krb5_kdc_configuration *config,
291 SubjectPublicKeyInfo *dh_key_info,
292 pk_client_params *client_params)
294 DomainParameters dhparam;
295 DH *dh = NULL;
296 krb5_error_code ret;
298 memset(&dhparam, 0, sizeof(dhparam));
300 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
301 ret = KRB5_BADMSGTYPE;
302 krb5_set_error_message(context, ret,
303 "PKINIT: subjectPublicKey not aligned "
304 "to 8 bit boundary");
305 goto out;
308 if (dh_key_info->algorithm.parameters == NULL) {
309 krb5_set_error_message(context, KRB5_BADMSGTYPE,
310 "PKINIT missing algorithm parameter "
311 "in clientPublicValue");
312 return KRB5_BADMSGTYPE;
315 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
316 dh_key_info->algorithm.parameters->length,
317 &dhparam,
318 NULL);
319 if (ret) {
320 krb5_set_error_message(context, ret, "Can't decode algorithm "
321 "parameters in clientPublicValue");
322 goto out;
325 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
326 &dhparam.p, &dhparam.g, dhparam.q, moduli,
327 &client_params->dh_group_name);
328 if (ret) {
329 /* XXX send back proposal of better group */
330 goto out;
333 dh = DH_new();
334 if (dh == NULL) {
335 ret = ENOMEM;
336 krb5_set_error_message(context, ret, "Cannot create DH structure");
337 goto out;
339 ret = KRB5_BADMSGTYPE;
340 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
341 if (dh->p == NULL)
342 goto out;
343 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
344 if (dh->g == NULL)
345 goto out;
347 if (dhparam.q) {
348 dh->q = integer_to_BN(context, "DH p-1 factor", dhparam.q);
349 if (dh->q == NULL)
350 goto out;
354 heim_integer glue;
355 size_t size;
357 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
358 dh_key_info->subjectPublicKey.length / 8,
359 &glue,
360 &size);
361 if (ret) {
362 krb5_clear_error_message(context);
363 return ret;
366 client_params->u.dh.public_key = integer_to_BN(context,
367 "subjectPublicKey",
368 &glue);
369 der_free_heim_integer(&glue);
370 if (client_params->u.dh.public_key == NULL) {
371 ret = KRB5_BADMSGTYPE;
372 goto out;
376 client_params->u.dh.key = dh;
377 dh = NULL;
378 ret = 0;
380 out:
381 if (dh)
382 DH_free(dh);
383 free_DomainParameters(&dhparam);
384 return ret;
387 krb5_error_code
388 _kdc_pk_rd_padata(astgs_request_t priv,
389 const PA_DATA *pa,
390 pk_client_params **ret_params)
392 /* XXXrcd: we use priv vs r due to a conflict */
393 krb5_context context = priv->context;
394 krb5_kdc_configuration *config = priv->config;
395 const KDC_REQ *req = &priv->req;
396 hdb_entry *client = priv->client;
397 pk_client_params *cp;
398 krb5_error_code ret;
399 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
400 krb5_data eContent = { 0, NULL };
401 krb5_data signed_content = { 0, NULL };
402 const char *type = "unknown type";
403 hx509_certs trust_anchors;
404 int have_data = 0;
405 const HDB_Ext_PKINIT_cert *pc;
407 *ret_params = NULL;
409 if (!config->enable_pkinit) {
410 kdc_log(context, config, 0, "PKINIT request but PKINIT not enabled");
411 krb5_clear_error_message(context);
412 return 0;
415 cp = calloc(1, sizeof(*cp));
416 if (cp == NULL) {
417 krb5_clear_error_message(context);
418 ret = ENOMEM;
419 goto out;
422 ret = hx509_certs_init(context->hx509ctx,
423 "MEMORY:trust-anchors",
424 0, NULL, &trust_anchors);
425 if (ret) {
426 krb5_set_error_message(context, ret, "failed to create trust anchors");
427 goto out;
430 ret = hx509_certs_merge(context->hx509ctx, trust_anchors,
431 kdc_identity->anchors);
432 if (ret) {
433 hx509_certs_free(&trust_anchors);
434 krb5_set_error_message(context, ret, "failed to create verify context");
435 goto out;
438 /* Add any registered certificates for this client as trust anchors */
439 ret = hdb_entry_get_pkinit_cert(client, &pc);
440 if (ret == 0 && pc != NULL) {
441 hx509_cert cert;
442 unsigned int i;
444 for (i = 0; i < pc->len; i++) {
445 cert = hx509_cert_init_data(context->hx509ctx,
446 pc->val[i].cert.data,
447 pc->val[i].cert.length,
448 NULL);
449 if (cert == NULL)
450 continue;
451 hx509_certs_add(context->hx509ctx, trust_anchors, cert);
452 hx509_cert_free(cert);
456 ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx);
457 if (ret) {
458 hx509_certs_free(&trust_anchors);
459 krb5_set_error_message(context, ret, "failed to create verify context");
460 goto out;
463 hx509_verify_set_time(cp->verify_ctx, kdc_time);
464 hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors);
465 hx509_certs_free(&trust_anchors);
467 hx509_verify_attach_revoke(cp->verify_ctx, kdc_identity->revokectx);
469 if (config->pkinit_allow_proxy_certs)
470 hx509_verify_set_proxy_certificate(cp->verify_ctx, 1);
472 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
473 PA_PK_AS_REQ_Win2k r;
475 type = "PK-INIT-Win2k";
477 if (_kdc_is_anonymous(context, client->principal)) {
478 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
479 krb5_set_error_message(context, ret,
480 "Anonymous client not supported in RSA mode");
481 goto out;
484 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
485 pa->padata_value.length,
487 NULL);
488 if (ret) {
489 krb5_set_error_message(context, ret, "Can't decode "
490 "PK-AS-REQ-Win2k: %d", ret);
491 goto out;
494 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
495 &contentInfoOid,
496 &signed_content,
497 &have_data);
498 free_PA_PK_AS_REQ_Win2k(&r);
499 if (ret) {
500 krb5_set_error_message(context, ret,
501 "Can't unwrap ContentInfo(win): %d", ret);
502 goto out;
505 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
506 PA_PK_AS_REQ r;
508 type = "PK-INIT-IETF";
510 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
511 pa->padata_value.length,
513 NULL);
514 if (ret) {
515 krb5_set_error_message(context, ret,
516 "Can't decode PK-AS-REQ: %d", ret);
517 goto out;
520 /* XXX look at r.kdcPkId */
521 if (r.trustedCertifiers) {
522 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
523 unsigned int i, maxedi;
525 ret = hx509_certs_init(context->hx509ctx,
526 "MEMORY:client-anchors",
527 0, NULL,
528 &cp->client_anchors);
529 if (ret) {
530 krb5_set_error_message(context, ret,
531 "Can't allocate client anchors: %d",
532 ret);
533 goto out;
537 * If the client sent more than 10 EDIs, don't bother
538 * looking at more than 10 for performance reasons.
540 maxedi = edi->len;
541 if (maxedi > 10)
542 maxedi = 10;
543 for (i = 0; i < maxedi; i++) {
544 IssuerAndSerialNumber iasn;
545 hx509_query *q;
546 hx509_cert cert;
547 size_t size;
549 if (edi->val[i].issuerAndSerialNumber == NULL)
550 continue;
552 ret = hx509_query_alloc(context->hx509ctx, &q);
553 if (ret) {
554 krb5_set_error_message(context, ret,
555 "Failed to allocate hx509_query");
556 goto out;
559 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
560 edi->val[i].issuerAndSerialNumber->length,
561 &iasn,
562 &size);
563 if (ret) {
564 hx509_query_free(context->hx509ctx, q);
565 continue;
567 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
568 free_IssuerAndSerialNumber(&iasn);
569 if (ret) {
570 hx509_query_free(context->hx509ctx, q);
571 continue;
574 ret = hx509_certs_find(context->hx509ctx,
575 kdc_identity->certs,
577 &cert);
578 hx509_query_free(context->hx509ctx, q);
579 if (ret)
580 continue;
581 hx509_certs_add(context->hx509ctx,
582 cp->client_anchors, cert);
583 hx509_cert_free(cert);
587 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
588 &contentInfoOid,
589 &signed_content,
590 &have_data);
591 free_PA_PK_AS_REQ(&r);
592 if (ret) {
593 krb5_set_error_message(context, ret,
594 "Can't unwrap ContentInfo: %d", ret);
595 goto out;
598 } else {
599 krb5_clear_error_message(context);
600 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
601 goto out;
604 ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData);
605 if (ret != 0) {
606 ret = KRB5KRB_ERR_GENERIC;
607 krb5_set_error_message(context, ret,
608 "PK-AS-REQ-Win2k invalid content type oid");
609 goto out;
612 if (!have_data) {
613 ret = KRB5KRB_ERR_GENERIC;
614 krb5_set_error_message(context, ret,
615 "PK-AS-REQ-Win2k no signed auth pack");
616 goto out;
620 hx509_certs signer_certs;
621 int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */
623 if (_kdc_is_anonymous(context, client->principal)
624 || (config->historical_anon_realm && _kdc_is_anon_request(req)))
625 flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER;
627 ret = hx509_cms_verify_signed(context->hx509ctx,
628 cp->verify_ctx,
629 flags,
630 signed_content.data,
631 signed_content.length,
632 NULL,
633 kdc_identity->certpool,
634 &eContentType,
635 &eContent,
636 &signer_certs);
637 if (ret) {
638 char *s = hx509_get_error_string(context->hx509ctx, ret);
639 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
640 s, ret);
641 free(s);
642 goto out;
645 if (signer_certs) {
646 ret = hx509_get_one_cert(context->hx509ctx, signer_certs,
647 &cp->cert);
648 hx509_certs_free(&signer_certs);
650 if (ret)
651 goto out;
654 /* Signature is correct, now verify the signed message */
655 if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 &&
656 der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0)
658 ret = KRB5_BADMSGTYPE;
659 krb5_set_error_message(context, ret, "got wrong oid for PK AuthData");
660 goto out;
663 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
664 AuthPack_Win2k ap;
666 ret = decode_AuthPack_Win2k(eContent.data,
667 eContent.length,
668 &ap,
669 NULL);
670 if (ret) {
671 krb5_set_error_message(context, ret,
672 "Can't decode AuthPack: %d", ret);
673 goto out;
676 ret = pk_check_pkauthenticator_win2k(context,
677 &ap.pkAuthenticator,
678 req);
679 if (ret) {
680 free_AuthPack_Win2k(&ap);
681 goto out;
684 cp->type = PKINIT_WIN2K;
685 cp->nonce = ap.pkAuthenticator.nonce;
687 if (ap.clientPublicValue) {
688 ret = KRB5KRB_ERR_GENERIC;
689 krb5_set_error_message(context, ret,
690 "DH not supported for Win2k");
691 free_AuthPack_Win2k(&ap);
692 goto out;
694 free_AuthPack_Win2k(&ap);
696 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
697 AuthPack ap;
699 ret = decode_AuthPack(eContent.data,
700 eContent.length,
701 &ap,
702 NULL);
703 if (ret) {
704 krb5_set_error_message(context, ret,
705 "Can't decode AuthPack: %d", ret);
706 free_AuthPack(&ap);
707 goto out;
710 if (_kdc_is_anonymous(context, client->principal) &&
711 ap.clientPublicValue == NULL) {
712 free_AuthPack(&ap);
713 ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED;
714 krb5_set_error_message(context, ret,
715 "Anonymous client not supported in RSA mode");
716 goto out;
719 ret = pk_check_pkauthenticator(context,
720 &ap.pkAuthenticator,
721 req);
722 if (ret) {
723 free_AuthPack(&ap);
724 goto out;
727 cp->type = PKINIT_27;
728 cp->nonce = ap.pkAuthenticator.nonce;
730 if (ap.clientPublicValue) {
731 if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) {
732 cp->keyex = USE_DH;
733 ret = get_dh_param(context, config,
734 ap.clientPublicValue, cp);
735 } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) {
736 cp->keyex = USE_ECDH;
737 ret = _kdc_get_ecdh_param(context, config,
738 ap.clientPublicValue,
739 &cp->u.ecdh.public_key);
740 } else {
741 ret = KRB5_BADMSGTYPE;
742 krb5_set_error_message(context, ret,
743 "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());
779 * Copy the freshness token into the out parameters if it is present.
781 if (ap.pkAuthenticator.freshnessToken != NULL) {
782 cp->freshness_token = calloc(1, sizeof (*cp->freshness_token));
783 if (cp->freshness_token == NULL) {
784 ret = ENOMEM;
785 free_AuthPack(&ap);
786 goto out;
789 ret = der_copy_octet_string(ap.pkAuthenticator.freshnessToken, cp->freshness_token);
790 if (ret) {
791 free_AuthPack(&ap);
792 goto out;
796 free_AuthPack(&ap);
797 } else
798 krb5_abortx(context, "internal pkinit error");
800 kdc_log(context, config, 0, "PKINIT request of type %s", type);
802 out:
803 if (ret)
804 krb5_warn(context, ret, "PKINIT");
806 if (signed_content.data)
807 free(signed_content.data);
808 krb5_data_free(&eContent);
809 der_free_oid(&eContentType);
810 der_free_oid(&contentInfoOid);
811 if (ret) {
812 _kdc_pk_free_client_param(context, cp);
813 } else
814 *ret_params = cp;
815 return ret;
818 krb5_timestamp
819 _kdc_pk_endtime(pk_client_params *pkp)
821 return pkp->endtime;
824 krb5_timestamp
825 _kdc_pk_max_life(pk_client_params *pkp)
827 return pkp->max_life;
830 unsigned
831 _kdc_pk_nonce(pk_client_params *pkp)
833 return pkp->nonce;
840 static krb5_error_code
841 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
843 integer->length = BN_num_bytes(bn);
844 integer->data = malloc(integer->length);
845 if (integer->data == NULL) {
846 krb5_clear_error_message(context);
847 return ENOMEM;
849 BN_bn2bin(bn, integer->data);
850 integer->negative = BN_is_negative(bn);
851 return 0;
854 static krb5_error_code
855 pk_mk_pa_reply_enckey(krb5_context context,
856 krb5_kdc_configuration *config,
857 pk_client_params *cp,
858 const KDC_REQ *req,
859 const krb5_data *req_buffer,
860 krb5_keyblock *reply_key,
861 ContentInfo *content_info,
862 hx509_cert *kdc_cert)
864 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL;
865 krb5_error_code ret;
866 krb5_data buf, signed_data;
867 size_t size = 0;
868 int do_win2k = 0;
870 krb5_data_zero(&buf);
871 krb5_data_zero(&signed_data);
873 *kdc_cert = NULL;
876 * If the message client is a win2k-type but it sends pa data
877 * 09-binding it expects a IETF (checksum) reply so there can be
878 * no replay attacks.
881 switch (cp->type) {
882 case PKINIT_WIN2K: {
883 int i = 0;
884 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
885 && config->pkinit_require_binding == 0)
887 do_win2k = 1;
889 sdAlg = &asn1_oid_id_pkcs7_data;
890 evAlg = &asn1_oid_id_pkcs7_data;
891 envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc;
892 break;
894 case PKINIT_27:
895 sdAlg = &asn1_oid_id_pkrkeydata;
896 evAlg = &asn1_oid_id_pkcs7_signedData;
897 break;
898 default:
899 krb5_abortx(context, "internal pkinit error");
902 if (do_win2k) {
903 ReplyKeyPack_Win2k kp;
904 memset(&kp, 0, sizeof(kp));
906 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
907 if (ret) {
908 krb5_clear_error_message(context);
909 goto out;
911 kp.nonce = cp->nonce;
913 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
914 buf.data, buf.length,
915 &kp, &size,ret);
916 free_ReplyKeyPack_Win2k(&kp);
917 } else {
918 krb5_crypto ascrypto;
919 ReplyKeyPack kp;
920 memset(&kp, 0, sizeof(kp));
922 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
923 if (ret) {
924 krb5_clear_error_message(context);
925 goto out;
928 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
929 if (ret) {
930 krb5_clear_error_message(context);
931 goto out;
934 ret = krb5_create_checksum(context, ascrypto, 6, 0,
935 req_buffer->data, req_buffer->length,
936 &kp.asChecksum);
937 if (ret) {
938 krb5_clear_error_message(context);
939 goto out;
942 ret = krb5_crypto_destroy(context, ascrypto);
943 if (ret) {
944 krb5_clear_error_message(context);
945 goto out;
947 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
948 free_ReplyKeyPack(&kp);
950 if (ret) {
951 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
952 "failed (%d)", ret);
953 goto out;
955 if (buf.length != size)
956 krb5_abortx(context, "Internal ASN.1 encoder error");
959 hx509_query *q;
960 hx509_cert cert;
962 ret = hx509_query_alloc(context->hx509ctx, &q);
963 if (ret)
964 goto out;
966 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
967 if (config->pkinit_kdc_friendly_name)
968 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
970 ret = hx509_certs_find(context->hx509ctx,
971 kdc_identity->certs,
973 &cert);
974 hx509_query_free(context->hx509ctx, q);
975 if (ret)
976 goto out;
978 ret = hx509_cms_create_signed_1(context->hx509ctx,
980 sdAlg,
981 buf.data,
982 buf.length,
983 NULL,
984 cert,
985 cp->peer,
986 cp->client_anchors,
987 kdc_identity->certpool,
988 &signed_data);
989 *kdc_cert = cert;
992 krb5_data_free(&buf);
993 if (ret)
994 goto out;
996 if (cp->type == PKINIT_WIN2K) {
997 ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData,
998 &signed_data,
999 &buf);
1000 if (ret)
1001 goto out;
1002 krb5_data_free(&signed_data);
1003 signed_data = buf;
1006 ret = hx509_cms_envelope_1(context->hx509ctx,
1007 HX509_CMS_EV_NO_KU_CHECK,
1008 cp->cert,
1009 signed_data.data, signed_data.length,
1010 envelopedAlg,
1011 evAlg, &buf);
1012 if (ret)
1013 goto out;
1015 ret = _krb5_pk_mk_ContentInfo(context,
1016 &buf,
1017 &asn1_oid_id_pkcs7_envelopedData,
1018 content_info);
1019 out:
1020 if (ret && *kdc_cert) {
1021 hx509_cert_free(*kdc_cert);
1022 *kdc_cert = NULL;
1025 krb5_data_free(&buf);
1026 krb5_data_free(&signed_data);
1027 return ret;
1034 static krb5_error_code
1035 pk_mk_pa_reply_dh(krb5_context context,
1036 krb5_kdc_configuration *config,
1037 pk_client_params *cp,
1038 ContentInfo *content_info,
1039 hx509_cert *kdc_cert)
1041 KDCDHKeyInfo dh_info;
1042 krb5_data signed_data, buf;
1043 ContentInfo contentinfo;
1044 krb5_error_code ret;
1045 hx509_cert cert;
1046 hx509_query *q;
1047 size_t size = 0;
1049 memset(&contentinfo, 0, sizeof(contentinfo));
1050 memset(&dh_info, 0, sizeof(dh_info));
1051 krb5_data_zero(&signed_data);
1052 krb5_data_zero(&buf);
1054 *kdc_cert = NULL;
1056 if (cp->keyex == USE_DH) {
1057 DH *kdc_dh = cp->u.dh.key;
1058 heim_integer i;
1060 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
1061 if (ret)
1062 return ret;
1064 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
1065 der_free_heim_integer(&i);
1066 if (ret) {
1067 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1068 "DHPublicKey failed (%d)", ret);
1069 return ret;
1071 if (buf.length != size)
1072 krb5_abortx(context, "Internal ASN.1 encoder error");
1074 dh_info.subjectPublicKey.length = buf.length * 8;
1075 dh_info.subjectPublicKey.data = buf.data;
1076 krb5_data_zero(&buf);
1077 } else if (cp->keyex == USE_ECDH) {
1078 unsigned char *p;
1079 ret = _kdc_serialize_ecdh_key(context, cp->u.ecdh.key, &p,
1080 &dh_info.subjectPublicKey.length);
1081 if (ret)
1082 goto out;
1083 dh_info.subjectPublicKey.data = p;
1084 } else
1085 krb5_abortx(context, "no keyex selected ?");
1088 dh_info.nonce = cp->nonce;
1090 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
1091 ret);
1092 if (ret) {
1093 krb5_set_error_message(context, ret, "ASN.1 encoding of "
1094 "KdcDHKeyInfo failed (%d)", ret);
1095 goto out;
1097 if (buf.length != size)
1098 krb5_abortx(context, "Internal ASN.1 encoder error");
1101 * Create the SignedData structure and sign the KdcDHKeyInfo
1102 * filled in above
1105 ret = hx509_query_alloc(context->hx509ctx, &q);
1106 if (ret)
1107 goto out;
1109 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1110 if (config->pkinit_kdc_friendly_name)
1111 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
1113 ret = hx509_certs_find(context->hx509ctx,
1114 kdc_identity->certs,
1116 &cert);
1117 hx509_query_free(context->hx509ctx, q);
1118 if (ret)
1119 goto out;
1121 ret = hx509_cms_create_signed_1(context->hx509ctx,
1123 &asn1_oid_id_pkdhkeydata,
1124 buf.data,
1125 buf.length,
1126 NULL,
1127 cert,
1128 cp->peer,
1129 cp->client_anchors,
1130 kdc_identity->certpool,
1131 &signed_data);
1132 if (ret) {
1133 kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret);
1134 goto out;
1136 *kdc_cert = cert;
1138 ret = _krb5_pk_mk_ContentInfo(context,
1139 &signed_data,
1140 &asn1_oid_id_pkcs7_signedData,
1141 content_info);
1142 if (ret)
1143 goto out;
1145 out:
1146 if (ret && *kdc_cert) {
1147 hx509_cert_free(*kdc_cert);
1148 *kdc_cert = NULL;
1151 krb5_data_free(&buf);
1152 krb5_data_free(&signed_data);
1153 free_KDCDHKeyInfo(&dh_info);
1155 return ret;
1162 krb5_error_code
1163 _kdc_pk_mk_pa_reply(astgs_request_t r, pk_client_params *cp)
1165 krb5_kdc_configuration *config = r->config;
1166 krb5_enctype sessionetype = r->sessionetype;
1167 const KDC_REQ *req = &r->req;
1168 const krb5_data *req_buffer = &r->request;
1169 krb5_keyblock *reply_key = &r->reply_key;
1170 krb5_keyblock *sessionkey = &r->session_key;
1171 METHOD_DATA *md = r->rep.padata;
1172 krb5_error_code ret;
1173 void *buf = NULL;
1174 size_t len = 0, size = 0;
1175 krb5_enctype enctype;
1176 int pa_type;
1177 hx509_cert kdc_cert = NULL;
1178 size_t i;
1180 if (!config->enable_pkinit) {
1181 krb5_clear_error_message(r->context);
1182 return 0;
1185 if (req->req_body.etype.len > 0) {
1186 for (i = 0; i < req->req_body.etype.len; i++)
1187 if (krb5_enctype_valid(r->context, req->req_body.etype.val[i]) == 0)
1188 break;
1189 if (req->req_body.etype.len <= i) {
1190 ret = KRB5KRB_ERR_GENERIC;
1191 krb5_set_error_message(r->context, ret,
1192 "No valid enctype available from client");
1193 goto out;
1195 enctype = req->req_body.etype.val[i];
1196 } else
1197 enctype = ETYPE_DES3_CBC_SHA1;
1199 if (cp->type == PKINIT_27) {
1200 PA_PK_AS_REP rep;
1201 const char *type, *other = "";
1203 memset(&rep, 0, sizeof(rep));
1205 pa_type = KRB5_PADATA_PK_AS_REP;
1207 if (cp->keyex == USE_RSA) {
1208 ContentInfo info;
1210 type = "enckey";
1212 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1214 ret = krb5_generate_random_keyblock(r->context, enctype,
1215 &cp->reply_key);
1216 if (ret) {
1217 free_PA_PK_AS_REP(&rep);
1218 goto out;
1220 ret = pk_mk_pa_reply_enckey(r->context,
1221 config,
1223 req,
1224 req_buffer,
1225 &cp->reply_key,
1226 &info,
1227 &kdc_cert);
1228 if (ret) {
1229 free_PA_PK_AS_REP(&rep);
1230 goto out;
1232 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1233 rep.u.encKeyPack.length, &info, &size,
1234 ret);
1235 free_ContentInfo(&info);
1236 if (ret) {
1237 krb5_set_error_message(r->context, ret, "encoding of Key ContentInfo "
1238 "failed %d", ret);
1239 free_PA_PK_AS_REP(&rep);
1240 goto out;
1242 if (rep.u.encKeyPack.length != size)
1243 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1245 ret = krb5_generate_random_keyblock(r->context, sessionetype,
1246 sessionkey);
1247 if (ret) {
1248 free_PA_PK_AS_REP(&rep);
1249 goto out;
1252 } else {
1253 ContentInfo info;
1255 switch (cp->keyex) {
1256 case USE_DH: type = "dh"; break;
1257 case USE_ECDH: type = "ecdh"; break;
1258 default: krb5_abortx(r->context, "unknown keyex"); break;
1261 if (cp->dh_group_name)
1262 other = cp->dh_group_name;
1264 rep.element = choice_PA_PK_AS_REP_dhInfo;
1266 ret = generate_dh_keyblock(r->context, cp, enctype);
1267 if (ret)
1268 return ret;
1270 ret = pk_mk_pa_reply_dh(r->context, config,
1272 &info,
1273 &kdc_cert);
1274 if (ret) {
1275 free_PA_PK_AS_REP(&rep);
1276 krb5_set_error_message(r->context, ret,
1277 "create pa-reply-dh "
1278 "failed %d", ret);
1279 goto out;
1282 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1283 rep.u.dhInfo.dhSignedData.length, &info, &size,
1284 ret);
1285 free_ContentInfo(&info);
1286 if (ret) {
1287 krb5_set_error_message(r->context, ret,
1288 "encoding of Key ContentInfo "
1289 "failed %d", ret);
1290 free_PA_PK_AS_REP(&rep);
1291 goto out;
1293 if (rep.u.encKeyPack.length != size)
1294 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1296 /* generate the session key using the method from RFC6112 */
1298 krb5_keyblock kdc_contribution_key;
1299 krb5_crypto reply_crypto;
1300 krb5_crypto kdccont_crypto;
1301 krb5_data p1 = { strlen("PKINIT"), "PKINIT"};
1302 krb5_data p2 = { strlen("KEYEXCHANGE"), "KEYEXCHANGE"};
1303 void *kckdata;
1304 size_t kcklen;
1305 EncryptedData kx;
1306 void *kxdata;
1307 size_t kxlen;
1309 ret = krb5_generate_random_keyblock(r->context, sessionetype,
1310 &kdc_contribution_key);
1311 if (ret) {
1312 free_PA_PK_AS_REP(&rep);
1313 goto out;
1315 ret = krb5_crypto_init(r->context, &cp->reply_key, enctype, &reply_crypto);
1316 if (ret) {
1317 krb5_free_keyblock_contents(r->context, &kdc_contribution_key);
1318 free_PA_PK_AS_REP(&rep);
1319 goto out;
1321 ret = krb5_crypto_init(r->context, &kdc_contribution_key, sessionetype, &kdccont_crypto);
1322 if (ret) {
1323 krb5_crypto_destroy(r->context, reply_crypto);
1324 krb5_free_keyblock_contents(r->context, &kdc_contribution_key);
1325 free_PA_PK_AS_REP(&rep);
1326 goto out;
1328 /* KRB-FX-CF2 */
1329 ret = krb5_crypto_fx_cf2(r->context, kdccont_crypto, reply_crypto,
1330 &p1, &p2, sessionetype, sessionkey);
1331 krb5_crypto_destroy(r->context, kdccont_crypto);
1332 if (ret) {
1333 krb5_crypto_destroy(r->context, reply_crypto);
1334 krb5_free_keyblock_contents(r->context, &kdc_contribution_key);
1335 free_PA_PK_AS_REP(&rep);
1336 goto out;
1338 ASN1_MALLOC_ENCODE(EncryptionKey, kckdata, kcklen,
1339 &kdc_contribution_key, &size, ret);
1340 krb5_free_keyblock_contents(r->context, &kdc_contribution_key);
1341 if (ret) {
1342 krb5_set_error_message(r->context, ret, "encoding of PKINIT-KX Key failed %d", ret);
1343 krb5_crypto_destroy(r->context, reply_crypto);
1344 free_PA_PK_AS_REP(&rep);
1345 goto out;
1347 if (kcklen != size)
1348 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1349 ret = krb5_encrypt_EncryptedData(r->context, reply_crypto, KRB5_KU_PA_PKINIT_KX,
1350 kckdata, kcklen, 0, &kx);
1351 krb5_crypto_destroy(r->context, reply_crypto);
1352 free(kckdata);
1353 if (ret) {
1354 free_PA_PK_AS_REP(&rep);
1355 goto out;
1357 ASN1_MALLOC_ENCODE(EncryptedData, kxdata, kxlen,
1358 &kx, &size, ret);
1359 free_EncryptedData(&kx);
1360 if (ret) {
1361 krb5_set_error_message(r->context, ret,
1362 "encoding of PKINIT-KX failed %d", ret);
1363 free_PA_PK_AS_REP(&rep);
1364 goto out;
1366 if (kxlen != size)
1367 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1368 /* Add PA-PKINIT-KX */
1369 ret = krb5_padata_add(r->context, md, KRB5_PADATA_PKINIT_KX, kxdata, kxlen);
1370 if (ret) {
1371 krb5_set_error_message(r->context, ret,
1372 "Failed adding PKINIT-KX %d", ret);
1373 free(buf);
1374 goto out;
1379 #define use_btmm_with_enckey 0
1380 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) {
1381 PA_PK_AS_REP_BTMM btmm;
1382 heim_any any;
1384 any.data = rep.u.encKeyPack.data;
1385 any.length = rep.u.encKeyPack.length;
1387 btmm.dhSignedData = NULL;
1388 btmm.encKeyPack = &any;
1390 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret);
1391 } else {
1392 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1395 free_PA_PK_AS_REP(&rep);
1396 if (ret) {
1397 krb5_set_error_message(r->context, ret,
1398 "encode PA-PK-AS-REP failed %d", ret);
1399 goto out;
1401 if (len != size)
1402 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1404 kdc_log(r->context, config, 0, "PKINIT using %s %s", type, other);
1406 } else if (cp->type == PKINIT_WIN2K) {
1407 PA_PK_AS_REP_Win2k rep;
1408 ContentInfo info;
1410 if (cp->keyex != USE_RSA) {
1411 ret = KRB5KRB_ERR_GENERIC;
1412 krb5_set_error_message(r->context, ret,
1413 "Win2k PKINIT doesn't support DH");
1414 goto out;
1417 memset(&rep, 0, sizeof(rep));
1419 pa_type = KRB5_PADATA_PK_AS_REP_19;
1420 rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack;
1422 ret = krb5_generate_random_keyblock(r->context, enctype,
1423 &cp->reply_key);
1424 if (ret) {
1425 free_PA_PK_AS_REP_Win2k(&rep);
1426 goto out;
1428 ret = pk_mk_pa_reply_enckey(r->context,
1429 config,
1431 req,
1432 req_buffer,
1433 &cp->reply_key,
1434 &info,
1435 &kdc_cert);
1436 if (ret) {
1437 free_PA_PK_AS_REP_Win2k(&rep);
1438 goto out;
1440 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1441 rep.u.encKeyPack.length, &info, &size,
1442 ret);
1443 free_ContentInfo(&info);
1444 if (ret) {
1445 krb5_set_error_message(r->context, ret, "encoding of Key ContentInfo "
1446 "failed %d", ret);
1447 free_PA_PK_AS_REP_Win2k(&rep);
1448 goto out;
1450 if (rep.u.encKeyPack.length != size)
1451 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1453 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1454 free_PA_PK_AS_REP_Win2k(&rep);
1455 if (ret) {
1456 krb5_set_error_message(r->context, ret,
1457 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1458 goto out;
1460 if (len != size)
1461 krb5_abortx(r->context, "Internal ASN.1 encoder error");
1463 ret = krb5_generate_random_keyblock(r->context, sessionetype,
1464 sessionkey);
1465 if (ret) {
1466 free(buf);
1467 goto out;
1470 } else
1471 krb5_abortx(r->context, "PKINIT internal error");
1474 ret = krb5_padata_add(r->context, md, pa_type, buf, len);
1475 if (ret) {
1476 krb5_set_error_message(r->context, ret,
1477 "Failed adding PA-PK-AS-REP %d", ret);
1478 free(buf);
1479 goto out;
1482 if (config->pkinit_kdc_ocsp_file) {
1484 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1485 struct stat sb;
1486 int fd;
1488 krb5_data_free(&ocsp.data);
1490 ocsp.expire = 0;
1491 ocsp.next_update = kdc_time + 60 * 5;
1493 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1494 if (fd < 0) {
1495 kdc_log(r->context, config, 0,
1496 "PKINIT failed to open ocsp data file %d", errno);
1497 goto out_ocsp;
1499 ret = fstat(fd, &sb);
1500 if (ret) {
1501 ret = errno;
1502 close(fd);
1503 kdc_log(r->context, config, 0,
1504 "PKINIT failed to stat ocsp data %d", ret);
1505 goto out_ocsp;
1508 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1509 if (ret) {
1510 close(fd);
1511 kdc_log(r->context, config, 0,
1512 "PKINIT failed to allocate ocsp data %d", ret);
1513 goto out_ocsp;
1515 ocsp.data.length = sb.st_size;
1516 ret = read(fd, ocsp.data.data, sb.st_size);
1517 close(fd);
1518 if (ret != sb.st_size) {
1519 kdc_log(r->context, config, 0,
1520 "PKINIT failed to read ocsp data %d", errno);
1521 goto out_ocsp;
1524 ret = hx509_ocsp_verify(r->context->hx509ctx,
1525 kdc_time,
1526 kdc_cert,
1528 ocsp.data.data, ocsp.data.length,
1529 &ocsp.expire);
1530 if (ret) {
1531 kdc_log(r->context, config, 0,
1532 "PKINIT failed to verify ocsp data %d", ret);
1533 krb5_data_free(&ocsp.data);
1534 ocsp.expire = 0;
1535 } else if (ocsp.expire > 180) {
1536 ocsp.expire -= 180; /* refetch the ocsp before it expires */
1537 ocsp.next_update = ocsp.expire;
1538 } else {
1539 ocsp.next_update = kdc_time;
1541 out_ocsp:
1542 ret = 0;
1545 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1547 ret = krb5_padata_add(r->context, md,
1548 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1549 ocsp.data.data, ocsp.data.length);
1550 if (ret) {
1551 krb5_set_error_message(r->context, ret,
1552 "Failed adding OCSP response %d", ret);
1553 goto out;
1558 out:
1559 if (kdc_cert)
1560 hx509_cert_free(kdc_cert);
1562 if (ret == 0)
1563 ret = krb5_copy_keyblock_contents(r->context, &cp->reply_key, reply_key);
1564 return ret;
1567 static int
1568 match_rfc_san(krb5_context context,
1569 krb5_kdc_configuration *config,
1570 hx509_context hx509ctx,
1571 hx509_cert client_cert,
1572 krb5_const_principal match)
1574 hx509_octet_string_list list;
1575 int ret, found = 0;
1576 size_t i;
1578 memset(&list, 0 , sizeof(list));
1580 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1581 client_cert,
1582 &asn1_oid_id_pkinit_san,
1583 &list);
1584 if (ret)
1585 goto out;
1587 for (i = 0; !found && i < list.len; i++) {
1588 krb5_principal_data principal;
1589 KRB5PrincipalName kn;
1590 size_t size;
1592 ret = decode_KRB5PrincipalName(list.val[i].data,
1593 list.val[i].length,
1594 &kn, &size);
1595 if (ret) {
1596 const char *msg = krb5_get_error_message(context, ret);
1597 kdc_log(context, config, 0,
1598 "Decoding Kerberos principal name in certificate failed: %s", msg);
1599 krb5_free_error_message(context, msg);
1600 break;
1602 if (size != list.val[i].length) {
1603 kdc_log(context, config, 0,
1604 "Decoded Kerberos principal name did not have expected length");
1605 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1608 memset(&principal, 0, sizeof (principal));
1609 principal.name = kn.principalName;
1610 principal.realm = kn.realm;
1612 if (krb5_principal_compare(context, &principal, match) == TRUE)
1613 found = 1;
1614 free_KRB5PrincipalName(&kn);
1617 out:
1618 hx509_free_octet_string_list(&list);
1619 if (ret)
1620 return ret;
1622 if (!found)
1623 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1625 return 0;
1628 static int
1629 match_ms_upn_san(krb5_context context,
1630 krb5_kdc_configuration *config,
1631 hx509_context hx509ctx,
1632 hx509_cert client_cert,
1633 HDB *clientdb,
1634 hdb_entry *client)
1636 hx509_octet_string_list list;
1637 krb5_principal principal = NULL;
1638 int ret;
1639 MS_UPN_SAN upn;
1640 size_t size;
1642 memset(&list, 0 , sizeof(list));
1644 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1645 client_cert,
1646 &asn1_oid_id_pkinit_ms_san,
1647 &list);
1648 if (ret)
1649 goto out;
1651 if (list.len != 1) {
1652 if (list.len)
1653 kdc_log(context, config, 0,
1654 "More than one PKINIT MS UPN SAN");
1655 else
1656 kdc_log(context, config, 0,
1657 "No PKINIT MS UPN SAN");
1658 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1659 goto out;
1662 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1663 if (ret) {
1664 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1665 goto out;
1667 if (size != list.val[0].length) {
1668 free_MS_UPN_SAN(&upn);
1669 kdc_log(context, config, 0, "Trailing data in MS UPN SAN");
1670 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1671 goto out;
1674 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1676 ret = krb5_parse_name(context, upn, &principal);
1677 free_MS_UPN_SAN(&upn);
1678 if (ret) {
1679 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1680 goto out;
1683 if (clientdb->hdb_check_pkinit_ms_upn_match) {
1684 ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal);
1685 } else {
1688 * This is very wrong, but will do for a fallback
1690 strupr(principal->realm);
1692 if (krb5_principal_compare(context, principal, client->principal) == FALSE)
1693 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1696 out:
1697 if (principal)
1698 krb5_free_principal(context, principal);
1699 hx509_free_octet_string_list(&list);
1701 return ret;
1704 krb5_error_code
1705 _kdc_pk_check_client(astgs_request_t r,
1706 pk_client_params *cp,
1707 char **subject_name)
1709 krb5_kdc_configuration *config = r->config;
1710 HDB *clientdb = r->clientdb;
1711 hdb_entry *client = r->client;
1712 const HDB_Ext_PKINIT_acl *acl;
1713 const HDB_Ext_PKINIT_cert *pc;
1714 krb5_error_code ret;
1715 hx509_name name;
1716 size_t i;
1718 if (cp->cert == NULL) {
1719 if (!_kdc_is_anonymous(r->context, client->principal)
1720 && !config->historical_anon_realm)
1721 return KRB5KDC_ERR_BADOPTION;
1723 *subject_name = strdup("<unauthenticated anonymous client>");
1724 if (*subject_name == NULL)
1725 return ENOMEM;
1726 return 0;
1729 cp->endtime = hx509_cert_get_notAfter(cp->cert);
1730 cp->max_life = 0;
1731 if (config->pkinit_max_life_from_cert_extension)
1732 cp->max_life =
1733 hx509_cert_get_pkinit_max_life(r->context->hx509ctx, cp->cert,
1734 config->pkinit_max_life_bound);
1735 if (cp->max_life == 0 && config->pkinit_max_life_from_cert > 0) {
1736 cp->max_life = cp->endtime - hx509_cert_get_notBefore(cp->cert);
1737 if (cp->max_life > config->pkinit_max_life_from_cert)
1738 cp->max_life = config->pkinit_max_life_from_cert;
1741 ret = hx509_cert_get_base_subject(r->context->hx509ctx,
1742 cp->cert,
1743 &name);
1744 if (ret)
1745 return ret;
1747 ret = hx509_name_to_string(name, subject_name);
1748 hx509_name_free(&name);
1749 if (ret)
1750 return ret;
1752 kdc_log(r->context, config, 0,
1753 "Trying to authorize PKINIT subject DN %s",
1754 *subject_name);
1756 ret = hdb_entry_get_pkinit_cert(client, &pc);
1757 if (ret == 0 && pc) {
1758 hx509_cert cert;
1759 size_t j;
1761 for (j = 0; j < pc->len; j++) {
1762 cert = hx509_cert_init_data(r->context->hx509ctx,
1763 pc->val[j].cert.data,
1764 pc->val[j].cert.length,
1765 NULL);
1766 if (cert == NULL)
1767 continue;
1768 ret = hx509_cert_cmp(cert, cp->cert);
1769 hx509_cert_free(cert);
1770 if (ret == 0) {
1771 kdc_log(r->context, config, 5,
1772 "Found matching PKINIT cert in hdb");
1773 return 0;
1779 if (config->pkinit_princ_in_cert) {
1780 ret = match_rfc_san(r->context, config,
1781 r->context->hx509ctx,
1782 cp->cert,
1783 client->principal);
1784 if (ret == 0) {
1785 kdc_log(r->context, config, 5,
1786 "Found matching PKINIT SAN in certificate");
1787 return 0;
1789 ret = match_ms_upn_san(r->context, config,
1790 r->context->hx509ctx,
1791 cp->cert,
1792 clientdb,
1793 client);
1794 if (ret == 0) {
1795 kdc_log(r->context, config, 5,
1796 "Found matching MS UPN SAN in certificate");
1797 return 0;
1801 ret = hdb_entry_get_pkinit_acl(client, &acl);
1802 if (ret == 0 && acl != NULL) {
1804 * Cheat here and compare the generated name with the string
1805 * and not the reverse.
1807 for (i = 0; i < acl->len; i++) {
1808 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1809 continue;
1811 /* Don't support issuer and anchor checking right now */
1812 if (acl->val[0].issuer)
1813 continue;
1814 if (acl->val[0].anchor)
1815 continue;
1817 kdc_log(r->context, config, 5,
1818 "Found matching PKINIT database ACL");
1819 return 0;
1823 for (i = 0; i < principal_mappings.len; i++) {
1824 krb5_boolean b;
1826 b = krb5_principal_compare(r->context,
1827 client->principal,
1828 principal_mappings.val[i].principal);
1829 if (b == FALSE)
1830 continue;
1831 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1832 continue;
1833 kdc_log(r->context, config, 5,
1834 "Found matching PKINIT FILE ACL");
1835 return 0;
1838 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1839 krb5_set_error_message(r->context, ret,
1840 "PKINIT no matching principals for %s",
1841 *subject_name);
1843 kdc_log(r->context, config, 5,
1844 "PKINIT no matching principals for %s",
1845 *subject_name);
1847 free(*subject_name);
1848 *subject_name = NULL;
1850 return ret;
1853 krb5_error_code
1854 _kdc_pk_validate_freshness_token(astgs_request_t r,
1855 pk_client_params *cp)
1857 krb5_error_code ret = 0;
1858 uint8_t *token_data = NULL;
1859 size_t token_len;
1860 uint8_t *remaining_token_data = NULL;
1861 size_t remaining_len;
1862 EncryptedData enc_data;
1863 size_t size;
1864 const hdb_entry *krbtgt = NULL;
1865 krb5_kvno kvno;
1866 const Keys *keys = NULL;
1867 Key *key = NULL;
1868 krb5_crypto crypto;
1869 krb5_data ts_data;
1870 PA_ENC_TS_ENC ts_enc;
1871 long time_diff;
1873 if (cp->freshness_token == NULL) {
1874 if (r->config->require_pkinit_freshness) {
1875 ret = KRB5KDC_ERR_PREAUTH_FAILED;
1876 kdc_log(r->context, r->config, 0, "PKINIT request is missing required freshness token");
1879 return ret;
1882 token_data = cp->freshness_token->data;
1883 token_len = cp->freshness_token->length;
1885 /* Ensure that the token be not empty. */
1886 if (token_data == NULL) {
1887 kdc_log(r->context, r->config, 0, "Got empty freshness token");
1888 return KRB5KDC_ERR_PREAUTH_FAILED;
1891 /* Ensure that the two leading bytes are zero. */
1892 if (token_len < 2 || token_data[0] || token_data[1]) {
1893 kdc_log(r->context, r->config, 0, "Freshness token contains invalid data");
1894 return KRB5KRB_AP_ERR_MODIFIED;
1897 /* Decrypt the freshness token. */
1899 remaining_token_data = token_data + 2;
1900 remaining_len = token_len - 2;
1902 ret = decode_EncryptedData(remaining_token_data, remaining_len, &enc_data, &size);
1903 if (ret) {
1904 kdc_log(r->context, r->config, 0, "Failed to decode freshness token");
1905 return KRB5KRB_AP_ERR_MODIFIED;
1907 if (size != remaining_len) {
1908 kdc_log(r->context, r->config, 0, "Trailing data in EncryptedData of freshness token");
1909 free_EncryptedData(&enc_data);
1910 return KRB5KRB_AP_ERR_MODIFIED;
1913 krbtgt = (r->krbtgt != NULL) ? r->krbtgt : r->server;
1914 kvno = (enc_data.kvno != NULL) ? *enc_data.kvno : 0;
1916 /* We will only accept freshness tokens signed by our local krbtgt. */
1917 keys = hdb_kvno2keys(r->context, krbtgt, kvno);
1918 if (keys == NULL) {
1919 kdc_log(r->context, r->config, 0,
1920 "No key with kvno %"PRId32" to decrypt freshness token",
1921 kvno);
1922 free_EncryptedData(&enc_data);
1923 return KRB5KDC_ERR_PREAUTH_FAILED;
1926 ret = hdb_enctype2key(r->context, r->client, keys,
1927 enc_data.etype, &key);
1928 if (ret) {
1929 kdc_log(r->context, r->config, 0,
1930 "No key with kvno %"PRId32", enctype %d to decrypt freshness token",
1931 kvno, enc_data.etype);
1932 free_EncryptedData(&enc_data);
1933 return KRB5KDC_ERR_PREAUTH_FAILED;
1936 ret = krb5_crypto_init(r->context, &key->key, 0, &crypto);
1937 if (ret) {
1938 const char *msg = krb5_get_error_message(r->context, ret);
1939 kdc_log(r->context, r->config, 0,
1940 "While attempting to decrypt freshness token, krb5_crypto_init failed: %s", msg);
1941 krb5_free_error_message(r->context, msg);
1943 free_EncryptedData(&enc_data);
1944 return ret;
1947 ret = krb5_decrypt_EncryptedData(r->context,
1948 crypto,
1949 KRB5_KU_AS_FRESHNESS,
1950 &enc_data,
1951 &ts_data);
1952 krb5_crypto_destroy(r->context, crypto);
1953 free_EncryptedData(&enc_data);
1954 if (ret) {
1955 kdc_log(r->context, r->config, 0, "Failed to decrypt freshness token");
1957 free_EncryptedData(&enc_data);
1958 return KRB5KRB_AP_ERR_MODIFIED;
1961 /* Decode the timestamp. */
1963 ret = decode_PA_ENC_TS_ENC(ts_data.data,
1964 ts_data.length,
1965 &ts_enc,
1966 &size);
1967 if (ret) {
1968 kdc_log(r->context, r->config, 0, "Failed to decode PA-ENC-TS-ENC in freshness token");
1969 krb5_data_free(&ts_data);
1970 return KRB5KRB_AP_ERR_MODIFIED;
1972 if (size != ts_data.length) {
1973 kdc_log(r->context, r->config, 0, "Trailing data in PA-ENC-TS-ENC of freshness token");
1974 free_PA_ENC_TS_ENC(&ts_enc);
1975 krb5_data_free(&ts_data);
1976 return KRB5KRB_AP_ERR_MODIFIED;
1978 krb5_data_free(&ts_data);
1980 time_diff = labs(kdc_time - ts_enc.patimestamp);
1981 if (time_diff > r->context->max_skew) {
1982 char token_time[100];
1984 krb5_format_time(r->context, ts_enc.patimestamp,
1985 token_time, sizeof(token_time), TRUE);
1987 kdc_log(r->context, r->config, 4, "Freshness token has too large time skew: "
1988 "time in token %s is out by %ld > %jd seconds — %s",
1989 token_time,
1990 time_diff,
1991 (intmax_t)(r->context->max_skew),
1992 r->cname);
1994 r->e_text = NULL;
1995 free_PA_ENC_TS_ENC(&ts_enc);
1996 return KRB5_KDC_ERR_PREAUTH_EXPIRED;
1999 r->pkinit_freshness_used = TRUE;
2001 free_PA_ENC_TS_ENC(&ts_enc);
2002 return 0;
2005 static krb5_error_code
2006 add_principal_mapping(krb5_context context,
2007 const char *principal_name,
2008 const char * subject)
2010 struct pk_allowed_princ *tmp;
2011 krb5_principal principal;
2012 krb5_error_code ret;
2014 tmp = realloc(principal_mappings.val,
2015 (principal_mappings.len + 1) * sizeof(*tmp));
2016 if (tmp == NULL)
2017 return ENOMEM;
2018 principal_mappings.val = tmp;
2020 ret = krb5_parse_name(context, principal_name, &principal);
2021 if (ret)
2022 return ret;
2024 principal_mappings.val[principal_mappings.len].principal = principal;
2026 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
2027 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
2028 krb5_free_principal(context, principal);
2029 return ENOMEM;
2031 principal_mappings.len++;
2033 return 0;
2036 krb5_error_code
2037 _kdc_add_initial_verified_cas(krb5_context context,
2038 krb5_kdc_configuration *config,
2039 pk_client_params *cp,
2040 EncTicketPart *tkt)
2042 AD_INITIAL_VERIFIED_CAS cas;
2043 krb5_error_code ret;
2044 krb5_data data;
2045 size_t size = 0;
2047 memset(&cas, 0, sizeof(cas));
2049 /* XXX add CAs to cas here */
2051 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
2052 &cas, &size, ret);
2053 if (ret)
2054 return ret;
2055 if (data.length != size)
2056 krb5_abortx(context, "internal asn.1 encoder error");
2058 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
2059 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
2060 &data);
2061 krb5_data_free(&data);
2062 return ret;
2069 static void
2070 load_mappings(krb5_context context, const char *fn)
2072 krb5_error_code ret;
2073 char buf[1024];
2074 unsigned long lineno = 0;
2075 FILE *f;
2077 f = fopen(fn, "r");
2078 if (f == NULL)
2079 return;
2081 while (fgets(buf, sizeof(buf), f) != NULL) {
2082 char *subject_name, *p;
2084 buf[strcspn(buf, "\n")] = '\0';
2085 lineno++;
2087 p = buf + strspn(buf, " \t");
2089 if (*p == '#' || *p == '\0')
2090 continue;
2092 subject_name = strchr(p, ':');
2093 if (subject_name == NULL) {
2094 krb5_warnx(context, "pkinit mapping file line %lu "
2095 "missing \":\" :%s",
2096 lineno, buf);
2097 continue;
2099 *subject_name++ = '\0';
2101 ret = add_principal_mapping(context, p, subject_name);
2102 if (ret) {
2103 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
2104 lineno, buf);
2105 continue;
2109 fclose(f);
2116 KDC_LIB_FUNCTION krb5_error_code KDC_LIB_CALL
2117 krb5_kdc_pk_initialize(krb5_context context,
2118 krb5_kdc_configuration *config,
2119 const char *user_id,
2120 const char *anchors,
2121 char **pool,
2122 char **revoke_list)
2124 const char *file;
2125 char *fn = NULL;
2126 krb5_error_code ret;
2128 file = krb5_config_get_string(context, NULL,
2129 "libdefaults", "moduli", NULL);
2131 ret = _krb5_parse_moduli(context, file, &moduli);
2132 if (ret)
2133 krb5_err(context, 1, ret, "PKINIT: failed to load moduli file");
2135 principal_mappings.len = 0;
2136 principal_mappings.val = NULL;
2138 ret = _krb5_pk_load_id(context,
2139 &kdc_identity,
2140 user_id,
2141 anchors,
2142 pool,
2143 revoke_list,
2144 NULL,
2145 NULL,
2146 NULL);
2147 if (ret) {
2148 krb5_warn(context, ret, "PKINIT: failed to load ID");
2149 config->enable_pkinit = 0;
2150 return ret;
2154 hx509_query *q;
2155 hx509_cert cert;
2157 ret = hx509_query_alloc(context->hx509ctx, &q);
2158 if (ret) {
2159 krb5_warnx(context, "PKINIT: out of memory");
2160 return ENOMEM;
2163 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
2164 if (config->pkinit_kdc_friendly_name)
2165 hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name);
2167 ret = hx509_certs_find(context->hx509ctx,
2168 kdc_identity->certs,
2170 &cert);
2171 hx509_query_free(context->hx509ctx, q);
2172 if (ret == 0) {
2173 if (hx509_cert_check_eku(context->hx509ctx, cert,
2174 &asn1_oid_id_pkkdcekuoid, 0)) {
2175 hx509_name name;
2176 char *str;
2177 ret = hx509_cert_get_subject(cert, &name);
2178 if (ret == 0) {
2179 hx509_name_to_string(name, &str);
2180 krb5_warnx(context, "WARNING Found KDC certificate (%s) "
2181 "is missing the PKINIT KDC EKU, this is bad for "
2182 "interoperability.", str);
2183 hx509_name_free(&name);
2184 free(str);
2187 hx509_cert_free(cert);
2188 } else
2189 krb5_warnx(context, "PKINIT: failed to find a signing "
2190 "certificate with a public key");
2193 if (krb5_config_get_bool_default(context,
2194 NULL,
2195 FALSE,
2196 "kdc",
2197 "pkinit_allow_proxy_certificate",
2198 NULL))
2199 config->pkinit_allow_proxy_certs = 1;
2201 file = krb5_config_get_string(context,
2202 NULL,
2203 "kdc",
2204 "pkinit_mappings_file",
2205 NULL);
2206 if (file == NULL) {
2207 int aret;
2209 aret = asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
2210 if (aret == -1) {
2211 krb5_warnx(context, "PKINIT: out of memory");
2212 return ENOMEM;
2215 file = fn;
2218 load_mappings(context, file);
2219 if (fn)
2220 free(fn);
2222 return 0;
2225 #endif /* PKINIT */