2 * Copyright (c) 2003 - 2005 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
34 #include "krb5_locl.h"
36 RCSID("$Id: pkinit.c,v 1.55 2005/05/19 18:49:05 lha Exp $");
40 #include <openssl/evp.h>
41 #include <openssl/x509.h>
42 #include <openssl/pem.h>
43 #include <openssl/err.h>
44 #include <openssl/dh.h>
45 #include <openssl/bn.h>
46 #include <openssl/engine.h>
47 #include <openssl/ui.h>
53 #include "heim_asn1.h"
54 #include "rfc2459_asn1.h"
56 #include "pkinit_asn1.h"
66 #define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R) \
69 (BL) = i2d_##T((S), NULL); \
79 (BL) = i2d_##T((S), &p); \
88 /* ENGING_load_private_key requires a UI_METHOD and data
89 * if to be usable from PAM
94 krb5_prompter_fct prompter;
98 struct krb5_pk_identity {
99 EVP_PKEY *private_key;
100 STACK_OF(X509) *cert;
101 STACK_OF(X509) *trusted_certs;
102 STACK_OF(X509_CRL) *crls;
106 struct krb5_pk_cert {
110 struct krb5_pk_init_ctx_data {
111 struct krb5_pk_identity *id;
116 void KRB5_LIB_FUNCTION
117 _krb5_pk_cert_free(struct krb5_pk_cert *cert)
120 X509_free(cert->cert);
124 static krb5_error_code
125 BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
127 integer->length = BN_num_bytes(bn);
128 integer->data = malloc(integer->length);
129 if (integer->data == NULL) {
130 krb5_clear_error_string(context);
133 BN_bn2bin(bn, integer->data);
134 integer->negative = bn->neg;
139 * UI ex_data has the callback_data as passed to Engine. This is far
140 * from being complete, we will only process one prompt
144 krb5_ui_method_read_string(UI *ui, UI_STRING *uis)
150 krb5_data password_data;
151 struct krb5_ui_data *ui_data;
153 ui_data = (struct krb5_ui_data *)UI_get_app_data(ui);
155 switch (UI_get_string_type(uis)) {
158 /* looks like the RedHat pam_prompter might handle
159 * INFO and ERROR, Will see what happens */
162 length = UI_get_result_maxsize(uis);
163 buffer = malloc(length);
164 if (buffer == NULL) {
165 krb5_set_error_string(ui_data->context, "malloc: out of memory");
168 password_data.data = buffer;
169 password_data.length = length;
171 prompt.prompt = UI_get0_output_string(uis);
172 prompt.hidden = !(UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO);
173 prompt.reply = &password_data;
174 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
176 ret = (*ui_data->prompter)(ui_data->context,
177 ui_data->prompter_data,
178 NULL, NULL, 1, &prompt);
180 buffer[length - 1] = '\0';
181 UI_set_result(ui, uis, password_data.data);
184 * RedHat pam_krb5 pam_prompter does a strdup but others
185 * may copy into buffer. XXX should we just leak the
189 if (buffer != password_data.data)
190 free(password_data.data);
191 memset (buffer, 0, length);
195 memset (buffer, 0, length);
200 /* XXX for now do not handle */
208 static krb5_error_code
209 set_digest_alg(DigestAlgorithmIdentifier *id,
211 void *param, size_t length)
215 id->parameters = malloc(sizeof(*id->parameters));
216 if (id->parameters == NULL)
218 id->parameters->data = malloc(length);
219 if (id->parameters->data == NULL) {
220 free(id->parameters);
221 id->parameters = NULL;
224 memcpy(id->parameters->data, param, length);
225 id->parameters->length = length;
227 id->parameters = NULL;
228 ret = copy_oid(oid, &id->algorithm);
230 if (id->parameters) {
231 free(id->parameters->data);
232 free(id->parameters);
233 id->parameters = NULL;
240 krb5_error_code KRB5_LIB_FUNCTION
241 _krb5_pk_create_sign(krb5_context context,
242 const heim_oid *eContentType,
244 struct krb5_pk_identity *id,
247 SignerInfo *signer_info;
249 heim_integer *serial;
257 X509_NAME *issuer_name;
259 memset(&sd, 0, sizeof(sd));
262 return HEIM_PKINIT_NO_CERTIFICATE;
263 if (id->cert == NULL)
264 return HEIM_PKINIT_NO_CERTIFICATE;
265 if (id->private_key == NULL)
266 return HEIM_PKINIT_NO_PRIVATE_KEY;
268 if (sk_X509_num(id->cert) == 0)
269 return HEIM_PKINIT_NO_CERTIFICATE;
273 sd.digestAlgorithms.len = 0;
274 sd.digestAlgorithms.val = NULL;
275 copy_oid(eContentType, &sd.encapContentInfo.eContentType);
276 ALLOC(sd.encapContentInfo.eContent, 1);
277 if (sd.encapContentInfo.eContent == NULL) {
278 krb5_clear_error_string(context);
283 ret = krb5_data_copy(&buf, eContent->data, eContent->length);
285 krb5_clear_error_string(context);
290 sd.encapContentInfo.eContent->data = buf.data;
291 sd.encapContentInfo.eContent->length = buf.length;
293 ALLOC_SEQ(&sd.signerInfos, 1);
294 if (sd.signerInfos.val == NULL) {
295 krb5_set_error_string(context, "malloc: out of memory");
300 signer_info = &sd.signerInfos.val[0];
302 user_cert = sk_X509_value(id->cert, 0);
303 if (user_cert == NULL) {
304 krb5_set_error_string(context, "pkinit: no user certificate");
305 ret = HEIM_PKINIT_NO_CERTIFICATE;
309 signer_info->version = 1;
311 issuer_name = X509_get_issuer_name(user_cert);
313 OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME,
319 krb5_set_error_string(context, "pkinit: failed encoding name");
322 ret = decode_Name(buf.data, buf.length,
323 &signer_info->sid.u.issuerAndSerialNumber.issuer,
327 krb5_set_error_string(context, "pkinit: failed to parse Name");
330 signer_info->sid.element = choice_CMSIdentifier_issuerAndSerialNumber;
332 serial = &signer_info->sid.u.issuerAndSerialNumber.serialNumber;
334 ASN1_INTEGER *isn = X509_get_serialNumber(user_cert);
335 BIGNUM *bn = ASN1_INTEGER_to_BN(isn, NULL);
338 krb5_set_error_string(context, "pkinit: failed allocating "
342 ret = BN_to_integer(context, bn, serial);
345 krb5_set_error_string(context, "pkinit: failed encoding "
351 ret = set_digest_alg(&signer_info->digestAlgorithm,
352 oid_id_secsig_sha_1(), "\x05\x00", 2);
354 krb5_set_error_string(context, "malloc: out of memory");
358 signer_info->signedAttrs = NULL;
359 signer_info->unsignedAttrs = NULL;
361 copy_oid(oid_id_pkcs1_rsaEncryption(),
362 &signer_info->signatureAlgorithm.algorithm);
363 signer_info->signatureAlgorithm.parameters = NULL;
365 buf.data = malloc(EVP_PKEY_size(id->private_key));
366 if (buf.data == NULL) {
367 krb5_set_error_string(context, "malloc: out of memory");
372 EVP_SignInit(&md, EVP_sha1());
374 sd.encapContentInfo.eContent->data,
375 sd.encapContentInfo.eContent->length);
376 ret = EVP_SignFinal(&md, buf.data, &len, id->private_key);
379 krb5_set_error_string(context, "PKINIT: failed to sign with "
381 ERR_error_string(ERR_get_error(), NULL));
386 signer_info->signature.data = buf.data;
387 signer_info->signature.length = len;
389 ALLOC_SEQ(&sd.digestAlgorithms, 1);
390 if (sd.digestAlgorithms.val == NULL) {
391 krb5_clear_error_string(context);
396 ret = set_digest_alg(&sd.digestAlgorithms.val[0],
397 oid_id_secsig_sha_1(), "\x05\x00", 2);
399 krb5_set_error_string(context, "malloc: out of memory");
403 ALLOC(sd.certificates, 1);
404 if (sd.certificates == NULL) {
405 krb5_clear_error_string(context);
410 sd.certificates->data = NULL;
411 sd.certificates->length = 0;
413 for (i = 0; i < sk_X509_num(id->cert); i++) {
416 OPENSSL_ASN1_MALLOC_ENCODE(X509,
419 sk_X509_value(id->cert, i),
422 krb5_clear_error_string(context);
425 data = realloc(sd.certificates->data,
426 sd.certificates->length + buf.length);
429 krb5_clear_error_string(context);
433 memcpy(((char *)data) + sd.certificates->length,
434 buf.data, buf.length);
435 sd.certificates->length += buf.length;
436 sd.certificates->data = data;
440 ASN1_MALLOC_ENCODE(SignedData, sd_data->data, sd_data->length,
443 krb5_set_error_string(context, "SignedData failed %d", ret);
446 if (sd_data->length != size)
447 krb5_abortx(context, "internal ASN1 encoder error");
450 free_SignedData(&sd);
455 static krb5_error_code
456 build_auth_pack_win2k(krb5_context context,
458 const KDC_REQ_BODY *body,
465 /* fill in PKAuthenticator */
466 ret = copy_PrincipalName(body->sname, &a->pkAuthenticator.kdcName);
469 ret = copy_Realm(&body->realm, &a->pkAuthenticator.kdcRealm);
473 krb5_us_timeofday(context, &sec, &usec);
474 a->pkAuthenticator.ctime = sec;
475 a->pkAuthenticator.cusec = usec;
476 a->pkAuthenticator.nonce = nonce;
481 static krb5_error_code
482 build_auth_pack_19(krb5_context context,
484 const KDC_REQ_BODY *body,
487 size_t buf_size, len;
488 krb5_cksumtype cksum;
494 krb5_clear_error_string(context);
496 /* XXX some PACKETCABLE needs implemetations need md5 */
497 cksum = CKSUMTYPE_RSA_MD5;
499 krb5_us_timeofday(context, &sec, &usec);
500 a->pkAuthenticator.ctime = sec;
501 a->pkAuthenticator.nonce = nonce;
503 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
507 krb5_abortx(context, "internal error in ASN.1 encoder");
509 ret = krb5_create_checksum(context,
515 &a->pkAuthenticator.paChecksum);
521 static krb5_error_code
522 build_auth_pack(krb5_context context,
525 const KDC_REQ_BODY *body,
528 size_t buf_size, len;
535 krb5_clear_error_string(context);
537 memset(&checksum, 0, sizeof(checksum));
539 krb5_us_timeofday(context, &sec, &usec);
540 a->pkAuthenticator.ctime = sec;
541 a->pkAuthenticator.nonce = nonce;
543 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
547 krb5_abortx(context, "internal error in ASN.1 encoder");
549 ret = krb5_create_checksum(context,
558 ret = krb5_data_copy(&a->pkAuthenticator.paChecksum,
559 checksum.checksum.data, checksum.checksum.length);
560 free_Checksum(&checksum);
563 if (ret == 0 && dh) {
565 heim_integer dh_pub_key;
569 ALLOC(a->clientPublicValue, 1);
570 if (a->clientPublicValue == NULL)
572 ret = copy_oid(oid_id_dhpublicnumber(),
573 &a->clientPublicValue->algorithm.algorithm);
577 memset(&dp, 0, sizeof(dp));
579 ret = BN_to_integer(context, dh->p, &dp.p);
581 free_DomainParameters(&dp);
584 ret = BN_to_integer(context, dh->g, &dp.g);
586 free_DomainParameters(&dp);
589 ret = BN_to_integer(context, dh->q, &dp.q);
591 free_DomainParameters(&dp);
595 dp.validationParms = NULL;
597 a->clientPublicValue->algorithm.parameters =
598 malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
599 if (a->clientPublicValue->algorithm.parameters == NULL) {
600 free_DomainParameters(&dp);
604 ASN1_MALLOC_ENCODE(DomainParameters,
605 a->clientPublicValue->algorithm.parameters->data,
606 a->clientPublicValue->algorithm.parameters->length,
608 free_DomainParameters(&dp);
611 if (size != a->clientPublicValue->algorithm.parameters->length)
612 krb5_abortx(context, "Internal ASN1 encoder error");
614 ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
618 buf.length = length_heim_integer(&dh_pub_key);
619 buf.data = malloc(buf.length);
620 if (buf.data == NULL) {
621 free_heim_integer(&dh_pub_key);
622 krb5_set_error_string(context, "malloc: out of memory");
625 ret = der_put_heim_integer((char *)buf.data + buf.length - 1,
626 buf.length, &dh_pub_key, &size);
627 free_heim_integer(&dh_pub_key);
632 if (size != buf.length)
633 krb5_abortx(context, "asn1 internal error");
635 a->clientPublicValue->subjectPublicKey.length = buf.length * 8;
636 a->clientPublicValue->subjectPublicKey.data = buf.data;
642 krb5_error_code KRB5_LIB_FUNCTION
643 _krb5_pk_mk_ContentInfo(krb5_context context,
644 const krb5_data *buf,
646 struct ContentInfo *content_info)
650 ret = copy_oid(oid, &content_info->contentType);
653 ALLOC(content_info->content, 1);
654 if (content_info->content == NULL)
656 content_info->content->data = malloc(buf->length);
657 if (content_info->content->data == NULL)
659 memcpy(content_info->content->data, buf->data, buf->length);
660 content_info->content->length = buf->length;
664 static krb5_error_code
665 pk_mk_padata(krb5_context context,
667 krb5_pk_init_ctx ctx,
668 const KDC_REQ_BODY *req_body,
672 struct ContentInfo content_info;
677 krb5_data buf, sd_buf;
680 krb5_data_zero(&buf);
681 krb5_data_zero(&sd_buf);
682 memset(&req, 0, sizeof(req));
683 memset(&content_info, 0, sizeof(content_info));
685 if (compat == COMPAT_WIN2K) {
688 memset(&ap, 0, sizeof(ap));
690 ret = build_auth_pack_win2k(context, nonce, req_body, &ap);
692 free_AuthPack_Win2k(&ap);
696 ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
698 free_AuthPack_Win2k(&ap);
700 krb5_set_error_string(context, "AuthPack_Win2k: %d", ret);
703 if (buf.length != size)
704 krb5_abortx(context, "internal ASN1 encoder error");
706 oid = oid_id_pkcs7_data();
707 } else if (compat == COMPAT_19) {
710 memset(&ap, 0, sizeof(ap));
712 ret = build_auth_pack_19(context, nonce, req_body, &ap);
714 free_AuthPack_19(&ap);
718 ASN1_MALLOC_ENCODE(AuthPack_19, buf.data, buf.length, &ap, &size, ret);
719 free_AuthPack_19(&ap);
721 krb5_set_error_string(context, "AuthPack_19: %d", ret);
724 if (buf.length != size)
725 krb5_abortx(context, "internal ASN1 encoder error");
727 oid = oid_id_pkauthdata();
728 } else if (compat == COMPAT_25) {
731 memset(&ap, 0, sizeof(ap));
733 ret = build_auth_pack(context, nonce, ctx->dh, req_body, &ap);
739 ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
742 krb5_set_error_string(context, "AuthPack: %d", ret);
745 if (buf.length != size)
746 krb5_abortx(context, "internal ASN1 encoder error");
748 oid = oid_id_pkauthdata();
750 krb5_abortx(context, "internal pkinit error");
752 ret = _krb5_pk_create_sign(context,
757 krb5_data_free(&buf);
761 ret = _krb5_pk_mk_ContentInfo(context, &sd_buf, oid_id_pkcs7_signedData(),
763 krb5_data_free(&sd_buf);
767 /* XXX tell the kdc what CAs the client is willing to accept */
768 req.trustedCertifiers = NULL;
771 if (compat == COMPAT_WIN2K) {
772 PA_PK_AS_REQ_Win2k winreq;
774 pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
776 memset(&winreq, 0, sizeof(winreq));
778 ASN1_MALLOC_ENCODE(ContentInfo,
779 winreq.signed_auth_pack.data,
780 winreq.signed_auth_pack.length,
786 if (winreq.signed_auth_pack.length != size)
787 krb5_abortx(context, "Internal ASN1 encoder error");
789 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
790 &winreq, &size, ret);
791 free_PA_PK_AS_REQ_Win2k(&winreq);
793 } else if (compat == COMPAT_19) {
794 PA_PK_AS_REQ_19 req_19;
796 pa_type = KRB5_PADATA_PK_AS_REQ_19;
798 memset(&req_19, 0, sizeof(req_19));
800 ret = copy_ContentInfo(&content_info, &req_19.signedAuthPack);
802 krb5_clear_error_string(context);
805 req_19.kdcCert = NULL;
806 req_19.trustedCertifiers = NULL;
807 req_19.encryptionCert = NULL;
809 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_19, buf.data, buf.length,
810 &req_19, &size, ret);
812 free_PA_PK_AS_REQ_19(&req_19);
814 } else if (compat == COMPAT_25) {
816 pa_type = KRB5_PADATA_PK_AS_REQ;
818 ASN1_MALLOC_ENCODE(ContentInfo,
819 req.signedAuthPack.data,
820 req.signedAuthPack.length,
826 if (req.signedAuthPack.length != size)
827 krb5_abortx(context, "Internal ASN1 encoder error");
829 ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
833 krb5_abortx(context, "internal pkinit error");
835 krb5_set_error_string(context, "PA-PK-AS-REQ %d", ret);
838 if (buf.length != size)
839 krb5_abortx(context, "Internal ASN1 encoder error");
841 ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
845 free_ContentInfo(&content_info);
851 krb5_error_code KRB5_LIB_FUNCTION
852 _krb5_pk_mk_padata(krb5_context context,
854 const KDC_REQ_BODY *req_body,
858 krb5_pk_init_ctx ctx = c;
862 const char *provisioning_server;
865 win2k_compat = krb5_config_get_bool_default(context, NULL,
871 if (context->pkinit_flags & KRB5_PKINIT_WIN2K)
875 ret = pk_mk_padata(context, COMPAT_WIN2K, ctx, req_body, nonce, md);
879 ret = pk_mk_padata(context, COMPAT_19, ctx, req_body, nonce, md);
883 ret = pk_mk_padata(context, COMPAT_25, ctx, req_body, nonce, md);
888 provisioning_server =
889 krb5_config_get_string(context, NULL,
892 "packet-cable-provisioning-server",
895 if (provisioning_server) {
896 /* PacketCable requires the PROV-SRV-LOCATION authenticator */
897 const PROV_SRV_LOCATION prov_server = (char *)provisioning_server;
899 ASN1_MALLOC_ENCODE(PROV_SRV_LOCATION, buf.data, buf.length,
900 &prov_server, &size, ret);
903 if (buf.length != size)
904 krb5_abortx(context, "Internal ASN1 encoder error");
906 /* PacketCable uses -1 (application specific) as the auth data type */
907 ret = krb5_padata_add(context, md, -1, buf.data, buf.length);
916 pk_peer_compare(krb5_context context,
917 const SignerIdentifier *peer1,
920 switch (peer1->element) {
921 case choice_CMSIdentifier_issuerAndSerialNumber: {
923 const heim_integer *serial;
928 i = X509_get_serialNumber(peer2);
929 serial = &peer1->u.issuerAndSerialNumber.serialNumber;
931 if (i->length != serial->length ||
932 memcmp(i->data, serial->data, i->length) != 0)
935 p = peer1->u.issuerAndSerialNumber.issuer._save.data;
936 len = peer1->u.issuerAndSerialNumber.issuer._save.length;
937 name = d2i_X509_NAME(NULL, &p, len);
941 if (X509_NAME_cmp(name, X509_get_issuer_name(peer2)) != 0) {
942 X509_NAME_free(name);
945 X509_NAME_free(name);
948 case choice_CMSIdentifier_subjectKeyIdentifier:
956 static krb5_error_code
957 pk_decrypt_key(krb5_context context,
958 heim_octet_string *encrypted_key,
965 buf = malloc(EVP_PKEY_size(priv_key));
967 krb5_set_error_string(context, "malloc: out of memory");
970 ret = EVP_PKEY_decrypt(buf,
972 encrypted_key->length,
976 krb5_set_error_string(context, "Can't decrypt key: %s",
977 ERR_error_string(ERR_get_error(), NULL));
982 key->keyvalue.length = ret;
983 key->keyvalue.data = malloc(ret);
984 if (key->keyvalue.data == NULL) {
986 krb5_set_error_string(context, "malloc: out of memory");
989 memcpy(key->keyvalue.data, buf, ret);
995 static krb5_error_code
996 pk_verify_chain_standard(krb5_context context,
997 struct krb5_pk_identity *id,
998 const SignerIdentifier *client,
999 STACK_OF(X509) *chain,
1002 X509_STORE *cert_store = NULL;
1003 X509_STORE_CTX *store_ctx = NULL;
1008 ret = KRB5_KDC_ERROR_CLIENT_NAME_MISMATCH;
1009 for (i = 0; i < sk_X509_num(chain); i++) {
1010 cert = sk_X509_value(chain, i);
1011 if (pk_peer_compare(context, client, cert) == TRUE) {
1017 krb5_set_error_string(context, "PKINIT: verify chain failed "
1018 "to find client in chain");
1022 cert_store = X509_STORE_new();
1023 if (cert_store == NULL) {
1025 krb5_set_error_string(context, "PKINIT: can't create X509 store: %s",
1026 ERR_error_string(ERR_get_error(), NULL));
1029 store_ctx = X509_STORE_CTX_new();
1030 if (store_ctx == NULL) {
1032 krb5_set_error_string(context,
1033 "PKINIT: can't create X509 store ctx: %s",
1034 ERR_error_string(ERR_get_error(), NULL));
1038 X509_STORE_CTX_init(store_ctx, cert_store, cert, chain);
1039 X509_STORE_CTX_trusted_stack(store_ctx, id->trusted_certs);
1040 X509_verify_cert(store_ctx);
1041 /* the last checked certificate is in store_ctx->current_cert */
1042 krb5_clear_error_string(context);
1043 switch(store_ctx->error) {
1047 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
1048 ret = KRB5_KDC_ERROR_CANT_VERIFY_CERTIFICATE;
1049 krb5_set_error_string(context, "PKINIT: failed to verify "
1051 X509_verify_cert_error_string(store_ctx->error));
1053 case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
1054 case X509_V_ERR_CERT_SIGNATURE_FAILURE:
1055 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
1056 case X509_V_ERR_CERT_NOT_YET_VALID:
1057 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
1058 case X509_V_ERR_CERT_HAS_EXPIRED:
1059 ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE;
1060 krb5_set_error_string(context, "PKINIT: invalid certificate: %s ",
1061 X509_verify_cert_error_string(store_ctx->error));
1063 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
1064 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1065 case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
1066 case X509_V_ERR_CERT_CHAIN_TOO_LONG:
1067 case X509_V_ERR_PATH_LENGTH_EXCEEDED:
1068 case X509_V_ERR_INVALID_CA:
1069 ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE;
1070 krb5_set_error_string(context, "PKINIT: unknown CA or can't "
1071 "verify certificate: %s",
1072 X509_verify_cert_error_string(store_ctx->error));
1075 ret = KRB5_KDC_ERROR_INVALID_CERTIFICATE; /* XXX */
1076 krb5_set_error_string(context, "PKINIT: failed to verify "
1077 "certificate: %s (%ld) ",
1078 X509_verify_cert_error_string(store_ctx->error),
1079 (long)store_ctx->error);
1086 * Since X509_verify_cert() doesn't do CRL checking at all, we have to
1087 * perform own verification against CRLs
1090 ret = pk_verify_crl(context, store_ctx, id->crls);
1095 if (client_cert && cert)
1096 *client_cert = X509_dup(cert);
1100 X509_STORE_free(cert_store);
1102 X509_STORE_CTX_free(store_ctx);
1107 cert_to_X509(krb5_context context, CertificateSetReal *set,
1108 STACK_OF(X509_CRL) **certs)
1110 krb5_error_code ret;
1113 *certs = sk_X509_new_null();
1116 for (i = 0; i < set->len; i++) {
1120 p = set->val[i].data;
1121 cert = d2i_X509(NULL, &p, set->val[i].length);
1123 ret = ASN1_BAD_FORMAT;
1126 sk_X509_insert(*certs, cert, i);
1129 krb5_set_error_string(context,
1130 "PKINIT: Failed to decode certificate chain");
1131 sk_X509_free(*certs);
1137 static krb5_error_code
1138 any_to_CertificateSet(krb5_context context, heim_any *cert,
1139 CertificateSetReal *set)
1141 size_t size, len, length;
1151 length = cert->length;
1152 while (len < cert->length) {
1153 val = realloc(set->val, (set->len + 1) * sizeof(set->val[0]));
1159 ret = decode_heim_any(p, length, &set->val[set->len], &size);
1170 krb5_clear_error_string(context);
1171 free_CertificateSetReal(set);
1176 krb5_error_code KRB5_LIB_FUNCTION
1177 _krb5_pk_verify_sign(krb5_context context,
1180 struct krb5_pk_identity *id,
1181 heim_oid *contentType,
1183 struct krb5_pk_cert **signer)
1185 STACK_OF(X509) *certificates;
1186 SignerInfo *signer_info;
1187 const EVP_MD *evp_type;
1188 EVP_PKEY *public_key;
1189 krb5_error_code ret;
1190 CertificateSetReal set;
1197 krb5_data_zero(content);
1198 contentType->length = 0;
1199 contentType->components = NULL;
1201 memset(&sd, 0, sizeof(sd));
1203 ret = decode_SignedData(data, length, &sd, &size);
1205 krb5_set_error_string(context,
1206 "PKINIT: decoding failed SignedData: %d",
1211 if (sd.encapContentInfo.eContent == NULL) {
1212 krb5_set_error_string(context,
1213 "PKINIT: signature missing encapContent");
1214 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1218 /* XXX Check CMS version */
1220 if (sd.signerInfos.len < 1) {
1221 krb5_set_error_string(context,
1222 "PKINIT: signature information missing from "
1224 ret = KRB5_KDC_ERR_INVALID_SIG;
1228 signer_info = &sd.signerInfos.val[0];
1230 ret = any_to_CertificateSet(context, sd.certificates, &set);
1232 krb5_set_error_string(context,
1233 "PKINIT: failed to decode CertificateSet");
1237 ret = cert_to_X509(context, &set, &certificates);
1238 free_CertificateSetReal(&set);
1240 krb5_set_error_string(context,
1241 "PKINIT: failed to decode Certificates");
1245 ret = pk_verify_chain_standard(context, id,
1249 sk_X509_free(certificates);
1253 if (signer_info->signature.length == 0) {
1254 free_SignedData(&sd);
1256 krb5_set_error_string(context, "PKINIT: signature missing from"
1258 return KRB5_KDC_ERR_INVALID_SIG;
1261 public_key = X509_get_pubkey(cert);
1263 /* verify signature */
1264 if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm,
1265 oid_id_pkcs1_sha1WithRSAEncryption()) == 0)
1266 evp_type = EVP_sha1();
1267 else if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm,
1268 oid_id_pkcs1_md5WithRSAEncryption()) == 0)
1269 evp_type = EVP_md5();
1270 else if (heim_oid_cmp(&signer_info->digestAlgorithm.algorithm,
1271 oid_id_secsig_sha_1()) == 0)
1272 evp_type = EVP_sha1();
1275 krb5_set_error_string(context, "PKINIT: The requested digest "
1276 "algorithm is not supported");
1277 ret = KRB5_KDC_ERR_INVALID_SIG;
1281 EVP_VerifyInit(&md, evp_type);
1282 EVP_VerifyUpdate(&md,
1283 sd.encapContentInfo.eContent->data,
1284 sd.encapContentInfo.eContent->length);
1285 ret = EVP_VerifyFinal(&md,
1286 signer_info->signature.data,
1287 signer_info->signature.length,
1291 krb5_set_error_string(context, "PKINIT: signature didn't verify: %s",
1292 ERR_error_string(ERR_get_error(), NULL));
1293 ret = KRB5_KDC_ERR_INVALID_SIG;
1297 ret = copy_oid(&sd.encapContentInfo.eContentType, contentType);
1299 krb5_clear_error_string(context);
1303 content->data = malloc(sd.encapContentInfo.eContent->length);
1304 if (content->data == NULL) {
1305 krb5_clear_error_string(context);
1309 content->length = sd.encapContentInfo.eContent->length;
1310 memcpy(content->data,sd.encapContentInfo.eContent->data,content->length);
1312 *signer = malloc(sizeof(**signer));
1313 if (*signer == NULL) {
1314 krb5_clear_error_string(context);
1318 (*signer)->cert = cert;
1321 free_SignedData(&sd);
1323 free_oid(contentType);
1324 krb5_data_free(content);
1329 static krb5_error_code
1330 get_reply_key(krb5_context context,
1331 const krb5_data *content,
1333 krb5_keyblock **key)
1335 ReplyKeyPack_19 key_pack;
1336 krb5_error_code ret;
1339 ret = decode_ReplyKeyPack_19(content->data,
1344 krb5_set_error_string(context, "PKINIT decoding reply key failed");
1345 free_ReplyKeyPack_19(&key_pack);
1349 if (key_pack.nonce != nonce) {
1350 krb5_set_error_string(context, "PKINIT enckey nonce is wrong");
1351 free_ReplyKeyPack_19(&key_pack);
1352 return KRB5KRB_AP_ERR_MODIFIED;
1355 *key = malloc (sizeof (**key));
1357 krb5_set_error_string(context, "PKINIT failed allocating reply key");
1358 free_ReplyKeyPack_19(&key_pack);
1359 krb5_set_error_string(context, "malloc: out of memory");
1363 ret = copy_EncryptionKey(&key_pack.replyKey, *key);
1364 free_ReplyKeyPack_19(&key_pack);
1366 krb5_set_error_string(context, "PKINIT failed copying reply key");
1373 static krb5_error_code
1374 pk_verify_host(krb5_context context, struct krb5_pk_cert *host)
1380 static krb5_error_code
1381 pk_rd_pa_reply_enckey(krb5_context context,
1384 krb5_pk_init_ctx ctx,
1388 krb5_keyblock **key)
1390 krb5_error_code ret;
1392 krb5_keyblock tmp_key;
1395 KeyTransRecipientInfo *ri;
1402 heim_oid contentType = { 0, NULL };
1403 struct krb5_pk_cert *host = NULL;
1404 heim_octet_string encryptedContent;
1405 heim_octet_string *any;
1410 memset(&tmp_key, 0, sizeof(tmp_key));
1411 memset(&ed, 0, sizeof(ed));
1412 krb5_data_zero(&plain);
1413 krb5_data_zero(&content);
1414 krb5_data_zero(&encryptedContent);
1415 krb5_data_zero(&ivec);
1417 user_cert = sk_X509_value(ctx->id->cert, 0);
1419 if (heim_oid_cmp(oid_id_pkcs7_envelopedData(), &rep->contentType)) {
1420 krb5_set_error_string(context, "PKINIT: Invalid content type");
1424 if (rep->content == NULL) {
1425 krb5_set_error_string(context, "PKINIT: No content in reply");
1429 ret = decode_EnvelopedData(rep->content->data,
1430 rep->content->length,
1434 free_EnvelopedData(&ed);
1438 if (ed.recipientInfos.len != 1) {
1439 free_EnvelopedData(&ed);
1440 krb5_set_error_string(context, "pkinit: Number of recipient infos "
1442 ed.recipientInfos.len);
1443 return EINVAL; /* XXX */
1446 ri = &ed.recipientInfos.val[0];
1448 /* XXX make SignerIdentifier and RecipientIdentifier the same */
1449 bret = pk_peer_compare(context, (SignerIdentifier *)&ri->rid, user_cert);
1450 if (bret == FALSE) {
1451 ret = KRB5KRB_AP_ERR_BADMATCH; /* XXX */
1455 if (heim_oid_cmp(oid_id_pkcs1_rsaEncryption(),
1456 &ri->keyEncryptionAlgorithm.algorithm)) {
1457 krb5_set_error_string(context, "PKINIT: invalid content type");
1461 ret = pk_decrypt_key(context, &ri->encryptedKey,
1462 ctx->id->private_key, &tmp_key);
1467 /* verify content type */
1469 if (heim_oid_cmp(&ed.encryptedContentInfo.contentType, oid_id_pkcs7_data())) {
1470 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1474 if (heim_oid_cmp(&ed.encryptedContentInfo.contentType, oid_id_pkcs7_signedData())) {
1475 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1480 if (ed.encryptedContentInfo.encryptedContent == NULL) {
1481 krb5_set_error_string(context, "PKINIT: OPTIONAL encryptedContent "
1482 "field not filled in in KDC reply");
1483 ret = KRB5_BADMSGTYPE;
1487 any = ed.encryptedContentInfo.encryptedContent;
1488 ret = der_get_octet_string(any->data, any->length,
1489 &encryptedContent, NULL);
1491 krb5_set_error_string(context,
1492 "PKINIT: encryptedContent content invalid");
1496 if (ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters == NULL){
1497 krb5_set_error_string(context,
1498 "PKINIT: encryptedContent parameter missing");
1499 ret = KRB5_BADMSGTYPE;
1503 params.data = ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters->data;
1504 params.length = ed.encryptedContentInfo.contentEncryptionAlgorithm.parameters->length;
1506 ret = _krb5_oid_to_enctype(context,
1507 &ed.encryptedContentInfo.contentEncryptionAlgorithm.algorithm,
1512 ret = krb5_crypto_init(context, &tmp_key, 0, &crypto);
1516 ret = krb5_crypto_get_params(context, crypto, ¶ms, &ivec);
1520 ret = krb5_decrypt_ivec(context, crypto,
1522 encryptedContent.data,
1523 encryptedContent.length,
1528 length = plain.length;
1530 /* win2k uses ContentInfo */
1535 ret = decode_ContentInfo(p, length, &ci, &size);
1537 krb5_set_error_string(context,
1538 "PKINIT: failed decoding ContentInfo: %d",
1543 if (heim_oid_cmp(&ci.contentType, oid_id_pkcs7_signedData())) {
1544 ret = EINVAL; /* XXX */
1545 krb5_set_error_string(context, "PKINIT: Invalid content type");
1548 p = ci.content->data;
1549 length = ci.content->length;
1552 ret = _krb5_pk_verify_sign(context,
1562 /* make sure that it is the kdc's certificate */
1563 ret = pk_verify_host(context, host);
1565 krb5_set_error_string(context, "PKINIT: failed verify host: %d", ret);
1570 if (heim_oid_cmp(&contentType, oid_id_pkcs7_data()) != 0) {
1571 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1572 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1576 if (heim_oid_cmp(&contentType, oid_id_pkrkeydata()) != 0) {
1577 krb5_set_error_string(context, "PKINIT: reply key, wrong oid");
1578 ret = KRB5KRB_AP_ERR_MSG_TYPE;
1583 ret = get_reply_key(context, &content, nonce, key);
1587 /* XXX compare given etype with key->etype */
1591 _krb5_pk_cert_free(host);
1592 free_oid(&contentType);
1593 free_octet_string(&encryptedContent);
1594 krb5_data_free(&content);
1595 krb5_free_keyblock_contents(context, &tmp_key);
1596 krb5_data_free(&plain);
1597 krb5_data_free(&ivec);
1602 static krb5_error_code
1603 pk_rd_pa_reply_dh(krb5_context context,
1605 krb5_pk_init_ctx ctx,
1609 krb5_keyblock **key)
1611 unsigned char *p, *dh_gen_key = NULL;
1612 ASN1_INTEGER *dh_pub_key = NULL;
1613 struct krb5_pk_cert *host = NULL;
1614 BIGNUM *kdc_dh_pubkey = NULL;
1615 KDCDHKeyInfo kdc_dh_info;
1616 heim_oid contentType = { 0, NULL };
1618 krb5_error_code ret;
1622 krb5_data_zero(&content);
1623 memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
1625 if (heim_oid_cmp(oid_id_pkcs7_signedData(), &rep->contentType)) {
1626 krb5_set_error_string(context, "PKINIT: Invalid content type");
1630 if (rep->content == NULL) {
1631 krb5_set_error_string(context, "PKINIT: No content in reply");
1635 ret = _krb5_pk_verify_sign(context,
1637 rep->content->length,
1645 /* make sure that it is the kdc's certificate */
1646 ret = pk_verify_host(context, host);
1650 if (heim_oid_cmp(&contentType, oid_id_pkdhkeydata())) {
1651 ret = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
1655 ret = decode_KDCDHKeyInfo(content.data,
1663 if (kdc_dh_info.nonce != nonce) {
1664 krb5_set_error_string(context, "PKINIT: DH nonce is wrong");
1665 ret = KRB5KRB_AP_ERR_MODIFIED;
1669 p = kdc_dh_info.subjectPublicKey.data;
1670 size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
1671 dh_pub_key = d2i_ASN1_INTEGER(NULL, &p, size);
1672 if (dh_pub_key == NULL) {
1673 krb5_set_error_string(context,
1674 "PKINIT: Can't parse KDC's DH public key");
1675 ret = KRB5KRB_ERR_GENERIC;
1679 kdc_dh_pubkey = ASN1_INTEGER_to_BN(dh_pub_key, NULL);
1680 if (kdc_dh_pubkey == NULL) {
1681 krb5_set_error_string(context,
1682 "PKINIT: Can't convert KDC's DH public key");
1683 ret = KRB5KRB_ERR_GENERIC;
1687 dh_gen_key = malloc(DH_size(ctx->dh));
1688 if (dh_gen_key == NULL) {
1689 krb5_set_error_string(context, "malloc: out of memory");
1694 dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->dh);
1695 if (dh_gen_keylen == -1) {
1696 krb5_set_error_string(context,
1697 "PKINIT: Can't compute Diffie-Hellman key (%s)",
1698 ERR_error_string(ERR_get_error(), NULL));
1699 ret = KRB5KRB_ERR_GENERIC;
1703 *key = malloc (sizeof (**key));
1705 krb5_set_error_string(context, "malloc: out of memory");
1710 ret = krb5_random_to_key(context, etype, dh_gen_key, dh_gen_keylen, *key);
1712 krb5_set_error_string(context,
1713 "PKINIT: can't create key from DH key");
1721 BN_free(kdc_dh_pubkey);
1723 memset(dh_gen_key, 0, DH_size(ctx->dh));
1727 ASN1_INTEGER_free(dh_pub_key);
1729 _krb5_pk_cert_free(host);
1731 krb5_data_free(&content);
1732 free_KDCDHKeyInfo(&kdc_dh_info);
1737 krb5_error_code KRB5_LIB_FUNCTION
1738 _krb5_pk_rd_pa_reply(krb5_context context,
1743 krb5_keyblock **key)
1745 krb5_pk_init_ctx ctx = c;
1746 krb5_error_code ret;
1750 /* Check for PK-INIT -25 */
1751 if (pa->padata_type == KRB5_PADATA_PK_AS_REP) {
1754 memset(&rep, 0, sizeof(rep));
1756 ret = decode_PA_PK_AS_REP(pa->padata_value.data,
1757 pa->padata_value.length,
1763 switch (rep.element) {
1764 case choice_PA_PK_AS_REP_encKeyPack:
1765 ret = decode_ContentInfo(rep.u.encKeyPack.data,
1766 rep.u.encKeyPack.length,
1769 free_PA_PK_AS_REP(&rep);
1771 krb5_set_error_string(context,
1772 "PKINIT: -25 decoding failed "
1773 "ContentInfo: %d", ret);
1776 ret = pk_rd_pa_reply_enckey(context, 0, &ci, ctx,
1777 etype, nonce, pa, key);
1778 free_ContentInfo(&ci);
1781 free_PA_PK_AS_REP(&rep);
1782 krb5_set_error_string(context, "PKINIT: -25 reply "
1783 "invalid content type");
1788 /* Check for PK-INIT -19 */
1790 PA_PK_AS_REP_19 rep19;
1792 memset(&rep19, 0, sizeof(rep19));
1794 ret = decode_PA_PK_AS_REP_19(pa->padata_value.data,
1795 pa->padata_value.length,
1799 switch(rep19.element) {
1800 case choice_PA_PK_AS_REP_19_dhSignedData:
1801 ret = pk_rd_pa_reply_dh(context, &rep19.u.dhSignedData, ctx,
1802 etype, nonce, pa, key);
1804 case choice_PA_PK_AS_REP_19_encKeyPack:
1805 ret = pk_rd_pa_reply_enckey(context, 0,
1806 &rep19.u.encKeyPack, ctx,
1807 etype, nonce, pa, key);
1810 krb5_set_error_string(context, "PKINIT: -19 reply invalid "
1815 free_PA_PK_AS_REP_19(&rep19);
1821 /* Check for Windows encoding of the AS-REP pa data */
1823 PA_PK_AS_REP_Win2k w2krep;
1825 memset(&w2krep, 0, sizeof(w2krep));
1827 ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
1828 pa->padata_value.length,
1832 krb5_set_error_string(context, "PKINIT: Failed decoding windows"
1833 "pkinit reply %d", ret);
1837 switch (w2krep.element) {
1838 case choice_PA_PK_AS_REP_Win2k_encKeyPack:
1839 ret = decode_ContentInfo(w2krep.u.encKeyPack.data,
1840 w2krep.u.encKeyPack.length,
1843 free_PA_PK_AS_REP_Win2k(&w2krep);
1845 krb5_set_error_string(context,
1846 "PKINIT: decoding failed "
1851 ret = pk_rd_pa_reply_enckey(context, 1, &ci, ctx,
1852 etype, nonce, pa, key);
1853 free_ContentInfo(&ci);
1856 free_PA_PK_AS_REP_Win2k(&w2krep);
1857 krb5_set_error_string(context, "PKINIT: win2k reply invalid "
1869 ssl_pass_cb(char *buf, int size, int rwflag, void *u)
1871 krb5_error_code ret;
1873 krb5_data password_data;
1874 krb5_prompter_fct prompter = u;
1876 password_data.data = buf;
1877 password_data.length = size;
1878 prompt.prompt = "Enter your private key passphrase: ";
1880 prompt.reply = &password_data;
1881 prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
1883 ret = (*prompter)(NULL, NULL, NULL, NULL, 1, &prompt);
1885 memset (buf, 0, size);
1891 static krb5_error_code
1892 load_openssl_cert(krb5_context context,
1896 STACK_OF(X509) *certificate;
1897 krb5_error_code ret;
1900 f = fopen(file, "r");
1903 krb5_set_error_string(context, "PKINIT: open failed %s: %s",
1904 file, strerror(ret));
1908 certificate = sk_X509_new_null();
1910 /* see http://www.openssl.org/docs/crypto/pem.html section BUGS */
1912 cert = PEM_read_X509(f, NULL, NULL, NULL);
1914 if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE) {
1915 /* End of file reached. no error */
1919 krb5_set_error_string(context, "PKINIT: Can't read certificate");
1921 return HEIM_PKINIT_CERTIFICATE_INVALID;
1923 sk_X509_insert(certificate, cert, sk_X509_num(certificate));
1926 if (sk_X509_num(certificate) == 0) {
1927 krb5_set_error_string(context, "PKINIT: No certificate found");
1928 return HEIM_PKINIT_NO_CERTIFICATE;
1934 static krb5_error_code
1935 load_openssl_file(krb5_context context,
1937 krb5_prompter_fct prompter,
1938 void *prompter_data,
1939 const char *user_id,
1940 struct krb5_pk_identity *id)
1942 krb5_error_code ret;
1943 STACK_OF(X509) *certificate = NULL;
1944 char *cert_file = NULL, *key_file;
1945 EVP_PKEY *private_key = NULL;
1948 cert_file = strdup(user_id);
1949 if (cert_file == NULL) {
1950 krb5_set_error_string(context, "malloc: out of memory");
1953 key_file = strchr(cert_file, ',');
1954 if (key_file == NULL) {
1955 krb5_set_error_string(context, "PKINIT: key file missing");
1956 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
1961 ret = load_openssl_cert(context, cert_file, &certificate);
1965 /* load private key */
1966 f = fopen(key_file, "r");
1969 krb5_set_error_string(context, "PKINIT: open %s: %s",
1970 key_file, strerror(ret));
1973 if (password == NULL || password[0] == '\0') {
1974 if (prompter == NULL)
1975 prompter = krb5_prompter_posix;
1976 private_key = PEM_read_PrivateKey(f, NULL, ssl_pass_cb, prompter);
1978 private_key = PEM_read_PrivateKey(f, NULL, NULL, password);
1980 if (private_key == NULL) {
1981 krb5_set_error_string(context, "PKINIT: Can't read private key");
1982 ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
1985 ret = X509_check_private_key(sk_X509_value(certificate, 0), private_key);
1987 ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
1988 krb5_set_error_string(context,
1989 "PKINIT: The private key doesn't match "
1990 "the public key certificate");
1994 id->private_key = private_key;
1995 id->cert = certificate;
2002 sk_X509_pop_free(certificate, X509_free);
2004 EVP_PKEY_free(private_key);
2010 add_pair(krb5_context context, char *str, char ***cmds, int *num)
2016 p = strchr(str, ':');
2022 /* filter out dup keys */
2023 for (i = 0; i < *num; i++)
2024 if (strcmp((*cmds)[i * 2], str) == 0)
2027 c = realloc(*cmds, sizeof(*c) * ((*num + 1) * 2));
2029 krb5_set_error_string(context, "malloc: out of memory");
2033 c[(*num * 2)] = str;
2034 c[(*num * 2) + 1] = p;
2040 static krb5_error_code
2041 eval_pairs(krb5_context context, ENGINE *e, const char *name,
2042 const char *type, char **cmds, int num)
2046 for (i = 0; i < num; i++) {
2047 char *a1 = cmds[i * 2], *a2 = cmds[(i * 2) + 1];
2048 if(!ENGINE_ctrl_cmd_string(e, a1, a2, 0)) {
2049 krb5_set_error_string(context,
2050 "PKINIT: Failed %scommand (%s - %s:%s): %s",
2051 type, name, a1, a2 ? a2 : "(NULL)",
2052 ERR_error_string(ERR_get_error(), NULL));
2053 return HEIM_PKINIT_NO_PRIVATE_KEY;
2059 struct engine_context {
2069 static krb5_error_code
2070 parse_openssl_engine_conf(krb5_context context,
2071 struct engine_context *ctx,
2074 krb5_error_code ret;
2077 for (p = strtok_r(line, ",", &last);
2079 p = strtok_r(NULL, ",", &last)) {
2083 krb5_set_error_string(context,
2084 "PKINIT: openssl engine configuration "
2085 "key %s missing = and thus value", p);
2086 return HEIM_PKINIT_NO_PRIVATE_KEY;
2090 if (strcasecmp("PRE", p) == 0) {
2091 ret = add_pair(context, q, &ctx->pre_cmds, &ctx->num_pre);
2094 } else if (strcasecmp("POST", p) == 0) {
2095 ret = add_pair(context, q, &ctx->post_cmds, &ctx->num_post);
2098 } else if (strcasecmp("KEY", p) == 0) {
2100 } else if (strcasecmp("CERT", p) == 0) {
2102 } else if (strcasecmp("ENGINE", p) == 0) {
2103 ctx->engine_name = q;
2105 krb5_set_error_string(context,
2106 "PKINIT: openssl engine configuration "
2107 "key %s is unknown", p);
2108 return HEIM_PKINIT_NO_PRIVATE_KEY;
2115 static krb5_error_code
2116 load_openssl_engine(krb5_context context,
2118 krb5_prompter_fct prompter,
2119 void *prompter_data,
2121 struct krb5_pk_identity *id)
2123 struct engine_context ctx;
2124 krb5_error_code ret;
2126 char *file_conf = NULL, *user_conf = NULL;
2129 memset(&ctx, 0, sizeof(ctx));
2131 ENGINE_load_builtin_engines();
2133 user_conf = strdup(string);
2134 if (user_conf == NULL) {
2135 krb5_set_error_string(context, "malloc: out of memory");
2139 ret = parse_openssl_engine_conf(context, &ctx, user_conf);
2143 f = krb5_config_get_string_default(context, NULL, NULL,
2145 "pkinit-openssl-engine",
2148 file_conf = strdup(f);
2150 ret = parse_openssl_engine_conf(context, &ctx, file_conf);
2156 if (ctx.cert_file == NULL) {
2157 krb5_set_error_string(context,
2158 "PKINIT: openssl engine missing certificate");
2159 ret = HEIM_PKINIT_NO_CERTIFICATE;
2162 if (ctx.key_id == NULL) {
2163 krb5_set_error_string(context,
2164 "PKINIT: openssl engine missing key id");
2165 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2168 if (ctx.engine_name == NULL) {
2169 krb5_set_error_string(context,
2170 "PKINIT: openssl engine missing engine name");
2171 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2175 e = ENGINE_by_id(ctx.engine_name);
2177 krb5_set_error_string(context,
2178 "PKINIT: failed getting openssl engine %s: %s",
2180 ERR_error_string(ERR_get_error(), NULL));
2181 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2185 ret = eval_pairs(context, e, ctx.engine_name, "pre",
2186 ctx.pre_cmds, ctx.num_pre);
2190 if(!ENGINE_init(e)) {
2191 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2192 krb5_set_error_string(context,
2193 "PKINIT: openssl engine init %s failed: %s",
2195 ERR_error_string(ERR_get_error(), NULL));
2200 ret = eval_pairs(context, e, ctx.engine_name, "post",
2201 ctx.post_cmds, ctx.num_post);
2206 * If the engine supports a LOAD_CERT_CTRL function, lets try
2207 * it. OpenSC support this function. Eventially this should be
2208 * a ENGINE_load_cert function if it failes, treat it like a
2213 const char * cert_id;
2217 parms.cert_id = ctx.cert_file;
2219 ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1);
2221 id->cert = sk_X509_new_null();
2222 sk_X509_insert(id->cert, parms.cert, 0);
2226 if (id->cert == NULL) {
2227 ret = load_openssl_cert(context, ctx.cert_file, &id->cert);
2233 UI_METHOD * krb5_ui_method = NULL;
2234 struct krb5_ui_data ui_data;
2236 krb5_ui_method = UI_create_method("Krb5 ui method");
2237 if (krb5_ui_method == NULL) {
2238 krb5_set_error_string(context,
2239 "PKINIT: failed to setup prompter "
2241 ERR_error_string(ERR_get_error(), NULL));
2242 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2245 UI_method_set_reader(krb5_ui_method, krb5_ui_method_read_string);
2247 ui_data.context = context;
2248 ui_data.prompter = prompter;
2249 if (prompter == NULL)
2250 ui_data.prompter = krb5_prompter_posix;
2251 ui_data.prompter_data = prompter_data;
2253 id->private_key = ENGINE_load_private_key(e,
2257 UI_destroy_method(krb5_ui_method);
2260 if (id->private_key == NULL) {
2261 krb5_set_error_string(context,
2262 "PKINIT: failed to load private key: %s",
2263 ERR_error_string(ERR_get_error(), NULL));
2264 ret = HEIM_PKINIT_NO_PRIVATE_KEY;
2268 ret = X509_check_private_key(sk_X509_value(id->cert, 0), id->private_key);
2270 ret = HEIM_PKINIT_PRIVATE_KEY_INVALID;
2271 krb5_set_error_string(context,
2272 "PKINIT: The private key doesn't match "
2273 "the public key certificate");
2292 ENGINE_finish(e); /* make sure all shared libs are unloaded */
2299 krb5_error_code KRB5_LIB_FUNCTION
2300 _krb5_pk_load_openssl_id(krb5_context context,
2301 struct krb5_pk_identity **ret_id,
2302 const char *user_id,
2303 const char *x509_anchors,
2304 krb5_prompter_fct prompter,
2305 void *prompter_data,
2308 STACK_OF(X509) *trusted_certs = NULL;
2309 struct krb5_pk_identity *id = NULL;
2310 krb5_error_code ret;
2311 struct dirent *file;
2312 char *dirname = NULL;
2315 krb5_error_code (*load_pair)(krb5_context,
2317 krb5_prompter_fct prompter,
2318 void * prompter_data,
2320 struct krb5_pk_identity *) = NULL;
2325 if (x509_anchors == NULL) {
2326 krb5_set_error_string(context, "PKINIT: No root ca directory given");
2327 return HEIM_PKINIT_NO_VALID_CA;
2330 if (user_id == NULL) {
2331 krb5_set_error_string(context,
2332 "PKINIT: No user X509 source given given");
2333 return HEIM_PKINIT_NO_PRIVATE_KEY;
2340 if (strncasecmp(user_id, "FILE:", 5) == 0) {
2341 load_pair = load_openssl_file;
2343 } else if (strncasecmp(user_id, "ENGINE:", 7) == 0) {
2344 load_pair = load_openssl_engine;
2347 krb5_set_error_string(context, "PKINIT: user identity not FILE");
2348 return HEIM_PKINIT_NO_CERTIFICATE;
2350 if (strncasecmp(x509_anchors, "OPENSSL-ANCHOR-DIR:", 19) != 0) {
2351 krb5_set_error_string(context, "PKINIT: anchor OPENSSL-ANCHOR-DIR");
2352 return HEIM_PKINIT_NO_VALID_CA;
2356 id = malloc(sizeof(*id));
2358 krb5_set_error_string(context, "malloc: out of memory");
2362 memset(id, 0, sizeof(*id));
2364 OpenSSL_add_all_algorithms();
2365 ERR_load_crypto_strings();
2368 ret = (*load_pair)(context, password, prompter, prompter_data, user_id, id);
2374 dirname = strdup(x509_anchors);
2375 if (dirname == NULL) {
2376 krb5_set_error_string(context, "malloc: out of memory");
2383 len = strlen(dirname);
2384 if (dirname[len - 1] == '/')
2385 dirname[len - 1] = '\0';
2388 /* read ca certificates */
2389 dir = opendir(dirname);
2392 krb5_set_error_string(context, "PKINIT: open directory %s: %s",
2393 dirname, strerror(ret));
2397 trusted_certs = sk_X509_new_null();
2398 while ((file = readdir(dir)) != NULL) {
2403 * Assume the certificate filenames constist of hashed subject
2404 * name followed by suffix ".0"
2407 if (strlen(file->d_name) == 10 && strcmp(&file->d_name[8],".0") == 0) {
2408 asprintf(&filename, "%s/%s", dirname, file->d_name);
2409 if (filename == NULL) {
2411 krb5_set_error_string(context, "malloc: out or memory");
2414 f = fopen(filename, "r");
2417 krb5_set_error_string(context, "PKINIT: open %s: %s",
2418 filename, strerror(ret));
2423 cert = PEM_read_X509(f, NULL, NULL, NULL);
2426 /* order of the certs is not important */
2427 sk_X509_push(trusted_certs, cert);
2434 if (sk_X509_num(trusted_certs) == 0) {
2435 krb5_set_error_string(context,
2436 "PKINIT: No CA certificate(s) found in %s",
2438 ret = HEIM_PKINIT_NO_VALID_CA;
2442 id->trusted_certs = trusted_certs;
2452 sk_X509_pop_free(trusted_certs, X509_free);
2455 sk_X509_pop_free(id->cert, X509_free);
2456 if (id->private_key)
2457 EVP_PKEY_free(id->private_key);
2466 void KRB5_LIB_FUNCTION
2467 _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
2470 krb5_pk_init_ctx ctx;
2472 if (opt->private == NULL || opt->private->pk_init_ctx == NULL)
2474 ctx = opt->private->pk_init_ctx;
2480 sk_X509_pop_free(ctx->id->cert, X509_free);
2481 if (ctx->id->trusted_certs)
2482 sk_X509_pop_free(ctx->id->trusted_certs, X509_free);
2483 if (ctx->id->private_key)
2484 EVP_PKEY_free(ctx->id->private_key);
2485 if (ctx->id->engine) {
2486 ENGINE_finish(ctx->id->engine); /* unload shared libs etc */
2487 ENGINE_free(ctx->id->engine);
2488 ctx->id->engine = NULL;
2493 opt->private->pk_init_ctx = NULL;
2497 krb5_error_code KRB5_LIB_FUNCTION
2498 krb5_get_init_creds_opt_set_pkinit(krb5_context context,
2499 krb5_get_init_creds_opt *opt,
2500 krb5_principal principal,
2501 const char *user_id,
2502 const char *x509_anchors,
2504 krb5_prompter_fct prompter,
2505 void *prompter_data,
2509 krb5_error_code ret;
2511 if (opt->private == NULL) {
2512 krb5_set_error_string(context, "PKINIT: on non extendable opt");
2516 opt->private->pk_init_ctx = malloc(sizeof(*opt->private->pk_init_ctx));
2517 if (opt->private->pk_init_ctx == NULL) {
2518 krb5_set_error_string(context, "malloc: out of memory");
2521 opt->private->pk_init_ctx->dh = NULL;
2522 opt->private->pk_init_ctx->id = NULL;
2523 ret = _krb5_pk_load_openssl_id(context,
2524 &opt->private->pk_init_ctx->id,
2531 free(opt->private->pk_init_ctx);
2532 opt->private->pk_init_ctx = NULL;
2536 if (ret == 0 && (flags & 1) && !(flags & 2)) {
2539 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
2540 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
2541 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
2542 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
2543 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
2544 "FFFFFFFF" "FFFFFFFF";
2545 const char *G = "2";
2547 "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
2548 "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
2549 "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
2550 "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
2551 "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
2552 "FFFFFFFF" "FFFFFFFF";
2556 _krb5_get_init_creds_opt_free_pkinit(opt);
2559 opt->private->pk_init_ctx->dh = dh;
2560 if (!BN_hex2bn(&dh->p, P)) {
2561 _krb5_get_init_creds_opt_free_pkinit(opt);
2564 if (!BN_hex2bn(&dh->g, G)) {
2565 _krb5_get_init_creds_opt_free_pkinit(opt);
2568 if (!BN_hex2bn(&dh->q, Q)) {
2569 _krb5_get_init_creds_opt_free_pkinit(opt);
2572 /* XXX generate a new key for each request ? */
2573 if (DH_generate_key(dh) != 1) {
2574 _krb5_get_init_creds_opt_free_pkinit(opt);
2580 krb5_set_error_string(context, "no support for PKINIT compiled in");