2 * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #include <heim_asn1.h>
41 #include <rfc2459_asn1.h>
43 #include <pkinit_asn1.h>
46 #include "crypto-headers.h"
48 struct pk_client_params {
49 enum krb5_pk_type type;
50 BIGNUM *dh_public_key;
54 EncryptionKey reply_key;
57 hx509_certs client_anchors;
60 struct pk_principal_mapping {
62 struct pk_allowed_princ {
63 krb5_principal principal;
68 static struct krb5_pk_identity *kdc_identity;
69 static struct pk_principal_mapping principal_mappings;
70 static struct krb5_dh_moduli **moduli;
82 static krb5_error_code
83 pk_check_pkauthenticator_win2k(krb5_context context,
84 PKAuthenticator_Win2k *a,
89 krb5_timeofday (context, &now);
92 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
93 krb5_clear_error_string(context);
94 return KRB5KRB_AP_ERR_SKEW;
99 static krb5_error_code
100 pk_check_pkauthenticator(krb5_context context,
111 krb5_timeofday (context, &now);
114 if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
115 krb5_clear_error_string(context);
116 return KRB5KRB_AP_ERR_SKEW;
119 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
121 krb5_clear_error_string(context);
125 krb5_abortx(context, "Internal error in ASN.1 encoder");
127 ret = krb5_create_checksum(context,
136 krb5_clear_error_string(context);
140 if (a->paChecksum == NULL) {
141 krb5_clear_error_string(context);
142 ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
146 if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
147 krb5_clear_error_string(context);
148 ret = KRB5KRB_ERR_GENERIC;
152 free_Checksum(&checksum);
158 _kdc_pk_free_client_param(krb5_context context,
159 pk_client_params *client_params)
161 if (client_params->cert)
162 hx509_cert_free(client_params->cert);
163 if (client_params->dh)
164 DH_free(client_params->dh);
165 if (client_params->dh_public_key)
166 BN_free(client_params->dh_public_key);
167 krb5_free_keyblock_contents(context, &client_params->reply_key);
168 if (client_params->dh_group_name)
169 free(client_params->dh_group_name);
170 if (client_params->peer)
171 hx509_peer_info_free(client_params->peer);
172 if (client_params->client_anchors)
173 hx509_certs_free(&client_params->client_anchors);
174 memset(client_params, 0, sizeof(*client_params));
178 static krb5_error_code
179 generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
180 krb5_enctype enctype, krb5_keyblock *reply_key)
182 unsigned char *dh_gen_key = NULL;
185 size_t dh_gen_keylen, size;
187 memset(&key, 0, sizeof(key));
189 if (!DH_generate_key(client_params->dh)) {
190 ret = KRB5KRB_ERR_GENERIC;
191 krb5_set_error_message(context, ret, "Can't generate Diffie-Hellman keys");
194 if (client_params->dh_public_key == NULL) {
195 ret = KRB5KRB_ERR_GENERIC;
196 krb5_set_error_message(context, ret, "dh_public_key");
200 dh_gen_keylen = DH_size(client_params->dh);
201 size = BN_num_bytes(client_params->dh->p);
202 if (size < dh_gen_keylen)
203 size = dh_gen_keylen;
205 dh_gen_key = malloc(size);
206 if (dh_gen_key == NULL) {
208 krb5_set_error_message(context, ret, "malloc: out of memory");
211 memset(dh_gen_key, 0, size - dh_gen_keylen);
213 dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
214 client_params->dh_public_key,
216 if (dh_gen_keylen == -1) {
217 ret = KRB5KRB_ERR_GENERIC;
218 krb5_set_error_message(context, ret, "Can't compute Diffie-Hellman key");
222 ret = _krb5_pk_octetstring2key(context,
224 dh_gen_key, dh_gen_keylen,
231 if (key.keyvalue.data)
232 krb5_free_keyblock_contents(context, &key);
238 integer_to_BN(krb5_context context, const char *field, heim_integer *f)
242 bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
244 krb5_set_error_message(context, KRB5_BADMSGTYPE,
245 "PKINIT: parsing BN failed %s", field);
248 BN_set_negative(bn, f->negative);
252 static krb5_error_code
253 get_dh_param(krb5_context context,
254 krb5_kdc_configuration *config,
255 SubjectPublicKeyInfo *dh_key_info,
256 pk_client_params *client_params)
258 DomainParameters dhparam;
262 memset(&dhparam, 0, sizeof(dhparam));
264 if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
265 krb5_set_error_message(context, KRB5_BADMSGTYPE,
266 "PKINIT invalid oid in clientPublicValue");
267 return KRB5_BADMSGTYPE;
270 if (dh_key_info->algorithm.parameters == NULL) {
271 krb5_set_error_message(context, KRB5_BADMSGTYPE,
272 "PKINIT missing algorithm parameter "
273 "in clientPublicValue");
274 return KRB5_BADMSGTYPE;
277 ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
278 dh_key_info->algorithm.parameters->length,
282 krb5_set_error_message(context, ret, "Can't decode algorithm "
283 "parameters in clientPublicValue");
287 if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
288 ret = KRB5_BADMSGTYPE;
289 krb5_set_error_message(context, ret,
290 "PKINIT: subjectPublicKey not aligned "
291 "to 8 bit boundary");
296 ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
297 &dhparam.p, &dhparam.g, &dhparam.q, moduli,
298 &client_params->dh_group_name);
300 /* XXX send back proposal of better group */
307 krb5_set_error_message(context, ret, "Cannot create DH structure");
310 ret = KRB5_BADMSGTYPE;
311 dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
314 dh->g = integer_to_BN(context, "DH base", &dhparam.g);
317 dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
325 ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
326 dh_key_info->subjectPublicKey.length / 8,
330 krb5_clear_error_string(context);
334 client_params->dh_public_key = integer_to_BN(context,
337 der_free_heim_integer(&glue);
338 if (client_params->dh_public_key == NULL) {
339 ret = KRB5_BADMSGTYPE;
344 client_params->dh = dh;
351 free_DomainParameters(&dhparam);
356 _kdc_pk_rd_padata(krb5_context context,
357 krb5_kdc_configuration *config,
360 pk_client_params **ret_params)
362 pk_client_params *client_params;
364 heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
365 krb5_data eContent = { 0, NULL };
366 krb5_data signed_content = { 0, NULL };
367 const char *type = "unknown type";
372 if (!config->enable_pkinit) {
373 kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
374 krb5_clear_error_string(context);
378 hx509_verify_set_time(kdc_identity->verify_ctx, kdc_time);
380 client_params = calloc(1, sizeof(*client_params));
381 if (client_params == NULL) {
382 krb5_clear_error_string(context);
387 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
388 PA_PK_AS_REQ_Win2k r;
390 type = "PK-INIT-Win2k";
392 ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
393 pa->padata_value.length,
397 krb5_set_error_message(context, ret, "Can't decode "
398 "PK-AS-REQ-Win2k: %d", ret);
402 ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
406 free_PA_PK_AS_REQ_Win2k(&r);
408 krb5_set_error_message(context, ret,
409 "Can't decode PK-AS-REQ: %d", ret);
413 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
416 type = "PK-INIT-IETF";
418 ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
419 pa->padata_value.length,
423 krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret);
427 /* XXX look at r.kdcPkId */
428 if (r.trustedCertifiers) {
429 ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
432 ret = hx509_certs_init(kdc_identity->hx509ctx,
433 "MEMORY:client-anchors",
435 &client_params->client_anchors);
437 krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret);
441 for (i = 0; i < edi->len; i++) {
442 IssuerAndSerialNumber iasn;
447 if (edi->val[i].issuerAndSerialNumber == NULL)
450 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
452 krb5_set_error_message(context, ret,
453 "Failed to allocate hx509_query");
457 ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
458 edi->val[i].issuerAndSerialNumber->length,
462 hx509_query_free(kdc_identity->hx509ctx, q);
465 ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
466 free_IssuerAndSerialNumber(&iasn);
470 ret = hx509_certs_find(kdc_identity->hx509ctx,
474 hx509_query_free(kdc_identity->hx509ctx, q);
477 hx509_certs_add(kdc_identity->hx509ctx,
478 client_params->client_anchors, cert);
479 hx509_cert_free(cert);
483 ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
487 free_PA_PK_AS_REQ(&r);
489 krb5_set_error_message(context, ret,
490 "Can't unwrap ContentInfo: %d", ret);
495 krb5_clear_error_string(context);
496 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
500 ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
502 ret = KRB5KRB_ERR_GENERIC;
503 krb5_set_error_message(context, ret,
504 "PK-AS-REQ-Win2k invalid content type oid");
509 ret = KRB5KRB_ERR_GENERIC;
510 krb5_set_error_message(context, ret,
511 "PK-AS-REQ-Win2k no signed auth pack");
516 hx509_certs signer_certs;
518 ret = hx509_cms_verify_signed(kdc_identity->hx509ctx,
519 kdc_identity->verify_ctx,
521 signed_content.length,
523 kdc_identity->certpool,
528 char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret);
529 krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
535 ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs,
536 &client_params->cert);
537 hx509_certs_free(&signer_certs);
542 /* Signature is correct, now verify the signed message */
543 if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 &&
544 der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0)
546 ret = KRB5_BADMSGTYPE;
547 krb5_set_error_message(context, ret, "got wrong oid for pkauthdata");
551 if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
554 ret = decode_AuthPack_Win2k(eContent.data,
559 krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret);
563 ret = pk_check_pkauthenticator_win2k(context,
567 free_AuthPack_Win2k(&ap);
571 client_params->type = PKINIT_WIN2K;
572 client_params->nonce = ap.pkAuthenticator.nonce;
574 if (ap.clientPublicValue) {
575 ret = KRB5KRB_ERR_GENERIC;
576 krb5_set_error_message(context, ret, "DH not supported for windows");
579 free_AuthPack_Win2k(&ap);
581 } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
584 ret = decode_AuthPack(eContent.data,
589 krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret);
594 ret = pk_check_pkauthenticator(context,
602 client_params->type = PKINIT_27;
603 client_params->nonce = ap.pkAuthenticator.nonce;
605 if (ap.clientPublicValue) {
606 ret = get_dh_param(context, config,
607 ap.clientPublicValue, client_params);
614 if (ap.supportedCMSTypes) {
615 ret = hx509_peer_info_alloc(kdc_identity->hx509ctx,
616 &client_params->peer);
621 ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx,
623 ap.supportedCMSTypes->val,
624 ap.supportedCMSTypes->len);
632 krb5_abortx(context, "internal pkinit error");
634 kdc_log(context, config, 0, "PK-INIT request of type %s", type);
638 krb5_warn(context, ret, "PKINIT");
640 if (signed_content.data)
641 free(signed_content.data);
642 krb5_data_free(&eContent);
643 der_free_oid(&eContentType);
644 der_free_oid(&contentInfoOid);
646 _kdc_pk_free_client_param(context, client_params);
648 *ret_params = client_params;
656 static krb5_error_code
657 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
659 integer->length = BN_num_bytes(bn);
660 integer->data = malloc(integer->length);
661 if (integer->data == NULL) {
662 krb5_clear_error_string(context);
665 BN_bn2bin(bn, integer->data);
666 integer->negative = BN_is_negative(bn);
670 static krb5_error_code
671 pk_mk_pa_reply_enckey(krb5_context context,
672 krb5_kdc_configuration *config,
673 pk_client_params *client_params,
675 const krb5_data *req_buffer,
676 krb5_keyblock *reply_key,
677 ContentInfo *content_info)
679 const heim_oid *envelopedAlg = NULL, *sdAlg = NULL;
681 krb5_data buf, signed_data;
685 krb5_data_zero(&buf);
686 krb5_data_zero(&signed_data);
689 * If the message client is a win2k-type but it send pa data
690 * 09-binding it expects a IETF (checksum) reply so there can be
694 switch (client_params->type) {
697 if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
698 && config->pkinit_require_binding == 0)
707 krb5_abortx(context, "internal pkinit error");
711 ReplyKeyPack_Win2k kp;
712 memset(&kp, 0, sizeof(kp));
714 envelopedAlg = oid_id_rsadsi_des_ede3_cbc();
715 sdAlg = oid_id_pkcs7_data();
717 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
719 krb5_clear_error_string(context);
722 kp.nonce = client_params->nonce;
724 ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
725 buf.data, buf.length,
727 free_ReplyKeyPack_Win2k(&kp);
729 krb5_crypto ascrypto;
731 memset(&kp, 0, sizeof(kp));
733 sdAlg = oid_id_pkrkeydata();
735 ret = copy_EncryptionKey(reply_key, &kp.replyKey);
737 krb5_clear_error_string(context);
741 ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
743 krb5_clear_error_string(context);
747 ret = krb5_create_checksum(context, ascrypto, 6, 0,
748 req_buffer->data, req_buffer->length,
751 krb5_clear_error_string(context);
755 ret = krb5_crypto_destroy(context, ascrypto);
757 krb5_clear_error_string(context);
760 ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
761 free_ReplyKeyPack(&kp);
764 krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack "
768 if (buf.length != size)
769 krb5_abortx(context, "Internal ASN.1 encoder error");
775 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
779 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
780 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
782 ret = hx509_certs_find(kdc_identity->hx509ctx,
786 hx509_query_free(kdc_identity->hx509ctx, q);
790 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
798 client_params->client_anchors,
799 kdc_identity->certpool,
801 hx509_cert_free(cert);
804 krb5_data_free(&buf);
808 if (client_params->type == PKINIT_WIN2K) {
809 ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
814 krb5_data_free(&signed_data);
818 ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
821 signed_data.data, signed_data.length,
823 oid_id_pkcs7_signedData(), &buf);
827 ret = _krb5_pk_mk_ContentInfo(context,
829 oid_id_pkcs7_envelopedData(),
832 krb5_data_free(&buf);
833 krb5_data_free(&signed_data);
841 static krb5_error_code
842 pk_mk_pa_reply_dh(krb5_context context,
844 pk_client_params *client_params,
845 krb5_keyblock *reply_key,
846 ContentInfo *content_info,
847 hx509_cert *kdc_cert)
849 KDCDHKeyInfo dh_info;
850 krb5_data signed_data, buf;
851 ContentInfo contentinfo;
856 memset(&contentinfo, 0, sizeof(contentinfo));
857 memset(&dh_info, 0, sizeof(dh_info));
858 krb5_data_zero(&buf);
859 krb5_data_zero(&signed_data);
863 ret = BN_to_integer(context, kdc_dh->pub_key, &i);
867 ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
869 krb5_set_error_message(context, ret, "ASN.1 encoding of "
870 "DHPublicKey failed (%d)", ret);
873 if (buf.length != size)
874 krb5_abortx(context, "Internal ASN.1 encoder error");
876 dh_info.subjectPublicKey.length = buf.length * 8;
877 dh_info.subjectPublicKey.data = buf.data;
879 dh_info.nonce = client_params->nonce;
881 ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
884 krb5_set_error_message(context, ret, "ASN.1 encoding of "
885 "KdcDHKeyInfo failed (%d)", ret);
888 if (buf.length != size)
889 krb5_abortx(context, "Internal ASN.1 encoder error");
892 * Create the SignedData structure and sign the KdcDHKeyInfo
900 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
904 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
905 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
907 ret = hx509_certs_find(kdc_identity->hx509ctx,
911 hx509_query_free(kdc_identity->hx509ctx, q);
915 ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
917 oid_id_pkdhkeydata(),
923 client_params->client_anchors,
924 kdc_identity->certpool,
931 ret = _krb5_pk_mk_ContentInfo(context,
933 oid_id_pkcs7_signedData(),
939 if (ret && *kdc_cert) {
940 hx509_cert_free(*kdc_cert);
944 krb5_data_free(&buf);
945 krb5_data_free(&signed_data);
946 free_KDCDHKeyInfo(&dh_info);
956 _kdc_pk_mk_pa_reply(krb5_context context,
957 krb5_kdc_configuration *config,
958 pk_client_params *client_params,
959 const hdb_entry_ex *client,
961 const krb5_data *req_buffer,
962 krb5_keyblock **reply_key,
968 krb5_enctype enctype;
970 hx509_cert kdc_cert = NULL;
973 if (!config->enable_pkinit) {
974 krb5_clear_error_string(context);
978 if (req->req_body.etype.len > 0) {
979 for (i = 0; i < req->req_body.etype.len; i++)
980 if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
982 if (req->req_body.etype.len <= i) {
983 ret = KRB5KRB_ERR_GENERIC;
984 krb5_set_error_message(context, ret,
985 "No valid enctype available from client");
988 enctype = req->req_body.etype.val[i];
990 enctype = ETYPE_DES3_CBC_SHA1;
992 if (client_params->type == PKINIT_27) {
994 const char *type, *other = "";
996 memset(&rep, 0, sizeof(rep));
998 pa_type = KRB5_PADATA_PK_AS_REP;
1000 if (client_params->dh == NULL) {
1005 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1007 ret = krb5_generate_random_keyblock(context, enctype,
1008 &client_params->reply_key);
1010 free_PA_PK_AS_REP(&rep);
1013 ret = pk_mk_pa_reply_enckey(context,
1018 &client_params->reply_key,
1021 free_PA_PK_AS_REP(&rep);
1024 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1025 rep.u.encKeyPack.length, &info, &size,
1027 free_ContentInfo(&info);
1029 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1031 free_PA_PK_AS_REP(&rep);
1034 if (rep.u.encKeyPack.length != size)
1035 krb5_abortx(context, "Internal ASN.1 encoder error");
1041 if (client_params->dh_group_name)
1042 other = client_params->dh_group_name;
1044 rep.element = choice_PA_PK_AS_REP_dhInfo;
1046 ret = generate_dh_keyblock(context, client_params, enctype,
1047 &client_params->reply_key);
1051 ret = pk_mk_pa_reply_dh(context, client_params->dh,
1053 &client_params->reply_key,
1057 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
1058 rep.u.dhInfo.dhSignedData.length, &info, &size,
1060 free_ContentInfo(&info);
1062 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1064 free_PA_PK_AS_REP(&rep);
1067 if (rep.u.encKeyPack.length != size)
1068 krb5_abortx(context, "Internal ASN.1 encoder error");
1072 free_PA_PK_AS_REP(&rep);
1076 ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
1077 free_PA_PK_AS_REP(&rep);
1079 krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d",
1084 krb5_abortx(context, "Internal ASN.1 encoder error");
1086 kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
1088 } else if (client_params->type == PKINIT_WIN2K) {
1089 PA_PK_AS_REP_Win2k rep;
1092 if (client_params->dh) {
1093 ret = KRB5KRB_ERR_GENERIC;
1094 krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH");
1098 memset(&rep, 0, sizeof(rep));
1100 pa_type = KRB5_PADATA_PK_AS_REP_19;
1101 rep.element = choice_PA_PK_AS_REP_encKeyPack;
1103 ret = krb5_generate_random_keyblock(context, enctype,
1104 &client_params->reply_key);
1106 free_PA_PK_AS_REP_Win2k(&rep);
1109 ret = pk_mk_pa_reply_enckey(context,
1114 &client_params->reply_key,
1117 free_PA_PK_AS_REP_Win2k(&rep);
1120 ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
1121 rep.u.encKeyPack.length, &info, &size,
1123 free_ContentInfo(&info);
1125 krb5_set_error_message(context, ret, "encoding of Key ContentInfo "
1127 free_PA_PK_AS_REP_Win2k(&rep);
1130 if (rep.u.encKeyPack.length != size)
1131 krb5_abortx(context, "Internal ASN.1 encoder error");
1133 ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
1134 free_PA_PK_AS_REP_Win2k(&rep);
1136 krb5_set_error_message(context, ret,
1137 "encode PA-PK-AS-REP-Win2k failed %d", ret);
1141 krb5_abortx(context, "Internal ASN.1 encoder error");
1144 krb5_abortx(context, "PK-INIT internal error");
1147 ret = krb5_padata_add(context, md, pa_type, buf, len);
1149 krb5_set_error_message(context, ret, "failed adding PA-PK-AS-REP %d", ret);
1154 if (config->pkinit_kdc_ocsp_file) {
1156 if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
1160 krb5_data_free(&ocsp.data);
1163 ocsp.next_update = kdc_time + 60 * 5;
1165 fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
1167 kdc_log(context, config, 0,
1168 "PK-INIT failed to open ocsp data file %d", errno);
1171 ret = fstat(fd, &sb);
1175 kdc_log(context, config, 0,
1176 "PK-INIT failed to stat ocsp data %d", ret);
1180 ret = krb5_data_alloc(&ocsp.data, sb.st_size);
1183 kdc_log(context, config, 0,
1184 "PK-INIT failed to stat ocsp data %d", ret);
1187 ocsp.data.length = sb.st_size;
1188 ret = read(fd, ocsp.data.data, sb.st_size);
1190 if (ret != sb.st_size) {
1191 kdc_log(context, config, 0,
1192 "PK-INIT failed to read ocsp data %d", errno);
1196 ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
1200 ocsp.data.data, ocsp.data.length,
1203 kdc_log(context, config, 0,
1204 "PK-INIT failed to verify ocsp data %d", ret);
1205 krb5_data_free(&ocsp.data);
1207 } else if (ocsp.expire > 180) {
1208 ocsp.expire -= 180; /* refetch the ocsp before it expire */
1209 ocsp.next_update = ocsp.expire;
1211 ocsp.next_update = kdc_time;
1217 if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
1219 ret = krb5_padata_add(context, md,
1220 KRB5_PADATA_PA_PK_OCSP_RESPONSE,
1221 ocsp.data.data, ocsp.data.length);
1223 krb5_set_error_message(context, ret,
1224 "Failed adding OCSP response %d", ret);
1232 hx509_cert_free(kdc_cert);
1235 *reply_key = &client_params->reply_key;
1240 match_rfc_san(krb5_context context,
1241 krb5_kdc_configuration *config,
1242 hx509_context hx509ctx,
1243 hx509_cert client_cert,
1244 krb5_const_principal match)
1246 hx509_octet_string_list list;
1247 int ret, i, found = 0;
1249 memset(&list, 0 , sizeof(list));
1251 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1253 oid_id_pkinit_san(),
1258 for (i = 0; !found && i < list.len; i++) {
1259 krb5_principal_data principal;
1260 KRB5PrincipalName kn;
1263 ret = decode_KRB5PrincipalName(list.val[i].data,
1267 kdc_log(context, config, 0,
1268 "Decoding kerberos name in certificate failed: %s",
1269 krb5_get_err_text(context, ret));
1272 if (size != list.val[i].length) {
1273 kdc_log(context, config, 0,
1274 "Decoding kerberos name have extra bits on the end");
1275 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1278 principal.name = kn.principalName;
1279 principal.realm = kn.realm;
1281 if (krb5_principal_compare(context, &principal, match) == TRUE)
1283 free_KRB5PrincipalName(&kn);
1287 hx509_free_octet_string_list(&list);
1292 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1298 match_ms_upn_san(krb5_context context,
1299 krb5_kdc_configuration *config,
1300 hx509_context hx509ctx,
1301 hx509_cert client_cert,
1302 krb5_const_principal match)
1304 hx509_octet_string_list list;
1305 krb5_principal principal = NULL;
1310 memset(&list, 0 , sizeof(list));
1312 ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
1314 oid_id_pkinit_ms_san(),
1319 if (list.len != 1) {
1320 kdc_log(context, config, 0,
1321 "More then one PK-INIT MS UPN SAN");
1325 ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
1327 kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
1331 kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
1333 ret = krb5_parse_name(context, upn, &principal);
1334 free_MS_UPN_SAN(&upn);
1336 kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
1341 * This is very wrong, but will do for now, should really and a
1342 * plugin to the windc layer to very this ACL.
1344 strupr(principal->realm);
1346 if (krb5_principal_compare(context, principal, match) == TRUE)
1351 krb5_free_principal(context, principal);
1352 hx509_free_octet_string_list(&list);
1357 return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1363 _kdc_pk_check_client(krb5_context context,
1364 krb5_kdc_configuration *config,
1365 const hdb_entry_ex *client,
1366 pk_client_params *client_params,
1367 char **subject_name)
1369 const HDB_Ext_PKINIT_acl *acl;
1370 krb5_error_code ret;
1374 ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
1375 client_params->cert,
1380 ret = hx509_name_to_string(name, subject_name);
1381 hx509_name_free(&name);
1385 kdc_log(context, config, 0,
1386 "Trying to authorize PK-INIT subject DN %s",
1389 if (config->pkinit_princ_in_cert) {
1390 ret = match_rfc_san(context, config,
1391 kdc_identity->hx509ctx,
1392 client_params->cert,
1393 client->entry.principal);
1395 kdc_log(context, config, 5,
1396 "Found matching PK-INIT SAN in certificate");
1399 ret = match_ms_upn_san(context, config,
1400 kdc_identity->hx509ctx,
1401 client_params->cert,
1402 client->entry.principal);
1404 kdc_log(context, config, 5,
1405 "Found matching MS UPN SAN in certificate");
1410 ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
1411 if (ret == 0 && acl != NULL) {
1413 * Cheat here and compare the generated name with the string
1414 * and not the reverse.
1416 for (i = 0; i < acl->len; i++) {
1417 if (strcmp(*subject_name, acl->val[0].subject) != 0)
1420 /* Don't support isser and anchor checking right now */
1421 if (acl->val[0].issuer)
1423 if (acl->val[0].anchor)
1426 kdc_log(context, config, 5,
1427 "Found matching PK-INIT database ACL");
1432 for (i = 0; i < principal_mappings.len; i++) {
1435 b = krb5_principal_compare(context,
1436 client->entry.principal,
1437 principal_mappings.val[i].principal);
1440 if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
1442 kdc_log(context, config, 5,
1443 "Found matching PK-INIT FILE ACL");
1447 ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
1448 krb5_set_error_message(context, ret,
1449 "PKINIT no matching principals for %s",
1452 kdc_log(context, config, 5,
1453 "PKINIT no matching principals for %s",
1456 free(*subject_name);
1457 *subject_name = NULL;
1462 static krb5_error_code
1463 add_principal_mapping(krb5_context context,
1464 const char *principal_name,
1465 const char * subject)
1467 struct pk_allowed_princ *tmp;
1468 krb5_principal principal;
1469 krb5_error_code ret;
1471 tmp = realloc(principal_mappings.val,
1472 (principal_mappings.len + 1) * sizeof(*tmp));
1475 principal_mappings.val = tmp;
1477 ret = krb5_parse_name(context, principal_name, &principal);
1481 principal_mappings.val[principal_mappings.len].principal = principal;
1483 principal_mappings.val[principal_mappings.len].subject = strdup(subject);
1484 if (principal_mappings.val[principal_mappings.len].subject == NULL) {
1485 krb5_free_principal(context, principal);
1488 principal_mappings.len++;
1494 _kdc_add_inital_verified_cas(krb5_context context,
1495 krb5_kdc_configuration *config,
1496 pk_client_params *params,
1499 AD_INITIAL_VERIFIED_CAS cas;
1500 krb5_error_code ret;
1504 memset(&cas, 0, sizeof(cas));
1506 /* XXX add CAs to cas here */
1508 ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
1512 if (data.length != size)
1513 krb5_abortx(context, "internal asn.1 encoder error");
1515 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
1516 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
1518 krb5_data_free(&data);
1527 load_mappings(krb5_context context, const char *fn)
1529 krb5_error_code ret;
1531 unsigned long lineno = 0;
1538 while (fgets(buf, sizeof(buf), f) != NULL) {
1539 char *subject_name, *p;
1541 buf[strcspn(buf, "\n")] = '\0';
1544 p = buf + strspn(buf, " \t");
1546 if (*p == '#' || *p == '\0')
1549 subject_name = strchr(p, ':');
1550 if (subject_name == NULL) {
1551 krb5_warnx(context, "pkinit mapping file line %lu "
1552 "missing \":\" :%s",
1556 *subject_name++ = '\0';
1558 ret = add_principal_mapping(context, p, subject_name);
1560 krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
1574 _kdc_pk_initialize(krb5_context context,
1575 krb5_kdc_configuration *config,
1576 const char *user_id,
1577 const char *anchors,
1583 krb5_error_code ret;
1585 file = krb5_config_get_string(context, NULL,
1586 "libdefaults", "moduli", NULL);
1588 ret = _krb5_parse_moduli(context, file, &moduli);
1590 krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
1592 principal_mappings.len = 0;
1593 principal_mappings.val = NULL;
1595 ret = _krb5_pk_load_id(context,
1605 krb5_warn(context, ret, "PKINIT: ");
1606 config->enable_pkinit = 0;
1614 ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
1616 krb5_warnx(context, "PKINIT: out of memory");
1620 hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
1621 hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
1623 ret = hx509_certs_find(kdc_identity->hx509ctx,
1624 kdc_identity->certs,
1627 hx509_query_free(kdc_identity->hx509ctx, q);
1629 if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
1630 oid_id_pkkdcekuoid(), 0))
1631 krb5_warnx(context, "WARNING Found KDC certificate "
1632 "is missing the PK-INIT KDC EKU, this is bad for "
1633 "interoperability.");
1634 hx509_cert_free(cert);
1636 krb5_warnx(context, "PKINIT: failed to find a signing "
1637 "certifiate with a public key");
1640 ret = krb5_config_get_bool_default(context,
1644 "pkinit_allow_proxy_certificate",
1646 _krb5_pk_allow_proxy_certificate(kdc_identity, ret);
1648 file = krb5_config_get_string(context,
1651 "pkinit_mappings_file",
1654 asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
1658 load_mappings(context, file);