Path: blob/main/crypto/krb5/src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
34923 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* COPYRIGHT (C) 2006,20073* THE REGENTS OF THE UNIVERSITY OF MICHIGAN4* ALL RIGHTS RESERVED5*6* Permission is granted to use, copy, create derivative works7* and redistribute this software and such derivative works8* for any purpose, so long as the name of The University of9* Michigan is not used in any advertising or publicity10* pertaining to the use of distribution of this software11* without specific, written prior authorization. If the12* above copyright notice or any other identification of the13* University of Michigan is included in any copy of any14* portion of this software, then the disclaimer below must15* also be included.16*17* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION18* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY19* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF20* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING21* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF22* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE23* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE24* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR25* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING26* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN27* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF28* SUCH DAMAGES.29*/3031#include "k5-int.h"32#include "k5-buf.h"33#include "k5-err.h"34#include "k5-hex.h"35#include "pkinit.h"3637#include <openssl/bn.h>38#include <openssl/dh.h>39#include <openssl/x509.h>40#include <openssl/pkcs7.h>41#include <openssl/pkcs12.h>42#include <openssl/obj_mac.h>43#include <openssl/x509v3.h>44#include <openssl/err.h>45#include <openssl/evp.h>46#include <openssl/sha.h>47#include <openssl/asn1.h>48#include <openssl/pem.h>49#include <openssl/asn1t.h>50#include <openssl/cms.h>51#if OPENSSL_VERSION_NUMBER >= 0x30000000L52#include <openssl/core_names.h>53#include <openssl/kdf.h>54#include <openssl/decoder.h>55#include <openssl/params.h>56#endif5758#define DN_BUF_LEN 25659#define MAX_CREDS_ALLOWED 206061struct _pkinit_cred_info {62char *name;63X509 *cert;64EVP_PKEY *key;65#ifndef WITHOUT_PKCS1166CK_BYTE_PTR cert_id;67int cert_id_len;68#endif69};70typedef struct _pkinit_cred_info *pkinit_cred_info;7172struct _pkinit_identity_crypto_context {73pkinit_cred_info creds[MAX_CREDS_ALLOWED+1];74X509 *my_cert; /* selected user or KDC cert */75char *identity; /* identity name for user cert */76EVP_PKEY *my_key; /* selected cert key if in filesystem */77STACK_OF(X509) *trustedCAs; /* available trusted ca certs */78STACK_OF(X509) *intermediateCAs; /* available intermediate ca certs */79STACK_OF(X509_CRL) *revoked; /* available crls */80int pkcs11_method;81krb5_prompter_fct prompter;82void *prompter_data;83#ifndef WITHOUT_PKCS1184char *p11_module_name;85CK_SLOT_ID slotid;86char *token_label;87char *cert_label;88/* These are crypto-specific. */89struct plugin_file_handle *p11_module;90CK_SESSION_HANDLE session;91CK_FUNCTION_LIST_PTR p11;92uint8_t *cert_id;93size_t cert_id_len;94#endif95krb5_boolean defer_id_prompt;96pkinit_deferred_id *deferred_ids;97};9899struct _pkinit_plg_crypto_context {100EVP_PKEY *dh_1024;101EVP_PKEY *dh_2048;102EVP_PKEY *dh_4096;103EVP_PKEY *ec_p256;104EVP_PKEY *ec_p384;105EVP_PKEY *ec_p521;106ASN1_OBJECT *id_pkinit_authData;107ASN1_OBJECT *id_pkinit_DHKeyData;108ASN1_OBJECT *id_pkinit_rkeyData;109ASN1_OBJECT *id_pkinit_san;110ASN1_OBJECT *id_ms_san_upn;111ASN1_OBJECT *id_pkinit_KPClientAuth;112ASN1_OBJECT *id_pkinit_KPKdc;113ASN1_OBJECT *id_ms_kp_sc_logon;114ASN1_OBJECT *id_kp_serverAuth;115};116117struct _pkinit_req_crypto_context {118X509 *received_cert;119EVP_PKEY *client_pkey;120};121122static krb5_error_code pkinit_init_pkinit_oids(pkinit_plg_crypto_context );123static void pkinit_fini_pkinit_oids(pkinit_plg_crypto_context );124125static krb5_error_code pkinit_init_dh_params(krb5_context,126pkinit_plg_crypto_context);127static void pkinit_fini_dh_params(pkinit_plg_crypto_context );128129static krb5_error_code pkinit_init_certs(pkinit_identity_crypto_context ctx);130static void pkinit_fini_certs(pkinit_identity_crypto_context ctx);131132static krb5_error_code pkinit_init_pkcs11(pkinit_identity_crypto_context ctx);133static void pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx);134135static krb5_error_code pkinit_sign_data136(krb5_context context, pkinit_identity_crypto_context cryptoctx,137unsigned char *data, unsigned int data_len,138unsigned char **sig, unsigned int *sig_len);139140static krb5_error_code create_signature141(unsigned char **, unsigned int *, unsigned char *, unsigned int,142EVP_PKEY *pkey);143144#ifdef DEBUG_DH145static void print_dh(DH *, char *);146static void print_pubkey(BIGNUM *, char *);147#endif148149static int openssl_callback (int, X509_STORE_CTX *);150static int openssl_callback_ignore_crls (int, X509_STORE_CTX *);151152static ASN1_OBJECT * pkinit_pkcs7type2oid153(pkinit_plg_crypto_context plg_cryptoctx, int pkcs7_type);154155static krb5_error_code pkinit_create_sequence_of_principal_identifiers156(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx,157pkinit_req_crypto_context req_cryptoctx,158pkinit_identity_crypto_context id_cryptoctx,159int type, krb5_pa_data ***e_data_out);160161#ifndef WITHOUT_PKCS11162static krb5_error_code163pkinit_find_private_key(krb5_context context,164pkinit_identity_crypto_context id_cryptoctx,165CK_ATTRIBUTE_TYPE usage,166CK_OBJECT_HANDLE *objp);167static krb5_error_code pkinit_login168(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,169CK_TOKEN_INFO *tip, const char *password);170static krb5_error_code pkinit_open_session171(krb5_context context, pkinit_identity_crypto_context id_cryptoctx);172#ifdef SILLYDECRYPT173CK_RV pkinit_C_Decrypt174(pkinit_identity_crypto_context id_cryptoctx,175CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,176CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen);177#endif178179static krb5_error_code pkinit_sign_data_pkcs11180(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,181unsigned char *data, unsigned int data_len,182unsigned char **sig, unsigned int *sig_len);183184static krb5_error_code p11err(krb5_context context, CK_RV rv, const char *op);185#endif /* WITHOUT_PKCS11 */186187static krb5_error_code pkinit_sign_data_fs188(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,189unsigned char *data, unsigned int data_len,190unsigned char **sig, unsigned int *sig_len);191192static krb5_error_code193create_krb5_invalidCertificates(krb5_context context,194pkinit_plg_crypto_context plg_cryptoctx,195pkinit_req_crypto_context req_cryptoctx,196pkinit_identity_crypto_context id_cryptoctx,197krb5_external_principal_identifier *** ids);198199static krb5_error_code200create_identifiers_from_stack(STACK_OF(X509) *sk,201krb5_external_principal_identifier *** ids);202203#if OPENSSL_VERSION_NUMBER < 0x10100000L204205/* 1.1 standardizes constructor and destructor names, renaming206* EVP_MD_CTX_{create,destroy} and deprecating ASN1_STRING_data. */207208#define EVP_MD_CTX_new EVP_MD_CTX_create209#define EVP_MD_CTX_free EVP_MD_CTX_destroy210#define ASN1_STRING_get0_data ASN1_STRING_data211212/*213* 1.1 adds DHX support, which uses the RFC 3279 DomainParameters encoding we214* need for PKINIT. For 1.0 we must use the original DH type when creating215* EVP_PKEY objects.216*/217#define EVP_PKEY_DHX EVP_PKEY_DH218219/* 1.1 makes many handle types opaque and adds accessors. Add compatibility220* versions of the new accessors we use for pre-1.1. */221222#define OBJ_get0_data(o) ((o)->data)223#define OBJ_length(o) ((o)->length)224225#define DH_set0_key compat_dh_set0_key226static int227compat_dh_set0_key(DH *dh, BIGNUM *pub, BIGNUM *priv)228{229if (pub != NULL) {230BN_clear_free(dh->pub_key);231dh->pub_key = pub;232}233if (priv != NULL) {234BN_clear_free(dh->priv_key);235dh->priv_key = priv;236}237return 1;238}239240#define DH_get0_key compat_dh_get0_key241static void compat_dh_get0_key(const DH *dh, const BIGNUM **pub,242const BIGNUM **priv)243{244if (pub != NULL)245*pub = dh->pub_key;246if (priv != NULL)247*priv = dh->priv_key;248}249250#define EVP_PKEY_get0_DH compat_get0_DH251static DH *252compat_get0_DH(const EVP_PKEY *pkey)253{254if (pkey->type != EVP_PKEY_DH)255return NULL;256return pkey->pkey.dh;257258}259260#define EVP_PKEY_get0_EC_KEY compat_get0_EC261static EC_KEY *262compat_get0_EC(const EVP_PKEY *pkey)263{264if (pkey->type != EVP_PKEY_EC)265return NULL;266return pkey->pkey.ec;267}268269#define ECDSA_SIG_set0 compat_ECDSA_SIG_set0270static int271compat_ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)272{273sig->r = r;274sig->s = s;275return 1;276}277278/* Return true if the cert c includes a key usage which doesn't include u.279* Define using direct member access for pre-1.1. */280#define ku_reject(c, u) \281(((c)->ex_flags & EXFLAG_KUSAGE) && !((c)->ex_kusage & (u)))282283#else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */284285/* Return true if the cert x includes a key usage which doesn't include u. */286#define ku_reject(c, u) (!(X509_get_key_usage(c) & (u)))287288#endif289290#if OPENSSL_VERSION_NUMBER < 0x30000000L291/* OpenSSL 3.0 changes several preferred function names. */292#define EVP_PKEY_parameters_eq EVP_PKEY_cmp_parameters293#define EVP_PKEY_get_size EVP_PKEY_size294#define EVP_PKEY_get_bits EVP_PKEY_bits295#define EVP_PKEY_get_base_id EVP_PKEY_base_id296297/*298* Convert *dh to an EVP_PKEY object, taking ownership of *dh and setting it to299* NULL. On error, return NULL and do not take ownership of or change *dh.300* OpenSSL 3.0 deprecates the low-level DH interfaces, so this helper will only301* be used with prior versions.302*/303static EVP_PKEY *304dh_to_pkey(DH **dh)305{306EVP_PKEY *pkey;307308pkey = EVP_PKEY_new();309if (pkey == NULL)310return NULL;311if (!EVP_PKEY_assign(pkey, EVP_PKEY_DHX, *dh)) {312EVP_PKEY_free(pkey);313return NULL;314}315*dh = NULL;316return pkey;317}318#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */319320/* Encode a bignum as an ASN.1 integer in DER. */321static int322encode_bn_der(const BIGNUM *bn, uint8_t **der_out, int *len_out)323{324ASN1_INTEGER *intval;325int len;326uint8_t *der = NULL, *outptr;327328intval = BN_to_ASN1_INTEGER(bn, NULL);329if (intval == NULL)330return 0;331len = i2d_ASN1_INTEGER(intval, NULL);332if (len > 0 && (outptr = der = malloc(len)) != NULL)333(void)i2d_ASN1_INTEGER(intval, &outptr);334ASN1_INTEGER_free(intval);335if (der == NULL)336return 0;337*der_out = der;338*len_out = len;339return 1;340}341342/* Decode an ASN.1 integer, returning a bignum. */343static BIGNUM *344decode_bn_der(const uint8_t *der, size_t len)345{346ASN1_INTEGER *intval;347BIGNUM *bn;348349intval = d2i_ASN1_INTEGER(NULL, &der, len);350if (intval == NULL)351return NULL;352bn = ASN1_INTEGER_to_BN(intval, NULL);353ASN1_INTEGER_free(intval);354return bn;355}356357#if OPENSSL_VERSION_NUMBER >= 0x10100000L358359#if OPENSSL_VERSION_NUMBER >= 0x30000000L360static EVP_PKEY *361decode_params(const krb5_data *params_der, const char *type)362{363EVP_PKEY *pkey = NULL;364const uint8_t *inptr = (uint8_t *)params_der->data;365size_t len = params_der->length;366OSSL_DECODER_CTX *dctx;367int ok;368369dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", "type-specific", type,370EVP_PKEY_KEY_PARAMETERS, NULL, NULL);371if (dctx == NULL)372return NULL;373374ok = OSSL_DECODER_from_data(dctx, &inptr, &len);375OSSL_DECODER_CTX_free(dctx);376return ok ? pkey : NULL;377}378379static EVP_PKEY *380decode_dh_params(const krb5_data *params_der)381{382return decode_params(params_der, "DHX");383}384385#else386387static EVP_PKEY *388decode_dh_params(const krb5_data *params_der)389{390const uint8_t *p = (uint8_t *)params_der->data;391DH *dh;392EVP_PKEY *pkey;393394dh = d2i_DHxparams(NULL, &p, params_der->length);395pkey = dh_to_pkey(&dh);396DH_free(dh);397return pkey;398}399400#endif401402static krb5_error_code403encode_spki(EVP_PKEY *pkey, krb5_data *spki_out)404{405krb5_error_code ret = ENOMEM;406int len;407uint8_t *outptr;408409len = i2d_PUBKEY(pkey, NULL);410ret = alloc_data(spki_out, len);411if (ret)412goto cleanup;413outptr = (uint8_t *)spki_out->data;414(void)i2d_PUBKEY(pkey, &outptr);415416cleanup:417return ret;418}419420static EVP_PKEY *421decode_spki(const krb5_data *spki)422{423const uint8_t *inptr = (uint8_t *)spki->data;424425return d2i_PUBKEY(NULL, &inptr, spki->length);426}427428#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */429430/*431* OpenSSL 1.0 has no DHX support, so we need a custom decoder for RFC 3279432* DomainParameters, and we need to use X509_PUBKEY values to marshal433* SubjectPublicKeyInfo.434*/435436typedef struct {437ASN1_BIT_STRING *seed;438BIGNUM *counter;439} int_dhvparams;440441typedef struct {442BIGNUM *p;443BIGNUM *q;444BIGNUM *g;445BIGNUM *j;446int_dhvparams *vparams;447} int_dhxparams;448449ASN1_SEQUENCE(int_dhvparams) = {450ASN1_SIMPLE(int_dhvparams, seed, ASN1_BIT_STRING),451ASN1_SIMPLE(int_dhvparams, counter, BIGNUM)452} ASN1_SEQUENCE_END(int_dhvparams);453454ASN1_SEQUENCE(int_dhxparams) = {455ASN1_SIMPLE(int_dhxparams, p, BIGNUM),456ASN1_SIMPLE(int_dhxparams, g, BIGNUM),457ASN1_SIMPLE(int_dhxparams, q, BIGNUM),458ASN1_OPT(int_dhxparams, j, BIGNUM),459ASN1_OPT(int_dhxparams, vparams, int_dhvparams)460} ASN1_SEQUENCE_END(int_dhxparams);461462static EVP_PKEY *463decode_dh_params(const krb5_data *params_der)464{465int_dhxparams *params;466DH *dh;467EVP_PKEY *pkey;468const uint8_t *p;469470dh = DH_new();471if (dh == NULL)472return NULL;473474p = (uint8_t *)params_der->data;475params = (int_dhxparams *)ASN1_item_d2i(NULL, &p, params_der->length,476ASN1_ITEM_rptr(int_dhxparams));477if (params == NULL) {478DH_free(dh);479return NULL;480}481482/* Steal p, q, and g from dhparams for dh. Ignore j and vparams. */483dh->p = params->p;484dh->q = params->q;485dh->g = params->g;486params->p = params->q = params->g = NULL;487ASN1_item_free((ASN1_VALUE *)params, ASN1_ITEM_rptr(int_dhxparams));488pkey = dh_to_pkey(&dh);489DH_free(dh);490return pkey;491}492493static krb5_error_code494encode_spki(EVP_PKEY *pkey, krb5_data *spki_out)495{496krb5_error_code ret = ENOMEM;497const DH *dh;498uint8_t *param_der = NULL, *pubkey_der = NULL, *outptr;499int param_der_len, pubkey_der_len, len;500X509_PUBKEY pubkey;501int_dhxparams dhxparams;502X509_ALGOR algor;503ASN1_OBJECT algorithm;504ASN1_TYPE parameter;505ASN1_STRING param_str, pubkey_str;506507if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {508/* Only DH keys require special encoding. */509len = i2d_PUBKEY(pkey, NULL);510ret = alloc_data(spki_out, len);511if (ret)512goto cleanup;513outptr = (uint8_t *)spki_out->data;514(void)i2d_PUBKEY(pkey, &outptr);515return 0;516}517518dh = EVP_PKEY_get0_DH(pkey);519if (dh == NULL)520goto cleanup;521522dhxparams.p = dh->p;523dhxparams.q = dh->q;524dhxparams.g = dh->g;525dhxparams.j = NULL;526dhxparams.vparams = NULL;527param_der_len = ASN1_item_i2d((ASN1_VALUE *)&dhxparams, ¶m_der,528ASN1_ITEM_rptr(int_dhxparams));529if (param_der_len < 0)530goto cleanup;531param_str.length = param_der_len;532param_str.type = V_ASN1_SEQUENCE;533param_str.data = param_der;534param_str.flags = 0;535parameter.type = V_ASN1_SEQUENCE;536parameter.value.sequence = ¶m_str;537538memset(&algorithm, 0, sizeof(algorithm));539algorithm.data = (uint8_t *)dh_oid.data;540algorithm.length = dh_oid.length;541542algor.algorithm = &algorithm;543algor.parameter = ¶meter;544545if (!encode_bn_der(dh->pub_key, &pubkey_der, &pubkey_der_len))546goto cleanup;547pubkey_str.length = pubkey_der_len;548pubkey_str.type = V_ASN1_BIT_STRING;549pubkey_str.data = pubkey_der;550pubkey_str.flags = ASN1_STRING_FLAG_BITS_LEFT;551552pubkey.algor = &algor;553pubkey.public_key = &pubkey_str;554len = i2d_X509_PUBKEY(&pubkey, NULL);555if (len < 0)556goto cleanup;557ret = alloc_data(spki_out, len);558if (ret)559goto cleanup;560outptr = (uint8_t *)spki_out->data;561i2d_X509_PUBKEY(&pubkey, &outptr);562563cleanup:564OPENSSL_free(param_der);565free(pubkey_der);566return ret;567}568569static EVP_PKEY *570decode_spki(const krb5_data *spki)571{572X509_PUBKEY *pubkey = NULL;573const uint8_t *inptr;574DH *dh;575EVP_PKEY *pkey = NULL, *pkey_ret = NULL;576const ASN1_STRING *params;577const ASN1_BIT_STRING *public_key;578krb5_data d;579580inptr = (uint8_t *)spki->data;581pubkey = d2i_X509_PUBKEY(NULL, &inptr, spki->length);582if (pubkey == NULL)583goto cleanup;584585if (OBJ_cmp(pubkey->algor->algorithm, OBJ_nid2obj(NID_dhKeyAgreement))) {586/* This is not a DH key, so we don't need special decoding. */587X509_PUBKEY_free(pubkey);588inptr = (uint8_t *)spki->data;589return d2i_PUBKEY(NULL, &inptr, spki->length);590}591592if (pubkey->algor->parameter->type != V_ASN1_SEQUENCE)593goto cleanup;594params = pubkey->algor->parameter->value.sequence;595d = make_data(params->data, params->length);596pkey = decode_dh_params(&d);597if (pkey == NULL)598goto cleanup;599dh = EVP_PKEY_get0_DH(pkey);600if (dh == NULL)601goto cleanup;602public_key = pubkey->public_key;603dh->pub_key = decode_bn_der(public_key->data, public_key->length);604if (dh->pub_key == NULL)605goto cleanup;606607pkey_ret = pkey;608pkey = NULL;609610cleanup:611X509_PUBKEY_free(pubkey);612EVP_PKEY_free(pkey);613return pkey_ret;614}615616#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */617618#if OPENSSL_VERSION_NUMBER >= 0x30000000L619620static EVP_PKEY *621decode_ec_params(const krb5_data *params_der)622{623return decode_params(params_der, "EC");624}625626#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */627628static EVP_PKEY *629decode_ec_params(const krb5_data *params_der)630{631const uint8_t *p = (uint8_t *)params_der->data;632EC_KEY *eckey;633EVP_PKEY *pkey;634635eckey = d2i_ECParameters(NULL, &p, params_der->length);636if (eckey == NULL)637return NULL;638pkey = EVP_PKEY_new();639if (pkey != NULL) {640if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {641EVP_PKEY_free(pkey);642pkey = NULL;643}644}645EC_KEY_free(eckey);646return pkey;647}648649#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */650651/* Attempt to specify padded Diffie-Hellman result derivation. Don't error out652* if this fails since we also detect short results and adjust them. */653#if OPENSSL_VERSION_NUMBER >= 0x30000000L654static void655set_padded_derivation(EVP_PKEY_CTX *ctx)656{657EVP_PKEY_CTX_set_dh_pad(ctx, 1);658}659#elif OPENSSL_VERSION_NUMBER >= 0x10100000L660static void661set_padded_derivation(EVP_PKEY_CTX *ctx)662{663/* We would use EVP_PKEY_CTX_set_dh_pad() but it doesn't work with DHX. */664EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,665EVP_PKEY_CTRL_DH_PAD, 1, NULL);666}667#else668static void669set_padded_derivation(EVP_PKEY_CTX *ctx)670{671/* There's no support for padded derivation in 1.0. */672}673#endif674675static int676dh_result(EVP_PKEY *pkey, EVP_PKEY *peer,677uint8_t **result_out, unsigned int *len_out)678{679EVP_PKEY_CTX *derive_ctx = NULL;680int ok = 0;681uint8_t *buf = NULL;682size_t len, result_size;683krb5_boolean ecc = (EVP_PKEY_id(pkey) == EVP_PKEY_EC);684685*result_out = NULL;686*len_out = 0;687688derive_ctx = EVP_PKEY_CTX_new(pkey, NULL);689if (derive_ctx == NULL)690goto cleanup;691if (EVP_PKEY_derive_init(derive_ctx) <= 0)692goto cleanup;693if (!ecc)694set_padded_derivation(derive_ctx);695if (EVP_PKEY_derive_set_peer(derive_ctx, peer) <= 0)696goto cleanup;697698if (ecc) {699if (EVP_PKEY_derive(derive_ctx, NULL, &result_size) <= 0)700goto cleanup;701} else {702/*703* For finite-field Diffie-Hellman we must ensure that the result704* matches the key size (normally through padded derivation, but that705* isn't supported by OpenSSL 1.0 so we must check).706*/707result_size = EVP_PKEY_get_size(pkey);708}709buf = malloc(result_size);710if (buf == NULL)711goto cleanup;712len = result_size;713if (EVP_PKEY_derive(derive_ctx, buf, &len) <= 0)714goto cleanup;715716/* If we couldn't specify padded derivation for finite-field DH we may need717* to fix up the result by right-shifting it within the buffer. */718if (len < result_size) {719memmove(buf + (result_size - len), buf, len);720memset(buf, 0, result_size - len);721}722723ok = 1;724*result_out = buf;725*len_out = result_size;726buf = NULL;727728cleanup:729EVP_PKEY_CTX_free(derive_ctx);730free(buf);731return ok;732}733734#if OPENSSL_VERSION_NUMBER >= 0x30000000L735static int736dh_pubkey_der(EVP_PKEY *pkey, uint8_t **pubkey_out, unsigned int *len_out)737{738BIGNUM *pubkey_bn = NULL;739int len, ok = 0;740uint8_t *buf, *outptr;741742if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) {743len = i2d_PublicKey(pkey, NULL);744if (len > 0 && (outptr = buf = malloc(len)) != NULL) {745(void)i2d_PublicKey(pkey, &outptr);746ok = 1;747}748} else {749if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pubkey_bn))750return 0;751ok = encode_bn_der(pubkey_bn, &buf, &len);752BN_free(pubkey_bn);753}754if (ok) {755*pubkey_out = buf;756*len_out = len;757}758return ok;759}760#else761static int762dh_pubkey_der(EVP_PKEY *pkey, uint8_t **pubkey_out, unsigned int *len_out)763{764const DH *dh;765EC_KEY *eckey; /* can be const when OpenSSL 1.0 dropped */766const BIGNUM *pubkey_bn;767uint8_t *buf, *outptr;768int len;769770dh = EVP_PKEY_get0_DH(pkey);771if (dh != NULL) {772DH_get0_key(dh, &pubkey_bn, NULL);773if (!encode_bn_der(pubkey_bn, &buf, &len))774return 0;775*pubkey_out = buf;776*len_out = len;777return 1;778}779780eckey = EVP_PKEY_get0_EC_KEY(pkey);781if (eckey != NULL) {782len = i2o_ECPublicKey(eckey, NULL);783if (len > 0 && (outptr = buf = malloc(len)) != NULL) {784(void)i2o_ECPublicKey(eckey, &outptr);785*pubkey_out = buf;786*len_out = len;787return 1;788}789}790791return 0;792}793#endif794795#if OPENSSL_VERSION_NUMBER >= 0x10100000L796/* OpenSSL 1.1 and later will copy the q parameter when generating keys. */797static int798copy_q_openssl10(EVP_PKEY *src, EVP_PKEY *dest)799{800return 1;801}802#else803/* OpenSSL 1.0 won't copy the q parameter, so we have to do it. */804static int805copy_q_openssl10(EVP_PKEY *src, EVP_PKEY *dest)806{807DH *dhsrc = EVP_PKEY_get0_DH(src), *dhdest = EVP_PKEY_get0_DH(dest);808809if (dhsrc == NULL || dhsrc->q == NULL || dhdest == NULL)810return 0;811if (dhdest->q != NULL)812return 1;813dhdest->q = BN_dup(dhsrc->q);814return dhdest->q != NULL;815}816#endif817818static EVP_PKEY *819generate_dh_pkey(EVP_PKEY *params)820{821EVP_PKEY_CTX *ctx = NULL;822EVP_PKEY *pkey = NULL;823824ctx = EVP_PKEY_CTX_new(params, NULL);825if (ctx == NULL)826goto cleanup;827if (EVP_PKEY_keygen_init(ctx) <= 0)828goto cleanup;829if (EVP_PKEY_keygen(ctx, &pkey) <= 0)830goto cleanup;831if (EVP_PKEY_get_base_id(pkey) == EVP_PKEY_DH &&832!copy_q_openssl10(params, pkey)) {833EVP_PKEY_free(pkey);834pkey = NULL;835}836837cleanup:838EVP_PKEY_CTX_free(ctx);839return pkey;840}841842#if OPENSSL_VERSION_NUMBER >= 0x30000000L843844static EVP_PKEY *845compose_dh_pkey(EVP_PKEY *params, const uint8_t *pubkey_der, size_t der_len)846{847EVP_PKEY *pkey = NULL, *pkey_ret = NULL;848BIGNUM *pubkey_bn = NULL;849uint8_t *pubkey_bin = NULL;850int binlen;851852pkey = EVP_PKEY_dup(params);853if (pkey == NULL)854goto cleanup;855856if (EVP_PKEY_id(params) == EVP_PKEY_EC) {857if (d2i_PublicKey(EVP_PKEY_id(params), &pkey, &pubkey_der,858der_len) == NULL)859goto cleanup;860} else {861pubkey_bn = decode_bn_der(pubkey_der, der_len);862if (pubkey_bn == NULL)863goto cleanup;864binlen = EVP_PKEY_get_size(pkey);865pubkey_bin = malloc(binlen);866if (pubkey_bin == NULL)867goto cleanup;868if (BN_bn2binpad(pubkey_bn, pubkey_bin, binlen) != binlen)869goto cleanup;870if (EVP_PKEY_set1_encoded_public_key(pkey, pubkey_bin, binlen) != 1)871goto cleanup;872}873874pkey_ret = pkey;875pkey = NULL;876877cleanup:878EVP_PKEY_free(pkey);879BN_free(pubkey_bn);880free(pubkey_bin);881return pkey_ret;882}883884#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */885886#if OPENSSL_VERSION_NUMBER >= 0x10100000L887static DH *888dup_dh_params(DH *src)889{890return DHparams_dup(src);891}892#else893/* DHparams_dup() won't copy q in OpenSSL 1.0. */894static DH *895dup_dh_params(DH *src)896{897DH *dh;898899dh = DH_new();900if (dh == NULL)901return NULL;902dh->p = BN_dup(src->p);903dh->q = BN_dup(src->q);904dh->g = BN_dup(src->g);905if (dh->p == NULL || dh->q == NULL || dh->g == NULL) {906DH_free(dh);907return NULL;908}909return dh;910}911#endif912913static EVP_PKEY *914compose_dh_pkey(EVP_PKEY *params, const uint8_t *pubkey_der, size_t der_len)915{916DH *dhparams, *dh = NULL;917EVP_PKEY *pkey = NULL, *pkey_ret = NULL;918BIGNUM *pubkey_bn = NULL;919EC_KEY *params_eckey, *eckey = NULL;920const EC_GROUP *group;921922if (EVP_PKEY_id(params) == EVP_PKEY_EC) {923/* We would like to use EVP_PKEY_copy_parameters() and d2i_PublicKey(),924* but the latter is broken in OpenSSL 1.1.0-1.1.1a for EC keys. */925params_eckey = EVP_PKEY_get0_EC_KEY(params);926if (params_eckey == NULL)927goto cleanup;928group = EC_KEY_get0_group(params_eckey);929eckey = EC_KEY_new();930if (eckey == NULL)931goto cleanup;932if (!EC_KEY_set_group(eckey, group))933goto cleanup;934if (o2i_ECPublicKey(&eckey, &pubkey_der, der_len) == NULL)935goto cleanup;936pkey = EVP_PKEY_new();937if (pkey == NULL)938return NULL;939if (!EVP_PKEY_assign(pkey, EVP_PKEY_EC, eckey)) {940EVP_PKEY_free(pkey);941return NULL;942}943eckey = NULL;944} else {945pubkey_bn = decode_bn_der(pubkey_der, der_len);946if (pubkey_bn == NULL)947goto cleanup;948949dhparams = EVP_PKEY_get0_DH(params);950if (dhparams == NULL)951goto cleanup;952dh = dup_dh_params(dhparams);953if (dh == NULL)954goto cleanup;955if (!DH_set0_key(dh, pubkey_bn, NULL))956goto cleanup;957pubkey_bn = NULL;958959pkey = dh_to_pkey(&dh);960}961962pkey_ret = pkey;963pkey = NULL;964965cleanup:966BN_free(pubkey_bn);967DH_free(dh);968EC_KEY_free(eckey);969EVP_PKEY_free(pkey);970return pkey_ret;971}972973#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */974975#ifndef WITHOUT_PKC11976static struct pkcs11_errstrings {977CK_RV code;978char *text;979} pkcs11_errstrings[] = {980{ 0x0, "ok" },981{ 0x1, "cancel" },982{ 0x2, "host memory" },983{ 0x3, "slot id invalid" },984{ 0x5, "general error" },985{ 0x6, "function failed" },986{ 0x7, "arguments bad" },987{ 0x8, "no event" },988{ 0x9, "need to create threads" },989{ 0xa, "cant lock" },990{ 0x10, "attribute read only" },991{ 0x11, "attribute sensitive" },992{ 0x12, "attribute type invalid" },993{ 0x13, "attribute value invalid" },994{ 0x20, "data invalid" },995{ 0x21, "data len range" },996{ 0x30, "device error" },997{ 0x31, "device memory" },998{ 0x32, "device removed" },999{ 0x40, "encrypted data invalid" },1000{ 0x41, "encrypted data len range" },1001{ 0x50, "function canceled" },1002{ 0x51, "function not parallel" },1003{ 0x54, "function not supported" },1004{ 0x60, "key handle invalid" },1005{ 0x62, "key size range" },1006{ 0x63, "key type inconsistent" },1007{ 0x64, "key not needed" },1008{ 0x65, "key changed" },1009{ 0x66, "key needed" },1010{ 0x67, "key indigestible" },1011{ 0x68, "key function not permitted" },1012{ 0x69, "key not wrappable" },1013{ 0x6a, "key unextractable" },1014{ 0x70, "mechanism invalid" },1015{ 0x71, "mechanism param invalid" },1016{ 0x82, "object handle invalid" },1017{ 0x90, "operation active" },1018{ 0x91, "operation not initialized" },1019{ 0xa0, "pin incorrect" },1020{ 0xa1, "pin invalid" },1021{ 0xa2, "pin len range" },1022{ 0xa3, "pin expired" },1023{ 0xa4, "pin locked" },1024{ 0xb0, "session closed" },1025{ 0xb1, "session count" },1026{ 0xb3, "session handle invalid" },1027{ 0xb4, "session parallel not supported" },1028{ 0xb5, "session read only" },1029{ 0xb6, "session exists" },1030{ 0xb7, "session read only exists" },1031{ 0xb8, "session read write so exists" },1032{ 0xc0, "signature invalid" },1033{ 0xc1, "signature len range" },1034{ 0xd0, "template incomplete" },1035{ 0xd1, "template inconsistent" },1036{ 0xe0, "token not present" },1037{ 0xe1, "token not recognized" },1038{ 0xe2, "token write protected" },1039{ 0xf0, "unwrapping key handle invalid" },1040{ 0xf1, "unwrapping key size range" },1041{ 0xf2, "unwrapping key type inconsistent" },1042{ 0x100, "user already logged in" },1043{ 0x101, "user not logged in" },1044{ 0x102, "user pin not initialized" },1045{ 0x103, "user type invalid" },1046{ 0x104, "user another already logged in" },1047{ 0x105, "user too many types" },1048{ 0x110, "wrapped key invalid" },1049{ 0x112, "wrapped key len range" },1050{ 0x113, "wrapping key handle invalid" },1051{ 0x114, "wrapping key size range" },1052{ 0x115, "wrapping key type inconsistent" },1053{ 0x120, "random seed not supported" },1054{ 0x121, "random no rng" },1055{ 0x130, "domain params invalid" },1056{ 0x150, "buffer too small" },1057{ 0x160, "saved state invalid" },1058{ 0x170, "information sensitive" },1059{ 0x180, "state unsaveable" },1060{ 0x190, "cryptoki not initialized" },1061{ 0x191, "cryptoki already initialized" },1062{ 0x1a0, "mutex bad" },1063{ 0x1a1, "mutex not locked" },1064{ 0x200, "function rejected" },1065{ -1, NULL }1066};1067#endif10681069MAKE_INIT_FUNCTION(pkinit_openssl_init);10701071static krb5_error_code oerr(krb5_context context, krb5_error_code code,1072const char *fmt, ...)1073#if !defined(__cplusplus) && (__GNUC__ > 2)1074__attribute__((__format__(__printf__, 3, 4)))1075#endif1076;10771078/*1079* Set an error string containing the formatted arguments and the first pending1080* OpenSSL error. Write the formatted arguments and all pending OpenSSL error1081* messages to the trace log. Return code, or KRB5KDC_ERR_PREAUTH_FAILED if1082* code is 0.1083*/1084static krb5_error_code1085oerr(krb5_context context, krb5_error_code code, const char *fmt, ...)1086{1087unsigned long err;1088va_list ap;1089char *str, buf[128];1090int r;10911092if (!code)1093code = KRB5KDC_ERR_PREAUTH_FAILED;10941095va_start(ap, fmt);1096r = vasprintf(&str, fmt, ap);1097va_end(ap);1098if (r < 0)1099return code;11001101err = ERR_peek_error();1102if (err) {1103krb5_set_error_message(context, code, _("%s: %s"), str,1104ERR_reason_error_string(err));1105} else {1106krb5_set_error_message(context, code, "%s", str);1107}11081109TRACE_PKINIT_OPENSSL_ERROR(context, str);1110while ((err = ERR_get_error()) != 0) {1111ERR_error_string_n(err, buf, sizeof(buf));1112TRACE_PKINIT_OPENSSL_ERROR(context, buf);1113}11141115free(str);1116return code;1117}11181119/*1120* Set an appropriate error string containing msg for a certificate1121* verification failure from certctx. Write the message and all pending1122* OpenSSL error messages to the trace log. Return code, or1123* KRB5KDC_ERR_PREAUTH_FAILED if code is 0.1124*/1125static krb5_error_code1126oerr_cert(krb5_context context, krb5_error_code code, X509_STORE_CTX *certctx,1127const char *msg)1128{1129int depth = X509_STORE_CTX_get_error_depth(certctx);1130int err = X509_STORE_CTX_get_error(certctx);1131const char *errstr = X509_verify_cert_error_string(err);11321133return oerr(context, code, _("%s (depth %d): %s"), msg, depth, errstr);1134}11351136krb5_error_code1137pkinit_init_plg_crypto(krb5_context context,1138pkinit_plg_crypto_context *cryptoctx)1139{1140krb5_error_code retval = ENOMEM;1141pkinit_plg_crypto_context ctx = NULL;11421143(void)CALL_INIT_FUNCTION(pkinit_openssl_init);11441145ctx = malloc(sizeof(*ctx));1146if (ctx == NULL)1147goto out;1148memset(ctx, 0, sizeof(*ctx));11491150pkiDebug("%s: initializing openssl crypto context at %p\n",1151__FUNCTION__, ctx);1152retval = pkinit_init_pkinit_oids(ctx);1153if (retval)1154goto out;11551156retval = pkinit_init_dh_params(context, ctx);1157if (retval)1158goto out;11591160*cryptoctx = ctx;11611162out:1163if (retval && ctx != NULL)1164pkinit_fini_plg_crypto(ctx);11651166return retval;1167}11681169void1170pkinit_fini_plg_crypto(pkinit_plg_crypto_context cryptoctx)1171{1172pkiDebug("%s: freeing context at %p\n", __FUNCTION__, cryptoctx);11731174if (cryptoctx == NULL)1175return;1176pkinit_fini_pkinit_oids(cryptoctx);1177pkinit_fini_dh_params(cryptoctx);1178free(cryptoctx);1179}11801181krb5_error_code1182pkinit_init_identity_crypto(pkinit_identity_crypto_context *idctx)1183{1184krb5_error_code retval = ENOMEM;1185pkinit_identity_crypto_context ctx = NULL;11861187ctx = malloc(sizeof(*ctx));1188if (ctx == NULL)1189goto out;1190memset(ctx, 0, sizeof(*ctx));11911192ctx->identity = NULL;11931194retval = pkinit_init_certs(ctx);1195if (retval)1196goto out;11971198retval = pkinit_init_pkcs11(ctx);1199if (retval)1200goto out;12011202pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);1203*idctx = ctx;12041205out:1206if (retval) {1207if (ctx)1208pkinit_fini_identity_crypto(ctx);1209}12101211return retval;1212}12131214void1215pkinit_fini_identity_crypto(pkinit_identity_crypto_context idctx)1216{1217if (idctx == NULL)1218return;12191220pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, idctx);1221if (idctx->deferred_ids != NULL)1222pkinit_free_deferred_ids(idctx->deferred_ids);1223free(idctx->identity);1224pkinit_fini_certs(idctx);1225pkinit_fini_pkcs11(idctx);1226free(idctx);1227}12281229krb5_error_code1230pkinit_init_req_crypto(pkinit_req_crypto_context *cryptoctx)1231{1232krb5_error_code retval = ENOMEM;1233pkinit_req_crypto_context ctx = NULL;12341235ctx = malloc(sizeof(*ctx));1236if (ctx == NULL)1237goto out;1238memset(ctx, 0, sizeof(*ctx));12391240ctx->client_pkey = NULL;1241ctx->received_cert = NULL;12421243*cryptoctx = ctx;12441245pkiDebug("%s: returning ctx at %p\n", __FUNCTION__, ctx);1246retval = 0;1247out:1248if (retval)1249free(ctx);12501251return retval;1252}12531254void1255pkinit_fini_req_crypto(pkinit_req_crypto_context req_cryptoctx)1256{1257if (req_cryptoctx == NULL)1258return;12591260pkiDebug("%s: freeing ctx at %p\n", __FUNCTION__, req_cryptoctx);1261EVP_PKEY_free(req_cryptoctx->client_pkey);1262X509_free(req_cryptoctx->received_cert);12631264free(req_cryptoctx);1265}12661267static krb5_error_code1268pkinit_init_pkinit_oids(pkinit_plg_crypto_context ctx)1269{1270ctx->id_pkinit_san = OBJ_txt2obj("1.3.6.1.5.2.2", 1);1271if (ctx->id_pkinit_san == NULL)1272return ENOMEM;12731274ctx->id_pkinit_authData = OBJ_txt2obj("1.3.6.1.5.2.3.1", 1);1275if (ctx->id_pkinit_authData == NULL)1276return ENOMEM;12771278ctx->id_pkinit_DHKeyData = OBJ_txt2obj("1.3.6.1.5.2.3.2", 1);1279if (ctx->id_pkinit_DHKeyData == NULL)1280return ENOMEM;12811282ctx->id_pkinit_rkeyData = OBJ_txt2obj("1.3.6.1.5.2.3.3", 1);1283if (ctx->id_pkinit_rkeyData == NULL)1284return ENOMEM;12851286ctx->id_pkinit_KPClientAuth = OBJ_txt2obj("1.3.6.1.5.2.3.4", 1);1287if (ctx->id_pkinit_KPClientAuth == NULL)1288return ENOMEM;12891290ctx->id_pkinit_KPKdc = OBJ_txt2obj("1.3.6.1.5.2.3.5", 1);1291if (ctx->id_pkinit_KPKdc == NULL)1292return ENOMEM;12931294ctx->id_ms_kp_sc_logon = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.2", 1);1295if (ctx->id_ms_kp_sc_logon == NULL)1296return ENOMEM;12971298ctx->id_ms_san_upn = OBJ_txt2obj("1.3.6.1.4.1.311.20.2.3", 1);1299if (ctx->id_ms_san_upn == NULL)1300return ENOMEM;13011302ctx->id_kp_serverAuth = OBJ_txt2obj("1.3.6.1.5.5.7.3.1", 1);1303if (ctx->id_kp_serverAuth == NULL)1304return ENOMEM;13051306return 0;1307}13081309static krb5_error_code1310get_cert(char *filename, X509 **retcert)1311{1312X509 *cert = NULL;1313BIO *tmp = NULL;1314int code;1315krb5_error_code retval;13161317if (filename == NULL || retcert == NULL)1318return EINVAL;13191320*retcert = NULL;13211322tmp = BIO_new(BIO_s_file());1323if (tmp == NULL)1324return ENOMEM;13251326code = BIO_read_filename(tmp, filename);1327if (code == 0) {1328retval = errno;1329goto cleanup;1330}13311332cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL);1333if (cert == NULL) {1334retval = EIO;1335pkiDebug("failed to read certificate from %s\n", filename);1336goto cleanup;1337}1338*retcert = cert;1339retval = 0;1340cleanup:1341if (tmp != NULL)1342BIO_free(tmp);1343return retval;1344}13451346struct get_key_cb_data {1347krb5_context context;1348pkinit_identity_crypto_context id_cryptoctx;1349const char *fsname;1350char *filename;1351const char *password;1352};13531354static int1355get_key_cb(char *buf, int size, int rwflag, void *userdata)1356{1357struct get_key_cb_data *data = userdata;1358pkinit_identity_crypto_context id_cryptoctx;1359krb5_data rdat;1360krb5_prompt kprompt;1361krb5_prompt_type prompt_type;1362krb5_error_code retval;1363char *prompt;13641365if (data->id_cryptoctx->defer_id_prompt) {1366/* Supply the identity name to be passed to a responder callback. */1367pkinit_set_deferred_id(&data->id_cryptoctx->deferred_ids,1368data->fsname, 0, NULL);1369return -1;1370}1371if (data->password == NULL) {1372/* We don't already have a password to use, so prompt for one. */1373if (data->id_cryptoctx->prompter == NULL)1374return -1;1375if (asprintf(&prompt, "%s %s", _("Pass phrase for"),1376data->filename) < 0)1377return -1;1378rdat.data = buf;1379rdat.length = size;1380kprompt.prompt = prompt;1381kprompt.hidden = 1;1382kprompt.reply = &rdat;1383prompt_type = KRB5_PROMPT_TYPE_PREAUTH;13841385/* PROMPTER_INVOCATION */1386k5int_set_prompt_types(data->context, &prompt_type);1387id_cryptoctx = data->id_cryptoctx;1388retval = (data->id_cryptoctx->prompter)(data->context,1389id_cryptoctx->prompter_data,1390NULL, NULL, 1, &kprompt);1391k5int_set_prompt_types(data->context, 0);1392free(prompt);1393if (retval != 0)1394return -1;1395} else {1396/* Just use the already-supplied password. */1397rdat.length = strlen(data->password);1398if ((int)rdat.length >= size)1399return -1;1400snprintf(buf, size, "%s", data->password);1401}1402return (int)rdat.length;1403}14041405static krb5_error_code1406get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx,1407char *filename, const char *fsname, EVP_PKEY **retkey,1408const char *password)1409{1410EVP_PKEY *pkey = NULL;1411BIO *tmp = NULL;1412struct get_key_cb_data cb_data;1413int code;1414krb5_error_code retval;14151416if (filename == NULL || retkey == NULL)1417return EINVAL;14181419tmp = BIO_new(BIO_s_file());1420if (tmp == NULL)1421return ENOMEM;14221423code = BIO_read_filename(tmp, filename);1424if (code == 0) {1425retval = errno;1426goto cleanup;1427}1428cb_data.context = context;1429cb_data.id_cryptoctx = id_cryptoctx;1430cb_data.filename = filename;1431cb_data.fsname = fsname;1432cb_data.password = password;1433pkey = PEM_read_bio_PrivateKey(tmp, NULL, get_key_cb, &cb_data);1434if (pkey == NULL && !id_cryptoctx->defer_id_prompt) {1435retval = EIO;1436pkiDebug("failed to read private key from %s\n", filename);1437goto cleanup;1438}1439*retkey = pkey;1440retval = 0;1441cleanup:1442if (tmp != NULL)1443BIO_free(tmp);1444return retval;1445}14461447static void1448pkinit_fini_pkinit_oids(pkinit_plg_crypto_context ctx)1449{1450if (ctx == NULL)1451return;1452ASN1_OBJECT_free(ctx->id_pkinit_san);1453ASN1_OBJECT_free(ctx->id_pkinit_authData);1454ASN1_OBJECT_free(ctx->id_pkinit_DHKeyData);1455ASN1_OBJECT_free(ctx->id_pkinit_rkeyData);1456ASN1_OBJECT_free(ctx->id_pkinit_KPClientAuth);1457ASN1_OBJECT_free(ctx->id_pkinit_KPKdc);1458ASN1_OBJECT_free(ctx->id_ms_kp_sc_logon);1459ASN1_OBJECT_free(ctx->id_ms_san_upn);1460ASN1_OBJECT_free(ctx->id_kp_serverAuth);1461}14621463static int1464try_import_group(krb5_context context, const krb5_data *params,1465const char *name, krb5_boolean ec, EVP_PKEY **pkey_out)1466{1467*pkey_out = ec ? decode_ec_params(params) : decode_dh_params(params);1468if (*pkey_out == NULL)1469TRACE_PKINIT_DH_GROUP_UNAVAILABLE(context, name);1470return (*pkey_out != NULL) ? 1 : 0;1471}14721473static krb5_error_code1474pkinit_init_dh_params(krb5_context context, pkinit_plg_crypto_context plgctx)1475{1476int n = 0;14771478n += try_import_group(context, &oakley_1024, "MODP 2 (1024-bit)", FALSE,1479&plgctx->dh_1024);1480n += try_import_group(context, &oakley_2048, "MODP 14 (2048-bit)", FALSE,1481&plgctx->dh_2048);1482n += try_import_group(context, &oakley_4096, "MODP 16 (4096-bit)", FALSE,1483&plgctx->dh_4096);1484n += try_import_group(context, &ec_p256, "P-256", TRUE, &plgctx->ec_p256);1485n += try_import_group(context, &ec_p384, "P-384", TRUE, &plgctx->ec_p384);1486n += try_import_group(context, &ec_p521, "P-521", TRUE, &plgctx->ec_p521);14871488if (n == 0) {1489pkinit_fini_dh_params(plgctx);1490k5_setmsg(context, ENOMEM,1491_("PKINIT cannot initialize any key exchange groups"));1492return ENOMEM;1493}14941495return 0;1496}14971498static void1499pkinit_fini_dh_params(pkinit_plg_crypto_context plgctx)1500{1501EVP_PKEY_free(plgctx->dh_1024);1502EVP_PKEY_free(plgctx->dh_2048);1503EVP_PKEY_free(plgctx->dh_4096);1504EVP_PKEY_free(plgctx->ec_p256);1505EVP_PKEY_free(plgctx->ec_p384);1506EVP_PKEY_free(plgctx->ec_p521);1507plgctx->dh_1024 = plgctx->dh_2048 = plgctx->dh_4096 = NULL;1508plgctx->ec_p256 = plgctx->ec_p384 = plgctx->ec_p521 = NULL;1509}15101511static krb5_error_code1512pkinit_init_certs(pkinit_identity_crypto_context ctx)1513{1514krb5_error_code retval = ENOMEM;1515int i;15161517for (i = 0; i < MAX_CREDS_ALLOWED; i++)1518ctx->creds[i] = NULL;1519ctx->my_cert = NULL;1520ctx->my_key = NULL;1521ctx->trustedCAs = NULL;1522ctx->intermediateCAs = NULL;1523ctx->revoked = NULL;15241525retval = 0;1526return retval;1527}15281529static void1530pkinit_fini_certs(pkinit_identity_crypto_context ctx)1531{1532if (ctx == NULL)1533return;15341535if (ctx->my_cert != NULL)1536X509_free(ctx->my_cert);15371538if (ctx->my_key != NULL)1539EVP_PKEY_free(ctx->my_key);15401541if (ctx->trustedCAs != NULL)1542sk_X509_pop_free(ctx->trustedCAs, X509_free);15431544if (ctx->intermediateCAs != NULL)1545sk_X509_pop_free(ctx->intermediateCAs, X509_free);15461547if (ctx->revoked != NULL)1548sk_X509_CRL_pop_free(ctx->revoked, X509_CRL_free);1549}15501551static krb5_error_code1552pkinit_init_pkcs11(pkinit_identity_crypto_context ctx)1553{1554krb5_error_code retval = ENOMEM;15551556#ifndef WITHOUT_PKCS111557ctx->p11_module_name = strdup(PKCS11_MODNAME);1558if (ctx->p11_module_name == NULL)1559return retval;1560ctx->p11_module = NULL;1561ctx->slotid = PK_NOSLOT;1562ctx->token_label = NULL;1563ctx->cert_label = NULL;1564ctx->session = CK_INVALID_HANDLE;1565ctx->p11 = NULL;1566#endif1567ctx->pkcs11_method = 0;15681569retval = 0;1570return retval;1571}15721573static void1574pkinit_fini_pkcs11(pkinit_identity_crypto_context ctx)1575{1576#ifndef WITHOUT_PKCS111577if (ctx == NULL)1578return;15791580if (ctx->p11 != NULL) {1581if (ctx->session != CK_INVALID_HANDLE) {1582ctx->p11->C_CloseSession(ctx->session);1583ctx->session = CK_INVALID_HANDLE;1584}1585ctx->p11->C_Finalize(NULL_PTR);1586ctx->p11 = NULL;1587}1588if (ctx->p11_module != NULL) {1589krb5int_close_plugin(ctx->p11_module);1590ctx->p11_module = NULL;1591}1592free(ctx->p11_module_name);1593free(ctx->token_label);1594free(ctx->cert_id);1595free(ctx->cert_label);1596ctx->p11_module_name = ctx->token_label = ctx->cert_label = NULL;1597ctx->cert_id = NULL;1598#endif1599}16001601krb5_error_code1602pkinit_identity_set_prompter(pkinit_identity_crypto_context id_cryptoctx,1603krb5_prompter_fct prompter,1604void *prompter_data)1605{1606id_cryptoctx->prompter = prompter;1607id_cryptoctx->prompter_data = prompter_data;16081609return 0;1610}16111612/* Create a CMS ContentInfo of type oid containing the octet string in data. */1613static krb5_error_code1614create_contentinfo(krb5_context context, ASN1_OBJECT *oid,1615unsigned char *data, size_t data_len, PKCS7 **p7_out)1616{1617PKCS7 *p7 = NULL;1618ASN1_OCTET_STRING *ostr = NULL;16191620*p7_out = NULL;16211622ostr = ASN1_OCTET_STRING_new();1623if (ostr == NULL)1624goto oom;1625if (!ASN1_OCTET_STRING_set(ostr, (unsigned char *)data, data_len))1626goto oom;16271628p7 = PKCS7_new();1629if (p7 == NULL)1630goto oom;1631p7->type = OBJ_dup(oid);1632if (p7->type == NULL)1633goto oom;16341635p7->d.other = ASN1_TYPE_new();1636if (p7->d.other == NULL)1637goto oom;1638p7->d.other->type = V_ASN1_OCTET_STRING;1639p7->d.other->value.octet_string = ostr;16401641*p7_out = p7;1642return 0;16431644oom:1645if (ostr != NULL)1646ASN1_OCTET_STRING_free(ostr);1647if (p7 != NULL)1648PKCS7_free(p7);1649return ENOMEM;1650}16511652krb5_error_code1653cms_contentinfo_create(krb5_context context, /* IN */1654pkinit_plg_crypto_context plg_cryptoctx, /* IN */1655pkinit_req_crypto_context req_cryptoctx, /* IN */1656pkinit_identity_crypto_context id_cryptoctx, /* IN */1657int cms_msg_type,1658unsigned char *data, unsigned int data_len,1659unsigned char **out_data, unsigned int *out_data_len)1660{1661krb5_error_code retval = ENOMEM;1662ASN1_OBJECT *oid;1663PKCS7 *p7 = NULL;1664unsigned char *p;16651666/* Pick the correct oid for the eContentInfo. */1667oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type);1668if (oid == NULL)1669goto cleanup;1670retval = create_contentinfo(context, oid, data, data_len, &p7);1671if (retval != 0)1672goto cleanup;1673*out_data_len = i2d_PKCS7(p7, NULL);1674if (!(*out_data_len)) {1675retval = oerr(context, 0, _("Failed to DER encode PKCS7"));1676goto cleanup;1677}1678retval = ENOMEM;1679if ((p = *out_data = malloc(*out_data_len)) == NULL)1680goto cleanup;16811682/* DER encode PKCS7 data */1683retval = i2d_PKCS7(p7, &p);1684if (!retval) {1685retval = oerr(context, 0, _("Failed to DER encode PKCS7"));1686goto cleanup;1687}1688retval = 0;1689cleanup:1690if (p7)1691PKCS7_free(p7);1692return retval;1693}16941695/* Return the name ID of the signature algorithm for cert, assuming that the1696* digest used is SHA-256 and the cert uses either an RSA or EC public key. */1697static int1698cert_sig_alg(X509 *cert)1699{1700/* Use X509_get0_pubkey() when OpenSSL 1.0 support is removed. */1701EVP_PKEY *pkey = X509_get_pubkey(cert);1702int id;17031704if (pkey != NULL && EVP_PKEY_get_base_id(pkey) == EVP_PKEY_EC)1705id = NID_ecdsa_with_SHA256;1706else1707id = NID_sha256WithRSAEncryption;1708EVP_PKEY_free(pkey);1709return id;1710}17111712krb5_error_code1713cms_signeddata_create(krb5_context context,1714pkinit_plg_crypto_context plg_cryptoctx,1715pkinit_req_crypto_context req_cryptoctx,1716pkinit_identity_crypto_context id_cryptoctx,1717int cms_msg_type,1718unsigned char *data,1719unsigned int data_len,1720unsigned char **signed_data,1721unsigned int *signed_data_len)1722{1723krb5_error_code retval = ENOMEM;1724PKCS7 *p7 = NULL, *inner_p7 = NULL;1725PKCS7_SIGNED *p7s = NULL;1726PKCS7_SIGNER_INFO *p7si = NULL;1727unsigned char *p;1728STACK_OF(X509) * cert_stack = NULL;1729ASN1_OCTET_STRING *digest_attr = NULL;1730EVP_MD_CTX *ctx;1731unsigned char md_data[EVP_MAX_MD_SIZE], *abuf = NULL;1732unsigned int md_len, alen;1733STACK_OF(X509_ATTRIBUTE) * sk;1734unsigned char *sig = NULL;1735unsigned int sig_len = 0;1736X509_ALGOR *alg = NULL;1737ASN1_OBJECT *oid = NULL, *oid_copy;1738int sig_alg_id;17391740/* Start creating PKCS7 data. */1741if ((p7 = PKCS7_new()) == NULL)1742goto cleanup;1743p7->type = OBJ_nid2obj(NID_pkcs7_signed);17441745if ((p7s = PKCS7_SIGNED_new()) == NULL)1746goto cleanup;1747p7->d.sign = p7s;1748if (!ASN1_INTEGER_set(p7s->version, 3))1749goto cleanup;17501751/* pick the correct oid for the eContentInfo */1752oid = pkinit_pkcs7type2oid(plg_cryptoctx, cms_msg_type);1753if (oid == NULL)1754goto cleanup;17551756if (id_cryptoctx->my_cert != NULL) {1757X509_STORE *certstore = NULL;1758X509_STORE_CTX *certctx;1759STACK_OF(X509) *certstack = NULL;1760char buf[DN_BUF_LEN];1761unsigned int i = 0, size = 0;17621763/* create a cert chain */1764if ((cert_stack = sk_X509_new_null()) == NULL)1765goto cleanup;17661767certstore = X509_STORE_new();1768if (certstore == NULL)1769goto cleanup;1770pkiDebug("building certificate chain\n");1771X509_STORE_set_verify_cb(certstore, openssl_callback);1772certctx = X509_STORE_CTX_new();1773if (certctx == NULL)1774goto cleanup;1775X509_STORE_CTX_init(certctx, certstore, id_cryptoctx->my_cert,1776id_cryptoctx->intermediateCAs);1777X509_STORE_CTX_trusted_stack(certctx, id_cryptoctx->trustedCAs);1778if (!X509_verify_cert(certctx)) {1779retval = oerr_cert(context, 0, certctx,1780_("Failed to verify own certificate"));1781goto cleanup;1782}1783certstack = X509_STORE_CTX_get1_chain(certctx);1784size = sk_X509_num(certstack);1785for (i = 0; i < size - 1; i++) {1786X509 *x = sk_X509_value(certstack, i);1787X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));1788TRACE_PKINIT_CERT_CHAIN_NAME(context, (int)i, buf);1789sk_X509_push(cert_stack, X509_dup(x));1790}1791X509_STORE_CTX_free(certctx);1792X509_STORE_free(certstore);1793sk_X509_pop_free(certstack, X509_free);17941795p7s->cert = cert_stack;17961797/* fill-in PKCS7_SIGNER_INFO */1798if ((p7si = PKCS7_SIGNER_INFO_new()) == NULL)1799goto cleanup;1800if (!ASN1_INTEGER_set(p7si->version, 1))1801goto cleanup;1802if (!X509_NAME_set(&p7si->issuer_and_serial->issuer,1803X509_get_issuer_name(id_cryptoctx->my_cert)))1804goto cleanup;1805/* because ASN1_INTEGER_set is used to set a 'long' we will do1806* things the ugly way. */1807ASN1_INTEGER_free(p7si->issuer_and_serial->serial);1808if (!(p7si->issuer_and_serial->serial =1809ASN1_INTEGER_dup(X509_get_serialNumber(id_cryptoctx->my_cert))))1810goto cleanup;18111812/* will not fill-out EVP_PKEY because it's on the smartcard */18131814/* Set digest algs */1815p7si->digest_alg->algorithm = OBJ_nid2obj(NID_sha256);18161817if (p7si->digest_alg->parameter != NULL)1818ASN1_TYPE_free(p7si->digest_alg->parameter);1819if ((p7si->digest_alg->parameter = ASN1_TYPE_new()) == NULL)1820goto cleanup;1821p7si->digest_alg->parameter->type = V_ASN1_NULL;18221823/* Set sig algs */1824if (p7si->digest_enc_alg->parameter != NULL)1825ASN1_TYPE_free(p7si->digest_enc_alg->parameter);1826sig_alg_id = cert_sig_alg(id_cryptoctx->my_cert);1827p7si->digest_enc_alg->algorithm = OBJ_nid2obj(sig_alg_id);1828if (!(p7si->digest_enc_alg->parameter = ASN1_TYPE_new()))1829goto cleanup;1830p7si->digest_enc_alg->parameter->type = V_ASN1_NULL;18311832/* add signed attributes */1833/* compute sha256 digest over the EncapsulatedContentInfo */1834ctx = EVP_MD_CTX_new();1835if (ctx == NULL)1836goto cleanup;1837EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);1838EVP_DigestUpdate(ctx, data, data_len);1839EVP_DigestFinal_ex(ctx, md_data, &md_len);1840EVP_MD_CTX_free(ctx);18411842/* create a message digest attr */1843digest_attr = ASN1_OCTET_STRING_new();1844ASN1_OCTET_STRING_set(digest_attr, md_data, (int)md_len);1845PKCS7_add_signed_attribute(p7si, NID_pkcs9_messageDigest,1846V_ASN1_OCTET_STRING, (char *)digest_attr);18471848/* create a content-type attr */1849oid_copy = OBJ_dup(oid);1850if (oid_copy == NULL)1851goto cleanup2;1852PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType,1853V_ASN1_OBJECT, oid_copy);18541855/* create the signature over signed attributes. get DER encoded value */1856/* This is the place where smartcard signature needs to be calculated */1857sk = p7si->auth_attr;1858alen = ASN1_item_i2d((ASN1_VALUE *)sk, &abuf,1859ASN1_ITEM_rptr(PKCS7_ATTR_SIGN));1860if (abuf == NULL)1861goto cleanup2;18621863retval = pkinit_sign_data(context, id_cryptoctx, abuf, alen,1864&sig, &sig_len);1865#ifdef DEBUG_SIG1866print_buffer(sig, sig_len);1867#endif1868OPENSSL_free(abuf);1869if (retval)1870goto cleanup2;18711872/* Add signature */1873if (!ASN1_STRING_set(p7si->enc_digest, (unsigned char *) sig,1874(int)sig_len)) {1875retval = oerr(context, 0, _("Failed to add digest attribute"));1876goto cleanup2;1877}1878/* adder signer_info to pkcs7 signed */1879if (!PKCS7_add_signer(p7, p7si))1880goto cleanup2;1881} /* we have a certificate */18821883/* start on adding data to the pkcs7 signed */1884retval = create_contentinfo(context, oid, data, data_len, &inner_p7);1885if (p7s->contents != NULL)1886PKCS7_free(p7s->contents);1887p7s->contents = inner_p7;18881889*signed_data_len = i2d_PKCS7(p7, NULL);1890if (!(*signed_data_len)) {1891retval = oerr(context, 0, _("Failed to DER encode PKCS7"));1892goto cleanup2;1893}1894retval = ENOMEM;1895if ((p = *signed_data = malloc(*signed_data_len)) == NULL)1896goto cleanup2;18971898/* DER encode PKCS7 data */1899retval = i2d_PKCS7(p7, &p);1900if (!retval) {1901retval = oerr(context, 0, _("Failed to DER encode PKCS7"));1902goto cleanup2;1903}1904retval = 0;19051906#ifdef DEBUG_ASN11907if (cms_msg_type == CMS_SIGN_CLIENT) {1908print_buffer_bin(*signed_data, *signed_data_len,1909"/tmp/client_pkcs7_signeddata");1910} else {1911print_buffer_bin(*signed_data, *signed_data_len,1912"/tmp/kdc_pkcs7_signeddata");1913}1914#endif19151916cleanup2:1917if (p7si) {1918if (alg != NULL)1919X509_ALGOR_free(alg);1920}1921cleanup:1922if (p7 != NULL)1923PKCS7_free(p7);1924free(sig);19251926return retval;1927}19281929krb5_error_code1930cms_signeddata_verify(krb5_context context,1931pkinit_plg_crypto_context plgctx,1932pkinit_req_crypto_context reqctx,1933pkinit_identity_crypto_context idctx,1934int cms_msg_type,1935int require_crl_checking,1936unsigned char *signed_data,1937unsigned int signed_data_len,1938unsigned char **data,1939unsigned int *data_len,1940unsigned char **authz_data,1941unsigned int *authz_data_len,1942int *is_signed)1943{1944/*1945* Warning: Since most openssl functions do not set retval, large chunks of1946* this function assume that retval is always a failure and may go to1947* cleanup without setting retval explicitly. Make sure retval is not set1948* to 0 or errors such as signature verification failure may be converted1949* to success with significant security consequences.1950*/1951krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;1952CMS_ContentInfo *cms = NULL;1953BIO *out = NULL;1954int flags = CMS_NO_SIGNER_CERT_VERIFY;1955int valid_oid = 0;1956unsigned int i = 0;1957unsigned int vflags = 0, size = 0;1958const unsigned char *p = signed_data;1959STACK_OF(CMS_SignerInfo) *si_sk = NULL;1960CMS_SignerInfo *si = NULL;1961X509 *x = NULL;1962X509_STORE *store = NULL;1963X509_STORE_CTX *cert_ctx;1964STACK_OF(X509) *signerCerts = NULL;1965STACK_OF(X509) *intermediateCAs = NULL;1966STACK_OF(X509_CRL) *signerRevoked = NULL;1967STACK_OF(X509_CRL) *revoked = NULL;1968STACK_OF(X509) *verified_chain = NULL;1969ASN1_OBJECT *oid = NULL;1970const ASN1_OBJECT *type = NULL, *etype = NULL;1971ASN1_OCTET_STRING **octets;1972krb5_external_principal_identifier **krb5_verified_chain = NULL;1973krb5_data *authz = NULL;1974char buf[DN_BUF_LEN];19751976#ifdef DEBUG_ASN11977print_buffer_bin(signed_data, signed_data_len,1978"/tmp/client_received_pkcs7_signeddata");1979#endif1980if (is_signed)1981*is_signed = 1;19821983oid = pkinit_pkcs7type2oid(plgctx, cms_msg_type);1984if (oid == NULL)1985goto cleanup;19861987/* decode received CMS message */1988if ((cms = d2i_CMS_ContentInfo(NULL, &p, (int)signed_data_len)) == NULL) {1989retval = oerr(context, 0, _("Failed to decode CMS message"));1990goto cleanup;1991}1992etype = CMS_get0_eContentType(cms);19931994/*1995* Prior to 1.10 the MIT client incorrectly emitted the pkinit structure1996* directly in a CMS ContentInfo rather than using SignedData with no1997* signers. Handle that case.1998*/1999type = CMS_get0_type(cms);2000if (is_signed && !OBJ_cmp(type, oid)) {2001unsigned char *d;2002*is_signed = 0;2003octets = CMS_get0_content(cms);2004if (!octets || ((*octets)->type != V_ASN1_OCTET_STRING)) {2005retval = KRB5KDC_ERR_PREAUTH_FAILED;2006krb5_set_error_message(context, retval,2007_("Invalid pkinit packet: octet string "2008"expected"));2009goto cleanup;2010}2011*data_len = ASN1_STRING_length(*octets);2012d = malloc(*data_len);2013if (d == NULL) {2014retval = ENOMEM;2015goto cleanup;2016}2017memcpy(d, ASN1_STRING_get0_data(*octets), *data_len);2018*data = d;2019goto out;2020} else {2021/* Verify that the received message is CMS SignedData message. */2022if (OBJ_obj2nid(type) != NID_pkcs7_signed) {2023pkiDebug("Expected id-signedData CMS msg (received type = %d)\n",2024OBJ_obj2nid(type));2025krb5_set_error_message(context, retval, _("wrong oid\n"));2026goto cleanup;2027}2028}20292030/* setup to verify X509 certificate used to sign CMS message */2031if (!(store = X509_STORE_new()))2032goto cleanup;20332034/* check if we are inforcing CRL checking */2035vflags = X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL;2036if (require_crl_checking)2037X509_STORE_set_verify_cb(store, openssl_callback);2038else2039X509_STORE_set_verify_cb(store, openssl_callback_ignore_crls);2040X509_STORE_set_flags(store, vflags);20412042/*2043* Get the signer's information from the CMS message. Match signer ID2044* against anchors and intermediate CAs in case no certs are present in the2045* SignedData. If we start sending kdcPkId values in requests, we'll need2046* to match against the source of that information too.2047*/2048CMS_set1_signers_certs(cms, NULL, 0);2049CMS_set1_signers_certs(cms, idctx->trustedCAs, CMS_NOINTERN);2050CMS_set1_signers_certs(cms, idctx->intermediateCAs, CMS_NOINTERN);2051if (((si_sk = CMS_get0_SignerInfos(cms)) == NULL) ||2052((si = sk_CMS_SignerInfo_value(si_sk, 0)) == NULL)) {2053/* Not actually signed; anonymous case */2054if (!is_signed)2055goto cleanup;2056*is_signed = 0;2057/* We cannot use CMS_dataInit because there may be no digest */2058octets = CMS_get0_content(cms);2059if (octets)2060out = BIO_new_mem_buf((*octets)->data, (*octets)->length);2061if (out == NULL)2062goto cleanup;2063} else {2064CMS_SignerInfo_get0_algs(si, NULL, &x, NULL, NULL);2065if (x == NULL)2066goto cleanup;20672068/* create available CRL information (get local CRLs and include CRLs2069* received in the CMS message2070*/2071signerRevoked = CMS_get1_crls(cms);2072if (idctx->revoked == NULL)2073revoked = signerRevoked;2074else if (signerRevoked == NULL)2075revoked = idctx->revoked;2076else {2077size = sk_X509_CRL_num(idctx->revoked);2078revoked = sk_X509_CRL_new_null();2079for (i = 0; i < size; i++)2080sk_X509_CRL_push(revoked, sk_X509_CRL_value(idctx->revoked, i));2081size = sk_X509_CRL_num(signerRevoked);2082for (i = 0; i < size; i++)2083sk_X509_CRL_push(revoked, sk_X509_CRL_value(signerRevoked, i));2084}20852086/* create available intermediate CAs chains (get local intermediateCAs and2087* include the CA chain received in the CMS message2088*/2089signerCerts = CMS_get1_certs(cms);2090if (idctx->intermediateCAs == NULL)2091intermediateCAs = signerCerts;2092else if (signerCerts == NULL)2093intermediateCAs = idctx->intermediateCAs;2094else {2095size = sk_X509_num(idctx->intermediateCAs);2096intermediateCAs = sk_X509_new_null();2097for (i = 0; i < size; i++) {2098sk_X509_push(intermediateCAs,2099sk_X509_value(idctx->intermediateCAs, i));2100}2101size = sk_X509_num(signerCerts);2102for (i = 0; i < size; i++) {2103sk_X509_push(intermediateCAs, sk_X509_value(signerCerts, i));2104}2105}21062107/* initialize x509 context with the received certificate and2108* trusted and intermediate CA chains and CRLs2109*/2110cert_ctx = X509_STORE_CTX_new();2111if (cert_ctx == NULL)2112goto cleanup;2113if (!X509_STORE_CTX_init(cert_ctx, store, x, intermediateCAs))2114goto cleanup;21152116X509_STORE_CTX_set0_crls(cert_ctx, revoked);21172118/* add trusted CAs certificates for cert verification */2119if (idctx->trustedCAs != NULL)2120X509_STORE_CTX_trusted_stack(cert_ctx, idctx->trustedCAs);2121else {2122pkiDebug("unable to find any trusted CAs\n");2123goto cleanup;2124}2125#ifdef DEBUG_CERTCHAIN2126if (intermediateCAs != NULL) {2127size = sk_X509_num(intermediateCAs);2128pkiDebug("untrusted cert chain of size %d\n", size);2129for (i = 0; i < size; i++) {2130X509_NAME_oneline(X509_get_subject_name(2131sk_X509_value(intermediateCAs, i)), buf, sizeof(buf));2132pkiDebug("cert #%d: %s\n", i, buf);2133}2134}2135if (idctx->trustedCAs != NULL) {2136size = sk_X509_num(idctx->trustedCAs);2137pkiDebug("trusted cert chain of size %d\n", size);2138for (i = 0; i < size; i++) {2139X509_NAME_oneline(X509_get_subject_name(2140sk_X509_value(idctx->trustedCAs, i)), buf, sizeof(buf));2141pkiDebug("cert #%d: %s\n", i, buf);2142}2143}2144if (revoked != NULL) {2145size = sk_X509_CRL_num(revoked);2146pkiDebug("CRL chain of size %d\n", size);2147for (i = 0; i < size; i++) {2148X509_CRL *crl = sk_X509_CRL_value(revoked, i);2149X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf));2150pkiDebug("crls by CA #%d: %s\n", i , buf);2151}2152}2153#endif21542155i = X509_verify_cert(cert_ctx);2156if (i <= 0) {2157int j = X509_STORE_CTX_get_error(cert_ctx);2158X509 *cert;21592160cert = X509_STORE_CTX_get_current_cert(cert_ctx);2161reqctx->received_cert = X509_dup(cert);2162switch(j) {2163case X509_V_ERR_CERT_REVOKED:2164retval = KRB5KDC_ERR_REVOKED_CERTIFICATE;2165break;2166case X509_V_ERR_UNABLE_TO_GET_CRL:2167retval = KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN;2168break;2169case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:2170case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:2171retval = KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE;2172break;2173default:2174retval = KRB5KDC_ERR_INVALID_CERTIFICATE;2175}2176(void)oerr_cert(context, retval, cert_ctx,2177_("Failed to verify received certificate"));2178if (reqctx->received_cert == NULL)2179strlcpy(buf, "(none)", sizeof(buf));2180else2181X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),2182buf, sizeof(buf));2183pkiDebug("problem with cert DN = %s (error=%d) %s\n", buf, j,2184X509_verify_cert_error_string(j));2185#ifdef DEBUG_CERTCHAIN2186size = sk_X509_num(signerCerts);2187pkiDebug("received cert chain of size %d\n", size);2188for (j = 0; j < size; j++) {2189X509 *tmp_cert = sk_X509_value(signerCerts, j);2190X509_NAME_oneline(X509_get_subject_name(tmp_cert), buf, sizeof(buf));2191pkiDebug("cert #%d: %s\n", j, buf);2192}2193#endif2194} else {2195/* retrieve verified certificate chain */2196if (cms_msg_type == CMS_SIGN_CLIENT)2197verified_chain = X509_STORE_CTX_get1_chain(cert_ctx);2198}2199X509_STORE_CTX_free(cert_ctx);2200if (i <= 0)2201goto cleanup;2202out = BIO_new(BIO_s_mem());2203if (CMS_verify(cms, NULL, store, NULL, out, flags) == 0) {2204if (ERR_peek_last_error() == CMS_R_VERIFICATION_FAILURE)2205retval = KRB5KDC_ERR_INVALID_SIG;2206else2207retval = KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED;2208(void)oerr(context, retval, _("Failed to verify CMS message"));2209goto cleanup;2210}2211} /* message was signed */2212if (!OBJ_cmp(etype, oid))2213valid_oid = 1;22142215if (valid_oid)2216pkiDebug("CMS Verification successful\n");2217else {2218pkiDebug("wrong oid in eContentType\n");2219print_buffer(OBJ_get0_data(etype), OBJ_length(etype));2220retval = KRB5KDC_ERR_PREAUTH_FAILED;2221krb5_set_error_message(context, retval, "wrong oid\n");2222goto cleanup;2223}22242225/* transfer the data from CMS message into return buffer */2226for (size = 0;;) {2227int remain;2228retval = ENOMEM;2229if ((*data = realloc(*data, size + 1024 * 10)) == NULL)2230goto cleanup;2231remain = BIO_read(out, &((*data)[size]), 1024 * 10);2232if (remain <= 0)2233break;2234else2235size += remain;2236}2237*data_len = size;22382239if (x) {2240reqctx->received_cert = X509_dup(x);22412242/* generate authorization data */2243if (cms_msg_type == CMS_SIGN_CLIENT) {22442245if (authz_data == NULL || authz_data_len == NULL)2246goto out;22472248*authz_data = NULL;2249retval = create_identifiers_from_stack(verified_chain,2250&krb5_verified_chain);2251if (retval) {2252pkiDebug("create_identifiers_from_stack failed\n");2253goto cleanup;2254}22552256retval = k5int_encode_krb5_td_trusted_certifiers((krb5_external_principal_identifier *const *)krb5_verified_chain, &authz);2257if (retval) {2258pkiDebug("encode_krb5_td_trusted_certifiers failed\n");2259goto cleanup;2260}2261#ifdef DEBUG_ASN12262print_buffer_bin((unsigned char *)authz->data, authz->length,2263"/tmp/kdc_ad_initial_verified_cas");2264#endif2265*authz_data = malloc(authz->length);2266if (*authz_data == NULL) {2267retval = ENOMEM;2268goto cleanup;2269}2270memcpy(*authz_data, authz->data, authz->length);2271*authz_data_len = authz->length;2272}2273}2274out:2275retval = 0;22762277cleanup:2278if (out != NULL)2279BIO_free(out);2280if (store != NULL)2281X509_STORE_free(store);2282if (cms != NULL) {2283if (signerCerts != NULL)2284sk_X509_pop_free(signerCerts, X509_free);2285if (idctx->intermediateCAs != NULL && signerCerts)2286sk_X509_free(intermediateCAs);2287if (signerRevoked != NULL)2288sk_X509_CRL_pop_free(signerRevoked, X509_CRL_free);2289if (idctx->revoked != NULL && signerRevoked)2290sk_X509_CRL_free(revoked);2291CMS_ContentInfo_free(cms);2292}2293if (verified_chain != NULL)2294sk_X509_pop_free(verified_chain, X509_free);2295if (krb5_verified_chain != NULL)2296free_krb5_external_principal_identifier(&krb5_verified_chain);2297if (authz != NULL)2298krb5_free_data(context, authz);22992300return retval;2301}23022303static krb5_error_code2304crypto_retrieve_X509_sans(krb5_context context,2305pkinit_plg_crypto_context plgctx,2306pkinit_req_crypto_context reqctx,2307X509 *cert,2308krb5_principal **princs_ret, char ***upn_ret,2309unsigned char ***dns_ret)2310{2311krb5_error_code retval = EINVAL;2312char buf[DN_BUF_LEN];2313size_t num_sans = 0, p = 0, u = 0, d = 0, i;2314int l;2315krb5_principal *princs = NULL;2316char **upns = NULL;2317unsigned char **dnss = NULL;2318X509_EXTENSION *ext = NULL;2319GENERAL_NAMES *ialt = NULL;2320GENERAL_NAME *gen = NULL;23212322if (princs_ret != NULL)2323*princs_ret = NULL;2324if (upn_ret != NULL)2325*upn_ret = NULL;2326if (dns_ret != NULL)2327*dns_ret = NULL;23282329if (princs_ret == NULL && upn_ret == NULL && dns_ret == NULL) {2330pkiDebug("%s: nowhere to return any values!\n", __FUNCTION__);2331return retval;2332}23332334if (cert == NULL) {2335pkiDebug("%s: no certificate!\n", __FUNCTION__);2336return retval;2337}23382339X509_NAME_oneline(X509_get_subject_name(cert),2340buf, sizeof(buf));23412342l = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);2343if (l < 0)2344return 0;23452346if (!(ext = X509_get_ext(cert, l)) || !(ialt = X509V3_EXT_d2i(ext))) {2347TRACE_PKINIT_SAN_CERT_NONE(context, buf);2348goto cleanup;2349}2350num_sans = sk_GENERAL_NAME_num(ialt);23512352/* OK, we're likely returning something. Allocate return values */2353if (princs_ret != NULL) {2354princs = calloc(num_sans + 1, sizeof(krb5_principal));2355if (princs == NULL) {2356retval = ENOMEM;2357goto cleanup;2358}2359}2360if (upn_ret != NULL) {2361upns = calloc(num_sans + 1, sizeof(*upns));2362if (upns == NULL) {2363retval = ENOMEM;2364goto cleanup;2365}2366}2367if (dns_ret != NULL) {2368dnss = calloc(num_sans + 1, sizeof(*dnss));2369if (dnss == NULL) {2370retval = ENOMEM;2371goto cleanup;2372}2373}23742375for (i = 0; i < num_sans; i++) {2376krb5_data name = { 0, 0, NULL };23772378gen = sk_GENERAL_NAME_value(ialt, i);2379switch (gen->type) {2380case GEN_OTHERNAME:2381name.length = gen->d.otherName->value->value.sequence->length;2382name.data = (char *)gen->d.otherName->value->value.sequence->data;2383if (princs != NULL &&2384OBJ_cmp(plgctx->id_pkinit_san,2385gen->d.otherName->type_id) == 0) {2386#ifdef DEBUG_ASN12387print_buffer_bin((unsigned char *)name.data, name.length,2388"/tmp/pkinit_san");2389#endif2390if (k5int_decode_krb5_principal_name(&name, &princs[p]) != 0) {2391pkiDebug("%s: failed decoding pkinit san value\n",2392__FUNCTION__);2393} else {2394p++;2395}2396} else if (upns != NULL &&2397OBJ_cmp(plgctx->id_ms_san_upn,2398gen->d.otherName->type_id) == 0) {2399/* Prevent abuse of embedded null characters. */2400if (memchr(name.data, '\0', name.length))2401break;2402upns[u] = k5memdup0(name.data, name.length, &retval);2403if (upns[u] == NULL)2404goto cleanup;2405u++;2406} else {2407pkiDebug("%s: unrecognized othername oid in SAN\n",2408__FUNCTION__);2409continue;2410}24112412break;2413case GEN_DNS:2414if (dnss != NULL) {2415/* Prevent abuse of embedded null characters. */2416if (memchr(gen->d.dNSName->data, '\0', gen->d.dNSName->length))2417break;2418pkiDebug("%s: found dns name = %s\n", __FUNCTION__,2419gen->d.dNSName->data);2420dnss[d] = (unsigned char *)2421strdup((char *)gen->d.dNSName->data);2422if (dnss[d] == NULL) {2423pkiDebug("%s: failed to duplicate dns name\n",2424__FUNCTION__);2425} else {2426d++;2427}2428}2429break;2430default:2431pkiDebug("%s: SAN type = %d expecting %d\n", __FUNCTION__,2432gen->type, GEN_OTHERNAME);2433}2434}2435sk_GENERAL_NAME_pop_free(ialt, GENERAL_NAME_free);24362437TRACE_PKINIT_SAN_CERT_COUNT(context, (int)num_sans, p, u, d, buf);24382439retval = 0;2440if (princs != NULL && *princs != NULL) {2441*princs_ret = princs;2442princs = NULL;2443}2444if (upns != NULL && *upns != NULL) {2445*upn_ret = upns;2446upns = NULL;2447}2448if (dnss != NULL && *dnss != NULL) {2449*dns_ret = dnss;2450dnss = NULL;2451}24522453cleanup:2454for (i = 0; princs != NULL && princs[i] != NULL; i++)2455krb5_free_principal(context, princs[i]);2456free(princs);2457for (i = 0; upns != NULL && upns[i] != NULL; i++)2458free(upns[i]);2459free(upns);2460for (i = 0; dnss != NULL && dnss[i] != NULL; i++)2461free(dnss[i]);2462free(dnss);2463return retval;2464}24652466krb5_error_code2467crypto_retrieve_signer_identity(krb5_context context,2468pkinit_identity_crypto_context id_cryptoctx,2469const char **identity)2470{2471*identity = id_cryptoctx->identity;2472if (*identity == NULL)2473return ENOENT;2474return 0;2475}24762477krb5_error_code2478crypto_retrieve_cert_sans(krb5_context context,2479pkinit_plg_crypto_context plgctx,2480pkinit_req_crypto_context reqctx,2481pkinit_identity_crypto_context idctx,2482krb5_principal **princs_ret, char ***upn_ret,2483unsigned char ***dns_ret)2484{2485krb5_error_code retval = EINVAL;24862487if (reqctx->received_cert == NULL) {2488pkiDebug("%s: No certificate!\n", __FUNCTION__);2489return retval;2490}24912492return crypto_retrieve_X509_sans(context, plgctx, reqctx,2493reqctx->received_cert, princs_ret,2494upn_ret, dns_ret);2495}24962497krb5_error_code2498crypto_check_cert_eku(krb5_context context,2499pkinit_plg_crypto_context plgctx,2500pkinit_req_crypto_context reqctx,2501pkinit_identity_crypto_context idctx,2502int checking_kdc_cert,2503int allow_secondary_usage,2504int *valid_eku)2505{2506char buf[DN_BUF_LEN];2507int found_eku = 0;2508krb5_error_code retval = EINVAL;2509int i;25102511*valid_eku = 0;2512if (reqctx->received_cert == NULL)2513goto cleanup;25142515X509_NAME_oneline(X509_get_subject_name(reqctx->received_cert),2516buf, sizeof(buf));25172518if ((i = X509_get_ext_by_NID(reqctx->received_cert,2519NID_ext_key_usage, -1)) >= 0) {2520EXTENDED_KEY_USAGE *extusage;25212522extusage = X509_get_ext_d2i(reqctx->received_cert, NID_ext_key_usage,2523NULL, NULL);2524if (extusage) {2525pkiDebug("%s: found eku info in the cert\n", __FUNCTION__);2526for (i = 0; found_eku == 0 && i < sk_ASN1_OBJECT_num(extusage); i++) {2527ASN1_OBJECT *tmp_oid;25282529tmp_oid = sk_ASN1_OBJECT_value(extusage, i);2530pkiDebug("%s: checking eku %d of %d, allow_secondary = %d\n",2531__FUNCTION__, i+1, sk_ASN1_OBJECT_num(extusage),2532allow_secondary_usage);2533if (checking_kdc_cert) {2534if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPKdc) == 0)2535|| (allow_secondary_usage2536&& OBJ_cmp(tmp_oid, plgctx->id_kp_serverAuth) == 0))2537found_eku = 1;2538} else {2539if ((OBJ_cmp(tmp_oid, plgctx->id_pkinit_KPClientAuth) == 0)2540|| (allow_secondary_usage2541&& OBJ_cmp(tmp_oid, plgctx->id_ms_kp_sc_logon) == 0))2542found_eku = 1;2543}2544}2545}2546EXTENDED_KEY_USAGE_free(extusage);25472548if (found_eku) {2549ASN1_BIT_STRING *usage = NULL;25502551/* check that digitalSignature KeyUsage is present */2552X509_check_ca(reqctx->received_cert);2553if ((usage = X509_get_ext_d2i(reqctx->received_cert,2554NID_key_usage, NULL, NULL))) {25552556if (!ku_reject(reqctx->received_cert,2557X509v3_KU_DIGITAL_SIGNATURE)) {2558TRACE_PKINIT_EKU(context);2559*valid_eku = 1;2560} else2561TRACE_PKINIT_EKU_NO_KU(context);2562}2563ASN1_BIT_STRING_free(usage);2564}2565}2566retval = 0;2567cleanup:2568pkiDebug("%s: returning retval %d, valid_eku %d\n",2569__FUNCTION__, retval, *valid_eku);2570return retval;2571}25722573static krb5_error_code2574octetstring2key(krb5_context context, krb5_enctype etype,2575const krb5_data *secret, krb5_keyblock *key_block)2576{2577krb5_error_code retval;2578unsigned char *buf = NULL;2579unsigned char md[SHA_DIGEST_LENGTH];2580unsigned char counter;2581size_t keybytes, keylength, offset;2582krb5_data random_data;2583EVP_MD_CTX *sha1_ctx = NULL;25842585buf = k5alloc(secret->length, &retval);2586if (buf == NULL)2587goto cleanup;25882589sha1_ctx = EVP_MD_CTX_new();2590if (sha1_ctx == NULL) {2591retval = KRB5_CRYPTO_INTERNAL;2592goto cleanup;2593}25942595counter = 0;2596offset = 0;2597do {2598if (!EVP_DigestInit(sha1_ctx, EVP_sha1()) ||2599!EVP_DigestUpdate(sha1_ctx, &counter, 1) ||2600!EVP_DigestUpdate(sha1_ctx, secret->data, secret->length) ||2601!EVP_DigestFinal(sha1_ctx, md, NULL)) {2602retval = KRB5_CRYPTO_INTERNAL;2603goto cleanup;2604}26052606if (secret->length - offset < sizeof(md))2607memcpy(buf + offset, md, secret->length - offset);2608else2609memcpy(buf + offset, md, sizeof(md));26102611offset += sizeof(md);2612counter++;2613} while (offset < secret->length);26142615key_block->magic = 0;2616key_block->enctype = etype;26172618retval = krb5_c_keylengths(context, etype, &keybytes, &keylength);2619if (retval)2620goto cleanup;26212622key_block->length = keylength;2623key_block->contents = k5alloc(keylength, &retval);2624if (key_block->contents == NULL)2625goto cleanup;26262627random_data.length = keybytes;2628random_data.data = (char *)buf;26292630retval = krb5_c_random_to_key(context, etype, &random_data, key_block);2631if (retval)2632goto cleanup;26332634TRACE_PKINIT_KDF_OS2K(context, key_block);26352636cleanup:2637EVP_MD_CTX_free(sha1_ctx);2638free(buf);2639/* If this is an error return, free the allocated keyblock, if any */2640if (retval) {2641krb5_free_keyblock_contents(context, key_block);2642}26432644return retval;2645}264626472648/* Return the OpenSSL descriptor for the given RFC 5652 OID specified in RFC2649* 8636. RFC 8636 defines a SHA384 variant, but we don't use it. */2650static const EVP_MD *2651algid_to_md(const krb5_data *alg_id)2652{2653if (data_eq(*alg_id, kdf_sha1_id))2654return EVP_sha1();2655if (data_eq(*alg_id, kdf_sha256_id))2656return EVP_sha256();2657if (data_eq(*alg_id, kdf_sha512_id))2658return EVP_sha512();2659return NULL;2660}26612662#if OPENSSL_VERSION_NUMBER >= 0x30000000L26632664#define sskdf openssl_sskdf2665static krb5_error_code2666openssl_sskdf(krb5_context context, const EVP_MD *md, const krb5_data *secret,2667const krb5_data *info, size_t len, krb5_data *out)2668{2669krb5_error_code ret;2670EVP_KDF *kdf = NULL;2671EVP_KDF_CTX *kctx = NULL;2672OSSL_PARAM params[4], *p = params;26732674ret = alloc_data(out, len);2675if (ret)2676goto cleanup;26772678kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL);2679if (kdf == NULL) {2680ret = oerr(context, KRB5_CRYPTO_INTERNAL, _("Failed to fetch SSKDF"));2681goto cleanup;2682}26832684kctx = EVP_KDF_CTX_new(kdf);2685if (!kctx) {2686ret = oerr(context, KRB5_CRYPTO_INTERNAL,2687_("Failed to instantiate SSKDF"));2688goto cleanup;2689}26902691*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,2692(char *)EVP_MD_get0_name(md), 0);2693*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,2694secret->data, secret->length);2695*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,2696info->data, info->length);2697*p = OSSL_PARAM_construct_end();2698if (EVP_KDF_derive(kctx, (uint8_t *)out->data, len, params) <= 0) {2699ret = oerr(context, KRB5_CRYPTO_INTERNAL,2700_("Failed to derive key using SSKDF"));2701goto cleanup;2702}27032704ret = 0;27052706cleanup:2707EVP_KDF_free(kdf);2708EVP_KDF_CTX_free(kctx);2709return ret;2710}27112712#else /* OPENSSL_VERSION_NUMBER < 0x30000000L */27132714#define sskdf builtin_sskdf2715static krb5_error_code2716builtin_sskdf(krb5_context context, const EVP_MD *md, const krb5_data *secret,2717const krb5_data *info, size_t len, krb5_data *out)2718{2719krb5_error_code ret;2720uint32_t counter = 1, reps;2721uint8_t be_counter[4], *outptr;2722EVP_MD_CTX *ctx = NULL;2723unsigned int s, hash_len;27242725hash_len = EVP_MD_size(md);27262727/* 1. reps = keydatalen (K) / hash length (H) rounded up. */2728reps = (len + hash_len - 1) / hash_len;27292730/* Allocate enough space in the random data buffer to hash directly into2731* it, even if the last hash will make it bigger than the key length. */2732ret = alloc_data(out, reps * hash_len);2733if (ret)2734goto cleanup;2735out->length = len;27362737/*2738* 2. Initialize a 32-bit, big-endian bit string counter as 1.2739* 3. For i = 1 to reps by 1, do the following:2740* - Compute Hashi = H(counter || Z || OtherInfo).2741* - Increment counter (modulo 2^32)2742* 4. Set key = Hash1 || Hash2 || ... so that length of key is K2743* bytes.2744*/2745outptr = (uint8_t *)out->data;2746for (counter = 1; counter <= reps; counter++) {2747store_32_be(counter, be_counter);27482749ctx = EVP_MD_CTX_new();2750if (ctx == NULL) {2751ret = KRB5_CRYPTO_INTERNAL;2752goto cleanup;2753}27542755/* - Compute Hashi = H(counter || Z || OtherInfo). */2756if (!EVP_DigestInit(ctx, md) ||2757!EVP_DigestUpdate(ctx, be_counter, 4) ||2758!EVP_DigestUpdate(ctx, secret->data, secret->length) ||2759!EVP_DigestUpdate(ctx, info->data, info->length) ||2760!EVP_DigestFinal(ctx, outptr, &s)) {2761ret = oerr(context, KRB5_CRYPTO_INTERNAL,2762_("Failed to compute digest"));2763goto cleanup;2764}27652766assert(s == hash_len);2767outptr += s;27682769EVP_MD_CTX_free(ctx);2770ctx = NULL;2771}27722773cleanup:2774EVP_MD_CTX_free(ctx);2775return ret;2776}27772778#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */27792780/* id-pkinit-kdf family, as specified by RFC 8636. If alg_oid is null,2781* octet2string(), as specified by RFC 4556. */2782krb5_error_code2783pkinit_kdf(krb5_context context, krb5_data *secret, const krb5_data *alg_oid,2784krb5_const_principal party_u_info,2785krb5_const_principal party_v_info, krb5_enctype enctype,2786const krb5_data *as_req, const krb5_data *pk_as_rep,2787krb5_keyblock *key_block)2788{2789krb5_error_code ret;2790size_t rand_len = 0, key_len = 0;2791const EVP_MD *md;2792krb5_sp80056a_other_info other_info_fields;2793krb5_pkinit_supp_pub_info supp_pub_info_fields;2794krb5_data *other_info = NULL, *supp_pub_info = NULL;2795krb5_data random_data = empty_data();2796krb5_algorithm_identifier alg_id;2797char *hash_name = NULL;27982799if (alg_oid == NULL)2800return octetstring2key(context, enctype, secret, key_block);28012802ret = krb5_c_keylengths(context, enctype, &rand_len, &key_len);2803if (ret)2804goto cleanup;28052806/* Allocate and initialize the key block. */2807key_block->magic = 0;2808key_block->enctype = enctype;2809key_block->length = key_len;2810key_block->contents = k5calloc(key_block->length, 1, &ret);2811if (key_block->contents == NULL)2812goto cleanup;28132814/* If this is anonymous pkinit, use the anonymous principle for2815* party_u_info. */2816if (party_u_info &&2817krb5_principal_compare_any_realm(context, party_u_info,2818krb5_anonymous_principal())) {2819party_u_info = krb5_anonymous_principal();2820}28212822md = algid_to_md(alg_oid);2823if (md == NULL) {2824krb5_set_error_message(context, KRB5_ERR_BAD_S2K_PARAMS,2825"Bad algorithm ID passed to PK-INIT KDF.");2826return KRB5_ERR_BAD_S2K_PARAMS;2827}28282829/* Encode the ASN.1 octet string for "SuppPubInfo". */2830supp_pub_info_fields.enctype = enctype;2831supp_pub_info_fields.as_req = *as_req;2832supp_pub_info_fields.pk_as_rep = *pk_as_rep;2833ret = encode_krb5_pkinit_supp_pub_info(&supp_pub_info_fields,2834&supp_pub_info);2835if (ret)2836goto cleanup;28372838/* Now encode the ASN.1 octet string for "OtherInfo". */2839memset(&alg_id, 0, sizeof(alg_id));2840alg_id.algorithm = *alg_oid;2841other_info_fields.algorithm_identifier = alg_id;2842other_info_fields.party_u_info = (krb5_principal)party_u_info;2843other_info_fields.party_v_info = (krb5_principal)party_v_info;2844other_info_fields.supp_pub_info = *supp_pub_info;2845ret = encode_krb5_sp80056a_other_info(&other_info_fields, &other_info);2846if (ret)2847goto cleanup;28482849ret = sskdf(context, md, secret, other_info, rand_len, &random_data);2850if (ret)2851goto cleanup;28522853ret = krb5_c_random_to_key(context, enctype, &random_data, key_block);2854if (ret)2855goto cleanup;28562857TRACE_PKINIT_KDF_ALG(context, alg_oid, key_block);28582859cleanup:2860if (ret)2861krb5_free_keyblock_contents(context, key_block);2862free(hash_name);2863zapfree(random_data.data, random_data.length);2864krb5_free_data(context, other_info);2865krb5_free_data(context, supp_pub_info);2866return ret;2867}28682869/* Return the equivalent finite-field bit strength of pkey if it matches a2870* well-known group, or -1 if it doesn't. */2871static int2872check_dh_wellknown(pkinit_plg_crypto_context cryptoctx, EVP_PKEY *pkey)2873{2874int nbits = EVP_PKEY_get_bits(pkey);28752876if (nbits == 1024 && EVP_PKEY_parameters_eq(cryptoctx->dh_1024, pkey) == 1)2877return nbits;2878if (nbits == 2048 && EVP_PKEY_parameters_eq(cryptoctx->dh_2048, pkey) == 1)2879return nbits;2880if (nbits == 4096 && EVP_PKEY_parameters_eq(cryptoctx->dh_4096, pkey) == 1)2881return nbits;2882if (nbits == 256 && EVP_PKEY_parameters_eq(cryptoctx->ec_p256, pkey) == 1)2883return PKINIT_DH_P256_BITS;2884if (nbits == 384 && EVP_PKEY_parameters_eq(cryptoctx->ec_p384, pkey) == 1)2885return PKINIT_DH_P384_BITS;2886if (nbits == 521 && EVP_PKEY_parameters_eq(cryptoctx->ec_p521, pkey) == 1)2887return PKINIT_DH_P521_BITS;2888return -1;2889}28902891/* Return a short description of the Diffie-Hellman group with the given2892* finite-field group size equivalent. */2893static const char *2894group_desc(int dh_bits)2895{2896switch (dh_bits) {2897case PKINIT_DH_P256_BITS: return "P-256";2898case PKINIT_DH_P384_BITS: return "P-384";2899case PKINIT_DH_P521_BITS: return "P-521";2900case 1024: return "1024-bit DH";2901case 2048: return "2048-bit DH";2902case 4096: return "4096-bit DH";2903}2904return "(unknown)";2905}29062907static EVP_PKEY *2908choose_dh_group(pkinit_plg_crypto_context plg_cryptoctx, int dh_size)2909{2910if (dh_size == 1024)2911return plg_cryptoctx->dh_1024;2912if (dh_size == 2048)2913return plg_cryptoctx->dh_2048;2914if (dh_size == 4096)2915return plg_cryptoctx->dh_4096;2916if (dh_size == PKINIT_DH_P256_BITS)2917return plg_cryptoctx->ec_p256;2918if (dh_size == PKINIT_DH_P384_BITS)2919return plg_cryptoctx->ec_p384;2920if (dh_size == PKINIT_DH_P521_BITS)2921return plg_cryptoctx->ec_p521;2922return NULL;2923}29242925krb5_error_code2926client_create_dh(krb5_context context,2927pkinit_plg_crypto_context plg_cryptoctx,2928pkinit_req_crypto_context cryptoctx,2929pkinit_identity_crypto_context id_cryptoctx,2930int dh_size, krb5_data *spki_out)2931{2932krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;2933EVP_PKEY *params = NULL, *pkey = NULL;29342935*spki_out = empty_data();29362937params = choose_dh_group(plg_cryptoctx, dh_size);2938if (params == NULL)2939goto cleanup;2940TRACE_PKINIT_DH_PROPOSING_GROUP(context, group_desc(dh_size));29412942pkey = generate_dh_pkey(params);2943if (pkey == NULL)2944goto cleanup;29452946retval = encode_spki(pkey, spki_out);2947if (retval)2948goto cleanup;29492950EVP_PKEY_free(cryptoctx->client_pkey);2951cryptoctx->client_pkey = pkey;2952pkey = NULL;29532954cleanup:2955EVP_PKEY_free(pkey);2956return retval;2957}29582959krb5_error_code2960client_process_dh(krb5_context context,2961pkinit_plg_crypto_context plg_cryptoctx,2962pkinit_req_crypto_context cryptoctx,2963pkinit_identity_crypto_context id_cryptoctx,2964unsigned char *subjectPublicKey_data,2965unsigned int subjectPublicKey_length,2966unsigned char **client_key_out,2967unsigned int *client_key_len_out)2968{2969krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;2970EVP_PKEY *server_pkey = NULL;2971uint8_t *client_key = NULL;2972unsigned int client_key_len;29732974*client_key_out = NULL;2975*client_key_len_out = 0;29762977server_pkey = compose_dh_pkey(cryptoctx->client_pkey,2978subjectPublicKey_data,2979subjectPublicKey_length);2980if (server_pkey == NULL) {2981retval = KRB5_PREAUTH_FAILED;2982k5_setmsg(context, retval, _("Cannot compose PKINIT KDC public key"));2983goto cleanup;2984}29852986if (!dh_result(cryptoctx->client_pkey, server_pkey,2987&client_key, &client_key_len))2988goto cleanup;29892990#ifdef DEBUG_DH2991print_pubkey(server_pub_key, "server's pub_key=");2992pkiDebug("client computed key (%d)= ", client_key_len);2993print_buffer(client_key, client_key_len);2994#endif29952996*client_key_out = client_key;2997*client_key_len_out = client_key_len;2998client_key = NULL;29993000retval = 0;30013002cleanup:3003EVP_PKEY_free(server_pkey);3004free(client_key);3005return retval;3006}30073008krb5_error_code3009server_check_dh(krb5_context context,3010pkinit_plg_crypto_context cryptoctx,3011pkinit_req_crypto_context req_cryptoctx,3012pkinit_identity_crypto_context id_cryptoctx,3013const krb5_data *client_spki,3014int minbits)3015{3016EVP_PKEY *client_pkey = NULL;3017int dh_bits;3018krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;30193020client_pkey = decode_spki(client_spki);3021if (client_pkey == NULL) {3022pkiDebug("failed to decode dhparams\n");3023goto cleanup;3024}30253026dh_bits = check_dh_wellknown(cryptoctx, client_pkey);3027if (dh_bits == -1 || dh_bits < minbits) {3028TRACE_PKINIT_DH_REJECTING_GROUP(context, group_desc(dh_bits),3029group_desc(minbits));3030goto cleanup;3031}3032TRACE_PKINIT_DH_RECEIVED_GROUP(context, group_desc(dh_bits));30333034retval = 0;30353036cleanup:3037if (retval == 0)3038req_cryptoctx->client_pkey = client_pkey;3039else3040EVP_PKEY_free(client_pkey);30413042return retval;3043}30443045/* kdc's dh function */3046krb5_error_code3047server_process_dh(krb5_context context,3048pkinit_plg_crypto_context plg_cryptoctx,3049pkinit_req_crypto_context cryptoctx,3050pkinit_identity_crypto_context id_cryptoctx,3051unsigned char **dh_pubkey_out,3052unsigned int *dh_pubkey_len_out,3053unsigned char **server_key_out,3054unsigned int *server_key_len_out)3055{3056krb5_error_code retval = ENOMEM;3057EVP_PKEY *server_pkey = NULL;3058unsigned char *dh_pubkey = NULL, *server_key = NULL;3059unsigned int dh_pubkey_len = 0, server_key_len = 0;30603061*dh_pubkey_out = *server_key_out = NULL;3062*dh_pubkey_len_out = *server_key_len_out = 0;30633064/* Generate a server DH key with the same parameters as the client key. */3065server_pkey = generate_dh_pkey(cryptoctx->client_pkey);3066if (server_pkey == NULL)3067goto cleanup;30683069if (!dh_result(server_pkey, cryptoctx->client_pkey, &server_key,3070&server_key_len))3071goto cleanup;30723073if (!dh_pubkey_der(server_pkey, &dh_pubkey, &dh_pubkey_len))3074goto cleanup;30753076*dh_pubkey_out = dh_pubkey;3077*dh_pubkey_len_out = dh_pubkey_len;3078*server_key_out = server_key;3079*server_key_len_out = server_key_len;3080dh_pubkey = server_key = NULL;30813082retval = 0;30833084cleanup:3085EVP_PKEY_free(server_pkey);3086free(dh_pubkey);3087free(server_key);30883089return retval;3090}30913092int3093pkinit_openssl_init(void)3094{3095/* Initialize OpenSSL. */3096ERR_load_crypto_strings();3097OpenSSL_add_all_algorithms();3098return 0;3099}31003101static krb5_error_code3102pkinit_create_sequence_of_principal_identifiers(3103krb5_context context,3104pkinit_plg_crypto_context plg_cryptoctx,3105pkinit_req_crypto_context req_cryptoctx,3106pkinit_identity_crypto_context id_cryptoctx,3107int type,3108krb5_pa_data ***e_data_out)3109{3110krb5_error_code retval = KRB5KRB_ERR_GENERIC;3111krb5_external_principal_identifier **krb5_trusted_certifiers = NULL;3112krb5_data *td_certifiers = NULL;3113krb5_pa_data **pa_data = NULL;31143115switch(type) {3116case TD_TRUSTED_CERTIFIERS:3117retval = create_krb5_trustedCertifiers(context, plg_cryptoctx,3118req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);3119if (retval) {3120pkiDebug("create_krb5_trustedCertifiers failed\n");3121goto cleanup;3122}3123break;3124case TD_INVALID_CERTIFICATES:3125retval = create_krb5_invalidCertificates(context, plg_cryptoctx,3126req_cryptoctx, id_cryptoctx, &krb5_trusted_certifiers);3127if (retval) {3128pkiDebug("create_krb5_invalidCertificates failed\n");3129goto cleanup;3130}3131break;3132default:3133retval = -1;3134goto cleanup;3135}31363137retval = k5int_encode_krb5_td_trusted_certifiers((krb5_external_principal_identifier *const *)krb5_trusted_certifiers, &td_certifiers);3138if (retval) {3139pkiDebug("encode_krb5_td_trusted_certifiers failed\n");3140goto cleanup;3141}3142#ifdef DEBUG_ASN13143print_buffer_bin((unsigned char *)td_certifiers->data,3144td_certifiers->length, "/tmp/kdc_td_certifiers");3145#endif3146pa_data = malloc(2 * sizeof(krb5_pa_data *));3147if (pa_data == NULL) {3148retval = ENOMEM;3149goto cleanup;3150}3151pa_data[1] = NULL;3152pa_data[0] = malloc(sizeof(krb5_pa_data));3153if (pa_data[0] == NULL) {3154free(pa_data);3155retval = ENOMEM;3156goto cleanup;3157}3158pa_data[0]->pa_type = type;3159pa_data[0]->length = td_certifiers->length;3160pa_data[0]->contents = (krb5_octet *)td_certifiers->data;3161*e_data_out = pa_data;3162retval = 0;31633164cleanup:3165if (krb5_trusted_certifiers != NULL)3166free_krb5_external_principal_identifier(&krb5_trusted_certifiers);3167free(td_certifiers);3168return retval;3169}31703171krb5_error_code3172pkinit_create_td_trusted_certifiers(krb5_context context,3173pkinit_plg_crypto_context plg_cryptoctx,3174pkinit_req_crypto_context req_cryptoctx,3175pkinit_identity_crypto_context id_cryptoctx,3176krb5_pa_data ***e_data_out)3177{3178krb5_error_code retval = KRB5KRB_ERR_GENERIC;31793180retval = pkinit_create_sequence_of_principal_identifiers(context,3181plg_cryptoctx, req_cryptoctx, id_cryptoctx,3182TD_TRUSTED_CERTIFIERS, e_data_out);31833184return retval;3185}31863187krb5_error_code3188pkinit_create_td_invalid_certificate(3189krb5_context context,3190pkinit_plg_crypto_context plg_cryptoctx,3191pkinit_req_crypto_context req_cryptoctx,3192pkinit_identity_crypto_context id_cryptoctx,3193krb5_pa_data ***e_data_out)3194{3195krb5_error_code retval = KRB5KRB_ERR_GENERIC;31963197retval = pkinit_create_sequence_of_principal_identifiers(context,3198plg_cryptoctx, req_cryptoctx, id_cryptoctx,3199TD_INVALID_CERTIFICATES, e_data_out);32003201return retval;3202}32033204krb5_error_code3205pkinit_create_td_dh_parameters(krb5_context context,3206pkinit_plg_crypto_context plg_cryptoctx,3207pkinit_req_crypto_context req_cryptoctx,3208pkinit_identity_crypto_context id_cryptoctx,3209pkinit_plg_opts *opts,3210krb5_pa_data ***e_data_out)3211{3212krb5_error_code ret;3213int i;3214krb5_pa_data **pa_data = NULL;3215krb5_data *der_alglist = NULL;3216krb5_algorithm_identifier alg_1024 = { dh_oid, oakley_1024 };3217krb5_algorithm_identifier alg_2048 = { dh_oid, oakley_2048 };3218krb5_algorithm_identifier alg_4096 = { dh_oid, oakley_4096 };3219krb5_algorithm_identifier alg_p256 = { ec_oid, ec_p256 };3220krb5_algorithm_identifier alg_p384 = { ec_oid, ec_p384 };3221krb5_algorithm_identifier alg_p521 = { ec_oid, ec_p521 };3222krb5_algorithm_identifier *alglist[7];32233224i = 0;3225if (plg_cryptoctx->ec_p256 != NULL &&3226opts->dh_min_bits <= PKINIT_DH_P256_BITS)3227alglist[i++] = &alg_p256;3228if (plg_cryptoctx->ec_p384 != NULL &&3229opts->dh_min_bits <= PKINIT_DH_P384_BITS)3230alglist[i++] = &alg_p384;3231if (plg_cryptoctx->ec_p521 != NULL)3232alglist[i++] = &alg_p521;3233if (plg_cryptoctx->dh_2048 != NULL && opts->dh_min_bits <= 2048)3234alglist[i++] = &alg_2048;3235if (plg_cryptoctx->dh_4096 != NULL && opts->dh_min_bits <= 4096)3236alglist[i++] = &alg_4096;3237if (plg_cryptoctx->dh_1024 != NULL && opts->dh_min_bits <= 1024)3238alglist[i++] = &alg_1024;3239alglist[i] = NULL;32403241if (i == 0) {3242ret = KRB5KRB_ERR_GENERIC;3243k5_setmsg(context, ret,3244_("OpenSSL has no supported key exchange groups for "3245"pkinit_dh_min_bits=%d"), opts->dh_min_bits);3246goto cleanup;3247}32483249ret = k5int_encode_krb5_td_dh_parameters(alglist, &der_alglist);3250if (ret)3251goto cleanup;32523253pa_data = k5calloc(2, sizeof(*pa_data), &ret);3254if (pa_data == NULL)3255goto cleanup;3256pa_data[1] = NULL;3257pa_data[0] = k5alloc(sizeof(*pa_data[0]), &ret);3258if (pa_data[0] == NULL) {3259free(pa_data);3260goto cleanup;3261}3262pa_data[0]->pa_type = TD_DH_PARAMETERS;3263pa_data[0]->length = der_alglist->length;3264pa_data[0]->contents = (krb5_octet *)der_alglist->data;3265der_alglist->data = NULL;3266*e_data_out = pa_data;32673268cleanup:3269krb5_free_data(context, der_alglist);3270return ret;3271}32723273krb5_error_code3274pkinit_check_kdc_pkid(krb5_context context,3275pkinit_plg_crypto_context plg_cryptoctx,3276pkinit_req_crypto_context req_cryptoctx,3277pkinit_identity_crypto_context id_cryptoctx,3278unsigned char *pdid_buf,3279unsigned int pkid_len,3280int *valid_kdcPkId)3281{3282PKCS7_ISSUER_AND_SERIAL *is = NULL;3283const unsigned char *p = pdid_buf;3284int status = 1;3285X509 *kdc_cert = id_cryptoctx->my_cert;32863287*valid_kdcPkId = 0;3288pkiDebug("found kdcPkId in AS REQ\n");3289is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p, (int)pkid_len);3290if (is == NULL)3291return KRB5KDC_ERR_PREAUTH_FAILED;32923293status = X509_NAME_cmp(X509_get_issuer_name(kdc_cert), is->issuer);3294if (!status) {3295status = ASN1_INTEGER_cmp(X509_get_serialNumber(kdc_cert), is->serial);3296if (!status)3297*valid_kdcPkId = 1;3298}32993300X509_NAME_free(is->issuer);3301ASN1_INTEGER_free(is->serial);3302free(is);33033304return 0;3305}33063307krb5_error_code3308pkinit_process_td_dh_params(krb5_context context,3309pkinit_plg_crypto_context cryptoctx,3310pkinit_req_crypto_context req_cryptoctx,3311pkinit_identity_crypto_context id_cryptoctx,3312krb5_algorithm_identifier **algId,3313int *new_dh_size)3314{3315krb5_error_code retval = KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;3316EVP_PKEY *params = NULL;3317size_t i;3318int dh_bits, old_dh_size;33193320pkiDebug("dh parameters\n");33213322old_dh_size = *new_dh_size;33233324for (i = 0; algId[i] != NULL; i++) {3325/* Free any parameters from the previous iteration. */3326EVP_PKEY_free(params);3327params = NULL;33283329if (data_eq(algId[i]->algorithm, dh_oid))3330params = decode_dh_params(&algId[i]->parameters);3331else if (data_eq(algId[i]->algorithm, ec_oid))3332params = decode_ec_params(&algId[i]->parameters);3333if (params == NULL)3334continue;33353336dh_bits = check_dh_wellknown(cryptoctx, params);3337/* Skip any parameters shorter than the previous size or unknown. */3338if (dh_bits == -1 || dh_bits < old_dh_size)3339continue;3340TRACE_PKINIT_DH_NEGOTIATED_GROUP(context, group_desc(dh_bits));33413342*new_dh_size = dh_bits;3343retval = 0;3344goto cleanup;3345}33463347cleanup:3348EVP_PKEY_free(params);3349return retval;3350}33513352static int3353openssl_callback(int ok, X509_STORE_CTX * ctx)3354{3355#ifdef DEBUG3356if (!ok) {3357X509 *cert = X509_STORE_CTX_get_current_cert(ctx);3358int err = X509_STORE_CTX_get_error(ctx);3359const char *errmsg = X509_verify_cert_error_string(err);3360char buf[DN_BUF_LEN];33613362X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));3363pkiDebug("cert = %s\n", buf);3364pkiDebug("callback function: %d (%s)\n", err, errmsg);3365}3366#endif3367return ok;3368}33693370static int3371openssl_callback_ignore_crls(int ok, X509_STORE_CTX * ctx)3372{3373if (ok)3374return ok;3375return X509_STORE_CTX_get_error(ctx) == X509_V_ERR_UNABLE_TO_GET_CRL;3376}33773378static ASN1_OBJECT *3379pkinit_pkcs7type2oid(pkinit_plg_crypto_context cryptoctx, int pkcs7_type)3380{3381switch (pkcs7_type) {3382case CMS_SIGN_CLIENT:3383return cryptoctx->id_pkinit_authData;3384case CMS_SIGN_SERVER:3385return cryptoctx->id_pkinit_DHKeyData;3386case CMS_ENVEL_SERVER:3387return cryptoctx->id_pkinit_rkeyData;3388default:3389return NULL;3390}33913392}33933394#ifndef WITHOUT_PKCS113395static krb5_error_code3396load_pkcs11_module(krb5_context context, const char *modname,3397struct plugin_file_handle **handle_out,3398CK_FUNCTION_LIST_PTR_PTR p11_out)3399{3400struct plugin_file_handle *handle = NULL;3401CK_RV rv, (*getflist)(CK_FUNCTION_LIST_PTR_PTR);3402struct errinfo einfo = EMPTY_ERRINFO;3403const char *errmsg = NULL, *failure;3404void (*sym)(void);3405long err;34063407TRACE_PKINIT_PKCS11_OPEN(context, modname);3408err = krb5int_open_plugin(modname, &handle, &einfo);3409if (err) {3410failure = _("Cannot load PKCS11 module");3411goto error;3412}34133414err = krb5int_get_plugin_func(handle, "C_GetFunctionList", &sym, &einfo);3415if (err) {3416failure = _("Cannot find C_GetFunctionList in PKCS11 module");3417goto error;3418}34193420getflist = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))sym;3421rv = (*getflist)(p11_out);3422if (rv != CKR_OK) {3423failure = _("Cannot retrieve function list in PKCS11 module");3424goto error;3425}34263427*handle_out = handle;3428return 0;34293430error:3431if (err) {3432errmsg = k5_get_error(&einfo, err);3433k5_setmsg(context, err, _("%s: %s"), failure, errmsg);3434} else {3435err = KRB5KDC_ERR_PREAUTH_FAILED;3436k5_setmsg(context, err, "%s", failure);3437}3438k5_clear_error(&einfo);3439if (handle != NULL)3440krb5int_close_plugin(handle);3441return err;3442}34433444static krb5_error_code3445pkinit_login(krb5_context context,3446pkinit_identity_crypto_context id_cryptoctx,3447CK_TOKEN_INFO *tip, const char *password)3448{3449krb5_error_code ret = 0;3450CK_RV rv;3451krb5_data rdat;3452char *prompt;3453const char *warning;3454krb5_prompt kprompt;3455krb5_prompt_type prompt_type;34563457if (tip->flags & CKF_PROTECTED_AUTHENTICATION_PATH) {3458rdat.data = NULL;3459rdat.length = 0;3460} else if (password != NULL) {3461rdat.data = strdup(password);3462rdat.length = strlen(password);3463} else if (id_cryptoctx->prompter == NULL) {3464ret = KRB5_LIBOS_CANTREADPWD;3465rdat.data = NULL;3466} else {3467if (tip->flags & CKF_USER_PIN_LOCKED)3468warning = " (Warning: PIN locked)";3469else if (tip->flags & CKF_USER_PIN_FINAL_TRY)3470warning = " (Warning: PIN final try)";3471else if (tip->flags & CKF_USER_PIN_COUNT_LOW)3472warning = " (Warning: PIN count low)";3473else3474warning = "";3475if (asprintf(&prompt, "%.*s PIN%s", (int) sizeof (tip->label),3476tip->label, warning) < 0)3477return ENOMEM;3478rdat.data = malloc(tip->ulMaxPinLen + 2);3479rdat.length = tip->ulMaxPinLen + 1;34803481kprompt.prompt = prompt;3482kprompt.hidden = 1;3483kprompt.reply = &rdat;3484prompt_type = KRB5_PROMPT_TYPE_PREAUTH;34853486/* PROMPTER_INVOCATION */3487k5int_set_prompt_types(context, &prompt_type);3488ret = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,3489NULL, NULL, 1, &kprompt);3490k5int_set_prompt_types(context, 0);3491free(prompt);3492}34933494if (!ret) {3495rv = id_cryptoctx->p11->C_Login(id_cryptoctx->session, CKU_USER,3496(uint8_t *)rdat.data, rdat.length);3497if (rv != CKR_OK)3498ret = p11err(context, rv, "C_Login");3499}3500free(rdat.data);35013502return ret;3503}35043505static krb5_error_code3506pkinit_open_session(krb5_context context,3507pkinit_identity_crypto_context cctx)3508{3509CK_ULONG i, rv;3510unsigned char *cp;3511size_t label_len;3512CK_ULONG count = 0;3513CK_SLOT_ID_PTR slotlist = NULL;3514CK_TOKEN_INFO tinfo;3515char *p11name = NULL;3516const char *password;3517krb5_error_code ret;35183519if (cctx->p11_module != NULL)3520return 0; /* session already open */35213522/* Load module */3523ret = load_pkcs11_module(context, cctx->p11_module_name, &cctx->p11_module,3524&cctx->p11);3525if (ret)3526goto cleanup;35273528/* Init */3529rv = cctx->p11->C_Initialize(NULL);3530if (rv != CKR_OK) {3531ret = p11err(context, rv, "C_Initialize");3532goto cleanup;3533}35343535/* Get the list of available slots */3536rv = cctx->p11->C_GetSlotList(TRUE, NULL, &count);3537if (rv != CKR_OK) {3538ret = p11err(context, rv, "C_GetSlotList");3539goto cleanup;3540}3541if (count == 0) {3542TRACE_PKINIT_PKCS11_NO_TOKEN(context);3543ret = KRB5KDC_ERR_PREAUTH_FAILED;3544goto cleanup;3545}3546slotlist = k5calloc(count, sizeof(CK_SLOT_ID), &ret);3547if (slotlist == NULL)3548goto cleanup;3549rv = cctx->p11->C_GetSlotList(TRUE, slotlist, &count);3550if (rv != CKR_OK) {3551ret = p11err(context, rv, "C_GetSlotList");3552goto cleanup;3553}35543555/* Look for the given token label, or if none given take the first one */3556for (i = 0; i < count; i++) {3557/* Skip slots that don't match the specified slotid, if given. */3558if (cctx->slotid != PK_NOSLOT && cctx->slotid != slotlist[i])3559continue;35603561/* Open session */3562rv = cctx->p11->C_OpenSession(slotlist[i], CKF_SERIAL_SESSION,3563NULL, NULL, &cctx->session);3564if (rv != CKR_OK) {3565ret = p11err(context, rv, "C_OpenSession");3566goto cleanup;3567}35683569/* Get token info */3570rv = cctx->p11->C_GetTokenInfo(slotlist[i], &tinfo);3571if (rv != CKR_OK) {3572ret = p11err(context, rv, "C_GetTokenInfo");3573goto cleanup;3574}35753576/* tinfo.label is zero-filled but not necessarily zero-terminated.3577* Find the length, ignoring any trailing spaces. */3578for (cp = tinfo.label + sizeof(tinfo.label); cp > tinfo.label; cp--) {3579if (cp[-1] != '\0' && cp[-1] != ' ')3580break;3581}3582label_len = cp - tinfo.label;35833584TRACE_PKINIT_PKCS11_SLOT(context, (int)slotlist[i], (int)label_len,3585tinfo.label);3586if (cctx->token_label == NULL ||3587(strlen(cctx->token_label) == label_len &&3588memcmp(cctx->token_label, tinfo.label, label_len) == 0))3589break;3590cctx->p11->C_CloseSession(cctx->session);3591}3592if (i >= count) {3593TRACE_PKINIT_PKCS11_NO_MATCH_TOKEN(context);3594ret = KRB5KDC_ERR_PREAUTH_FAILED;3595goto cleanup;3596}3597cctx->slotid = slotlist[i];3598pkiDebug("open_session: slotid %d (%lu of %d)\n", (int)cctx->slotid,3599i + 1, (int) count);36003601/* Login if needed */3602if (tinfo.flags & CKF_LOGIN_REQUIRED) {3603if (cctx->slotid != PK_NOSLOT) {3604if (asprintf(&p11name,3605"PKCS11:module_name=%s:slotid=%ld:token=%.*s",3606cctx->p11_module_name, (long)cctx->slotid,3607(int)label_len, tinfo.label) < 0)3608p11name = NULL;3609} else {3610if (asprintf(&p11name,3611"PKCS11:module_name=%s,token=%.*s",3612cctx->p11_module_name,3613(int)label_len, tinfo.label) < 0)3614p11name = NULL;3615}3616if (p11name == NULL) {3617ret = ENOMEM;3618goto cleanup;3619}3620if (cctx->defer_id_prompt) {3621/* Supply the identity name to be passed to the responder. */3622pkinit_set_deferred_id(&cctx->deferred_ids,3623p11name, tinfo.flags, NULL);3624ret = 0;3625goto cleanup;3626}3627/* Look up a responder-supplied password for the token. */3628password = pkinit_find_deferred_id(cctx->deferred_ids, p11name);3629ret = pkinit_login(context, cctx, &tinfo, password);3630if (ret)3631goto cleanup;3632}36333634ret = 0;3635cleanup:3636/* On error, finalize the PKCS11 fields to ensure that we don't mistakenly3637* short-circuit with success on the next call. */3638if (ret)3639pkinit_fini_pkcs11(cctx);3640free(slotlist);3641free(p11name);3642return ret;3643}36443645/*3646* Look for a key that's:3647* 1. private3648* 2. capable of the specified operation (usually signing or decrypting)3649* 3. matches the id of the cert we chose3650*3651* You must call pkinit_get_certs before calling pkinit_find_private_key3652* (that's because we need the ID of the private key)3653*3654* pkcs11 says the id of the key doesn't have to match that of the cert, but3655* I can't figure out any other way to decide which key to use.3656*3657* We should only find one key that fits all the requirements.3658* If there are more than one, we just take the first one.3659*/36603661static krb5_error_code3662pkinit_find_private_key(krb5_context context,3663pkinit_identity_crypto_context id_cryptoctx,3664CK_ATTRIBUTE_TYPE usage,3665CK_OBJECT_HANDLE *objp)3666{3667CK_OBJECT_CLASS cls;3668CK_ATTRIBUTE attrs[4];3669CK_ULONG count;3670CK_RV rv;3671unsigned int nattrs = 0;3672#ifdef PKINIT_USE_KEY_USAGE3673CK_BBOOL true_false;3674#endif36753676cls = CKO_PRIVATE_KEY;3677attrs[nattrs].type = CKA_CLASS;3678attrs[nattrs].pValue = &cls;3679attrs[nattrs].ulValueLen = sizeof cls;3680nattrs++;36813682#ifdef PKINIT_USE_KEY_USAGE3683/*3684* Some cards get confused if you try to specify a key usage,3685* so don't, and hope for the best. This will fail if you have3686* several keys with the same id and different usages but I have3687* not seen this on real cards.3688*/3689true_false = TRUE;3690attrs[nattrs].type = usage;3691attrs[nattrs].pValue = &true_false;3692attrs[nattrs].ulValueLen = sizeof true_false;3693nattrs++;3694#endif36953696attrs[nattrs].type = CKA_ID;3697attrs[nattrs].pValue = id_cryptoctx->cert_id;3698attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;3699nattrs++;37003701rv = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs,3702nattrs);3703if (rv != CKR_OK)3704return p11err(context, rv, _("C_FindObjectsInit"));37053706rv = id_cryptoctx->p11->C_FindObjects(id_cryptoctx->session, objp, 1,3707&count);3708id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);3709if (rv != CKR_OK)3710return p11err(context, rv, _("C_FindObjects"));3711if (count < 1) {3712k5_setmsg(context, KRB5KDC_ERR_PREAUTH_FAILED,3713_("Found no private keys in PKCS11 token"));3714return KRB5KDC_ERR_PREAUTH_FAILED;3715}3716return 0;3717}3718#endif37193720static krb5_error_code3721pkinit_sign_data_fs(krb5_context context,3722pkinit_identity_crypto_context id_cryptoctx,3723unsigned char *data,3724unsigned int data_len,3725unsigned char **sig,3726unsigned int *sig_len)3727{3728if (create_signature(sig, sig_len, data, data_len,3729id_cryptoctx->my_key) != 0) {3730pkiDebug("failed to create the signature\n");3731return KRB5KDC_ERR_PREAUTH_FAILED;3732}3733return 0;3734}37353736#ifndef WITHOUT_PKCS113737/*3738* DER-encode a DigestInfo sequence containing the algorithm md and the digest3739* mdbytes.3740*3741* DigestInfo ::= SEQUENCE {3742* digestAlgorithm AlgorithmIdentifier,3743* digest OCTET STRING3744* }3745*/3746static krb5_error_code3747encode_digestinfo(krb5_context context, const EVP_MD *md,3748const uint8_t *mdbytes, size_t mdlen,3749uint8_t **encoding_out, size_t *len_out)3750{3751krb5_boolean ok = FALSE;3752X509_ALGOR *alg = NULL;3753ASN1_OCTET_STRING *digest = NULL;3754uint8_t *buf, *p;3755int alg_len, digest_len, len;37563757*encoding_out = NULL;3758*len_out = 0;37593760alg = X509_ALGOR_new();3761if (alg == NULL ||3762!X509_ALGOR_set0(alg, OBJ_nid2obj(EVP_MD_nid(md)), V_ASN1_NULL, NULL))3763goto cleanup;3764alg_len = i2d_X509_ALGOR(alg, NULL);3765if (alg_len < 0)3766goto cleanup;37673768digest = ASN1_OCTET_STRING_new();3769if (digest == NULL || !ASN1_OCTET_STRING_set(digest, mdbytes, mdlen))3770goto cleanup;3771digest_len = i2d_ASN1_OCTET_STRING(digest, NULL);3772if (digest_len < 0)3773goto cleanup;37743775len = ASN1_object_size(1, alg_len + digest_len, V_ASN1_SEQUENCE);3776p = buf = malloc(len);3777if (buf == NULL)3778goto cleanup;3779ASN1_put_object(&p, 1, alg_len + digest_len, V_ASN1_SEQUENCE,3780V_ASN1_UNIVERSAL);3781i2d_X509_ALGOR(alg, &p);3782i2d_ASN1_OCTET_STRING(digest, &p);37833784*encoding_out = buf;3785*len_out = len;3786ok = TRUE;37873788cleanup:3789X509_ALGOR_free(alg);3790ASN1_OCTET_STRING_free(digest);3791if (!ok)3792return oerr(context, 0, _("Failed to DER encode DigestInfo"));3793return 0;3794}37953796/* Extract the r and s values from a PKCS11 ECDSA signature and re-encode them3797* in the DER representation of an ECDSA-Sig-Value for use in CMS. */3798static krb5_error_code3799convert_pkcs11_ecdsa_sig(krb5_context context,3800const uint8_t *p11sig, unsigned int p11siglen,3801uint8_t **sig_out, unsigned int *sig_len_out)3802{3803krb5_boolean ok = FALSE;3804BIGNUM *r = NULL, *s = NULL;3805ECDSA_SIG *sig = NULL;3806int len;3807uint8_t *p;38083809*sig_out = NULL;3810*sig_len_out = 0;38113812if (p11siglen % 2 != 0)3813return EINVAL;38143815/* Extract the r and s values from the PKCS11 signature. */3816r = BN_bin2bn(p11sig, p11siglen / 2, NULL);3817s = BN_bin2bn(p11sig + p11siglen / 2, p11siglen / 2, NULL);3818if (r == NULL || s == NULL)3819goto cleanup;38203821/* Create an ECDSA-Sig-Value object and transfer ownership of r and s. */3822sig = ECDSA_SIG_new();3823if (sig == NULL || !ECDSA_SIG_set0(sig, r, s))3824goto cleanup;3825r = s = NULL;38263827/* DER-encode the ECDSA-Sig-Value object. */3828len = i2d_ECDSA_SIG(sig, NULL);3829if (len < 0)3830goto cleanup;3831p = *sig_out = malloc(len);3832if (*sig_out == NULL)3833goto cleanup;3834*sig_len_out = len;3835i2d_ECDSA_SIG(sig, &p);3836ok = TRUE;38373838cleanup:3839BN_free(r);3840BN_free(s);3841ECDSA_SIG_free(sig);3842if (!ok)3843return oerr(context, 0, _("Failed to convert PKCS11 ECDSA signature"));3844return 0;3845}38463847static krb5_error_code3848pkinit_sign_data_pkcs11(krb5_context context,3849pkinit_identity_crypto_context id_cryptoctx,3850unsigned char *data,3851unsigned int data_len,3852unsigned char **sig,3853unsigned int *sig_len)3854{3855krb5_error_code ret;3856CK_OBJECT_HANDLE obj;3857CK_ULONG len;3858CK_MECHANISM mech;3859CK_SESSION_HANDLE session;3860CK_FUNCTION_LIST_PTR p11;3861CK_ATTRIBUTE attr;3862CK_KEY_TYPE keytype;3863CK_RV rv;3864EVP_MD_CTX *ctx;3865const EVP_MD *md = EVP_sha256();3866unsigned int mdlen;3867uint8_t mdbuf[EVP_MAX_MD_SIZE], *dinfo = NULL, *sigbuf = NULL, *input;3868size_t dinfo_len, input_len;38693870*sig = NULL;3871*sig_len = 0;38723873ret = pkinit_open_session(context, id_cryptoctx);3874if (ret)3875return ret;3876p11 = id_cryptoctx->p11;3877session = id_cryptoctx->session;38783879ret = pkinit_find_private_key(context, id_cryptoctx, CKA_SIGN, &obj);3880if (ret)3881return ret;38823883attr.type = CKA_KEY_TYPE;3884attr.pValue = &keytype;3885attr.ulValueLen = sizeof(keytype);3886rv = p11->C_GetAttributeValue(session, obj, &attr, 1);3887if (rv != CKR_OK) {3888ret = p11err(context, rv, "C_GetAttributeValue");3889goto cleanup;3890}38913892/*3893* We would ideally use CKM_SHA256_RSA_PKCS and CKM_ECDSA_SHA256, but3894* historically many cards seem to be confused about whether they are3895* capable of mechanisms or not. To be safe we compute the digest3896* ourselves and use CKM_RSA_PKCS and CKM_ECDSA.3897*/3898ctx = EVP_MD_CTX_new();3899if (ctx == NULL) {3900ret = KRB5KDC_ERR_PREAUTH_FAILED;3901goto cleanup;3902}3903EVP_DigestInit_ex(ctx, EVP_sha256(), NULL);3904EVP_DigestUpdate(ctx, data, data_len);3905EVP_DigestFinal_ex(ctx, mdbuf, &mdlen);3906EVP_MD_CTX_free(ctx);39073908if (keytype == CKK_RSA) {3909/* For RSA we must also encode the digest in a DigestInfo sequence. */3910mech.mechanism = CKM_RSA_PKCS;3911ret = encode_digestinfo(context, md, mdbuf, mdlen, &dinfo, &dinfo_len);3912if (ret)3913goto cleanup;3914input = dinfo;3915input_len = dinfo_len;3916} else if (keytype == CKK_EC) {3917mech.mechanism = CKM_ECDSA;3918input = mdbuf;3919input_len = mdlen;3920} else {3921ret = KRB5KDC_ERR_PREAUTH_FAILED;3922k5_setmsg(context, ret,3923_("PKCS11 certificate has unsupported key type %lu"),3924keytype);3925goto cleanup;3926}3927mech.pParameter = NULL;3928mech.ulParameterLen = 0;39293930rv = p11->C_SignInit(session, &mech, obj);3931if (rv != CKR_OK) {3932ret = p11err(context, rv, "C_SignInit");3933goto cleanup;3934}39353936/*3937* Key len would give an upper bound on sig size, but there's no way to3938* get that. So guess, and if it's too small, re-malloc.3939*/3940len = PK_SIGLEN_GUESS;3941sigbuf = k5alloc(len, &ret);3942if (sigbuf == NULL)3943goto cleanup;39443945rv = p11->C_Sign(session, input, input_len, sigbuf, &len);3946if (rv == CKR_BUFFER_TOO_SMALL ||3947(rv == CKR_OK && len >= PK_SIGLEN_GUESS)) {3948free(sigbuf);3949sigbuf = k5alloc(len, &ret);3950if (sigbuf == NULL)3951goto cleanup;3952rv = p11->C_Sign(session, input, input_len, sigbuf, &len);3953}3954if (rv != CKR_OK) {3955ret = p11err(context, rv, "C_Sign");3956goto cleanup;3957}39583959if (keytype == CKK_EC) {3960/* PKCS11 ECDSA signatures must be re-encoded for CMS. */3961ret = convert_pkcs11_ecdsa_sig(context, sigbuf, len, sig, sig_len);3962} else {3963*sig_len = len;3964*sig = sigbuf;3965sigbuf = NULL;3966}39673968cleanup:3969free(dinfo);3970free(sigbuf);3971return ret;3972}3973#endif39743975krb5_error_code3976pkinit_sign_data(krb5_context context,3977pkinit_identity_crypto_context id_cryptoctx,3978unsigned char *data,3979unsigned int data_len,3980unsigned char **sig,3981unsigned int *sig_len)3982{3983krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;39843985if (id_cryptoctx == NULL || id_cryptoctx->pkcs11_method != 1)3986retval = pkinit_sign_data_fs(context, id_cryptoctx, data, data_len,3987sig, sig_len);3988#ifndef WITHOUT_PKCS113989else3990retval = pkinit_sign_data_pkcs11(context, id_cryptoctx, data, data_len,3991sig, sig_len);3992#endif39933994return retval;3995}399639973998static krb5_error_code3999create_signature(unsigned char **sig, unsigned int *sig_len,4000unsigned char *data, unsigned int data_len, EVP_PKEY *pkey)4001{4002krb5_error_code retval = ENOMEM;4003EVP_MD_CTX *ctx;40044005if (pkey == NULL)4006return retval;40074008ctx = EVP_MD_CTX_new();4009if (ctx == NULL)4010return ENOMEM;4011EVP_SignInit(ctx, EVP_sha256());4012EVP_SignUpdate(ctx, data, data_len);4013*sig_len = EVP_PKEY_size(pkey);4014if ((*sig = malloc(*sig_len)) == NULL)4015goto cleanup;4016EVP_SignFinal(ctx, *sig, sig_len, pkey);40174018retval = 0;40194020cleanup:4021EVP_MD_CTX_free(ctx);40224023return retval;4024}40254026/*4027* Note:4028* This is not the routine the KDC uses to get its certificate.4029* This routine is intended to be called by the client4030* to obtain the KDC's certificate from some local storage4031* to be sent as a hint in its request to the KDC.4032*/4033krb5_error_code4034pkinit_get_kdc_cert(krb5_context context,4035pkinit_plg_crypto_context plg_cryptoctx,4036pkinit_req_crypto_context req_cryptoctx,4037pkinit_identity_crypto_context id_cryptoctx,4038krb5_principal princ)4039{4040krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;40414042req_cryptoctx->received_cert = NULL;4043retval = 0;4044return retval;4045}40464047static char *4048reassemble_pkcs12_name(const char *filename)4049{4050char *ret;40514052if (asprintf(&ret, "PKCS12:%s", filename) < 0)4053return NULL;4054return ret;4055}40564057static krb5_error_code4058pkinit_get_certs_pkcs12(krb5_context context,4059pkinit_plg_crypto_context plg_cryptoctx,4060pkinit_req_crypto_context req_cryptoctx,4061pkinit_identity_opts *idopts,4062pkinit_identity_crypto_context id_cryptoctx,4063krb5_principal princ)4064{4065krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;4066char *prompt_string = NULL;4067X509 *x = NULL;4068PKCS12 *p12 = NULL;4069int ret;4070FILE *fp;4071EVP_PKEY *y = NULL;40724073if (idopts->cert_filename == NULL) {4074pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);4075goto cleanup;4076}40774078if (idopts->key_filename == NULL) {4079pkiDebug("%s: failed to get user's private key location\n", __FUNCTION__);4080goto cleanup;4081}40824083fp = fopen(idopts->cert_filename, "rb");4084if (fp == NULL) {4085TRACE_PKINIT_PKCS_OPEN_FAIL(context, idopts->cert_filename, errno);4086goto cleanup;4087}4088set_cloexec_file(fp);40894090p12 = d2i_PKCS12_fp(fp, NULL);4091fclose(fp);4092if (p12 == NULL) {4093TRACE_PKINIT_PKCS_DECODE_FAIL(context, idopts->cert_filename);4094goto cleanup;4095}4096/*4097* Try parsing with no pass phrase first. If that fails,4098* prompt for the pass phrase and try again.4099*/4100ret = PKCS12_parse(p12, NULL, &y, &x, NULL);4101if (ret == 0) {4102krb5_data rdat;4103krb5_prompt kprompt;4104krb5_prompt_type prompt_type;4105krb5_error_code r;4106char prompt_reply[128];4107char *prompt_prefix = _("Pass phrase for");4108char *p12name = reassemble_pkcs12_name(idopts->cert_filename);4109const char *tmp;41104111TRACE_PKINIT_PKCS_PARSE_FAIL_FIRST(context);41124113if (p12name == NULL)4114goto cleanup;4115if (id_cryptoctx->defer_id_prompt) {4116/* Supply the identity name to be passed to the responder. */4117pkinit_set_deferred_id(&id_cryptoctx->deferred_ids, p12name, 0,4118NULL);4119free(p12name);4120retval = 0;4121goto cleanup;4122}4123/* Try to read a responder-supplied password. */4124tmp = pkinit_find_deferred_id(id_cryptoctx->deferred_ids, p12name);4125free(p12name);4126if (tmp != NULL) {4127/* Try using the responder-supplied password. */4128rdat.data = (char *)tmp;4129rdat.length = strlen(tmp);4130} else if (id_cryptoctx->prompter == NULL) {4131/* We can't use a prompter. */4132goto cleanup;4133} else {4134/* Ask using a prompter. */4135memset(prompt_reply, '\0', sizeof(prompt_reply));4136rdat.data = prompt_reply;4137rdat.length = sizeof(prompt_reply);41384139if (asprintf(&prompt_string, "%s %s", prompt_prefix,4140idopts->cert_filename) < 0) {4141prompt_string = NULL;4142goto cleanup;4143}4144kprompt.prompt = prompt_string;4145kprompt.hidden = 1;4146kprompt.reply = &rdat;4147prompt_type = KRB5_PROMPT_TYPE_PREAUTH;4148/* PROMPTER_INVOCATION */4149k5int_set_prompt_types(context, &prompt_type);4150r = (*id_cryptoctx->prompter)(context, id_cryptoctx->prompter_data,4151NULL, NULL, 1, &kprompt);4152k5int_set_prompt_types(context, 0);4153if (r) {4154TRACE_PKINIT_PKCS_PROMPT_FAIL(context);4155goto cleanup;4156}4157}41584159ret = PKCS12_parse(p12, rdat.data, &y, &x, NULL);4160if (ret == 0) {4161TRACE_PKINIT_PKCS_PARSE_FAIL_SECOND(context);4162goto cleanup;4163}4164}4165id_cryptoctx->creds[0] = malloc(sizeof(struct _pkinit_cred_info));4166if (id_cryptoctx->creds[0] == NULL)4167goto cleanup;4168id_cryptoctx->creds[0]->name =4169reassemble_pkcs12_name(idopts->cert_filename);4170id_cryptoctx->creds[0]->cert = x;4171#ifndef WITHOUT_PKCS114172id_cryptoctx->creds[0]->cert_id = NULL;4173id_cryptoctx->creds[0]->cert_id_len = 0;4174#endif4175id_cryptoctx->creds[0]->key = y;4176id_cryptoctx->creds[1] = NULL;41774178retval = 0;41794180cleanup:4181free(prompt_string);4182if (p12)4183PKCS12_free(p12);4184if (retval) {4185if (x != NULL)4186X509_free(x);4187if (y != NULL)4188EVP_PKEY_free(y);4189}4190return retval;4191}41924193static char *4194reassemble_files_name(const char *certfile, const char *keyfile)4195{4196char *ret;41974198if (keyfile != NULL) {4199if (asprintf(&ret, "FILE:%s,%s", certfile, keyfile) < 0)4200return NULL;4201} else {4202if (asprintf(&ret, "FILE:%s", certfile) < 0)4203return NULL;4204}4205return ret;4206}42074208static krb5_error_code4209pkinit_load_fs_cert_and_key(krb5_context context,4210pkinit_identity_crypto_context id_cryptoctx,4211char *certname,4212char *keyname,4213int cindex)4214{4215krb5_error_code retval;4216X509 *x = NULL;4217EVP_PKEY *y = NULL;4218char *fsname = NULL;4219const char *password;42204221fsname = reassemble_files_name(certname, keyname);42224223/* Try to read a responder-supplied password. */4224password = pkinit_find_deferred_id(id_cryptoctx->deferred_ids, fsname);42254226/* Load the certificate. */4227retval = get_cert(certname, &x);4228if (retval) {4229retval = oerr(context, retval, _("Cannot read certificate file '%s'"),4230certname);4231}4232if (retval || x == NULL)4233goto cleanup;4234/* Load the key. */4235retval = get_key(context, id_cryptoctx, keyname, fsname, &y, password);4236if (retval)4237retval = oerr(context, retval, _("Cannot read key file '%s'"), fsname);4238if (retval || y == NULL)4239goto cleanup;42404241id_cryptoctx->creds[cindex] = malloc(sizeof(struct _pkinit_cred_info));4242if (id_cryptoctx->creds[cindex] == NULL) {4243retval = ENOMEM;4244goto cleanup;4245}4246id_cryptoctx->creds[cindex]->name = reassemble_files_name(certname,4247keyname);4248id_cryptoctx->creds[cindex]->cert = x;4249#ifndef WITHOUT_PKCS114250id_cryptoctx->creds[cindex]->cert_id = NULL;4251id_cryptoctx->creds[cindex]->cert_id_len = 0;4252#endif4253id_cryptoctx->creds[cindex]->key = y;4254id_cryptoctx->creds[cindex+1] = NULL;42554256retval = 0;42574258cleanup:4259free(fsname);4260if (retval != 0 || y == NULL) {4261if (x != NULL)4262X509_free(x);4263if (y != NULL)4264EVP_PKEY_free(y);4265}4266return retval;4267}42684269static krb5_error_code4270pkinit_get_certs_fs(krb5_context context,4271pkinit_plg_crypto_context plg_cryptoctx,4272pkinit_req_crypto_context req_cryptoctx,4273pkinit_identity_opts *idopts,4274pkinit_identity_crypto_context id_cryptoctx,4275krb5_principal princ)4276{4277krb5_error_code retval = KRB5KDC_ERR_PREAUTH_FAILED;42784279if (idopts->cert_filename == NULL) {4280pkiDebug("%s: failed to get user's cert location\n", __FUNCTION__);4281goto cleanup;4282}42834284if (idopts->key_filename == NULL) {4285TRACE_PKINIT_NO_PRIVKEY(context);4286goto cleanup;4287}42884289retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,4290idopts->cert_filename,4291idopts->key_filename, 0);4292cleanup:4293return retval;4294}42954296static krb5_error_code4297pkinit_get_certs_dir(krb5_context context,4298pkinit_plg_crypto_context plg_cryptoctx,4299pkinit_req_crypto_context req_cryptoctx,4300pkinit_identity_opts *idopts,4301pkinit_identity_crypto_context id_cryptoctx,4302krb5_principal princ)4303{4304krb5_error_code retval = ENOMEM;4305int ncreds = 0, len, i;4306char *dirname, *suf, *name, **fnames = NULL;4307char *certname = NULL, *keyname = NULL;43084309if (idopts->cert_filename == NULL) {4310TRACE_PKINIT_NO_CERT(context);4311return ENOENT;4312}43134314dirname = idopts->cert_filename;4315retval = k5_dir_filenames(dirname, &fnames);4316if (retval)4317return retval;43184319/*4320* We'll assume that certs are named XXX.crt and the corresponding4321* key is named XXX.key4322*/4323for (i = 0; fnames[i] != NULL; i++) {4324/* Ignore anything starting with a dot */4325name = fnames[i];4326if (name[0] == '.')4327continue;4328len = strlen(name);4329if (len < 5)4330continue;4331suf = name + (len - 4);4332if (strncmp(suf, ".crt", 4) != 0)4333continue;43344335retval = k5_path_join(dirname, name, &certname);4336if (retval)4337goto cleanup;4338retval = k5_path_join(dirname, name, &keyname);4339if (retval)4340goto cleanup;43414342len = strlen(keyname);4343keyname[len - 3] = 'k';4344keyname[len - 2] = 'e';4345keyname[len - 1] = 'y';43464347retval = pkinit_load_fs_cert_and_key(context, id_cryptoctx,4348certname, keyname, ncreds);4349free(certname);4350free(keyname);4351certname = keyname = NULL;4352if (!retval) {4353TRACE_PKINIT_LOADED_CERT(context, name);4354if (++ncreds >= MAX_CREDS_ALLOWED)4355break;4356}4357}43584359if (!id_cryptoctx->defer_id_prompt && ncreds == 0) {4360TRACE_PKINIT_NO_CERT_AND_KEY(context, idopts->cert_filename);4361retval = ENOENT;4362goto cleanup;4363}43644365retval = 0;43664367cleanup:4368k5_free_filenames(fnames);4369free(certname);4370free(keyname);4371return retval;4372}43734374#ifndef WITHOUT_PKCS114375static char *4376reassemble_pkcs11_name(pkinit_identity_opts *idopts)4377{4378struct k5buf buf;4379int n = 0;43804381k5_buf_init_dynamic(&buf);4382k5_buf_add(&buf, "PKCS11:");4383n = 0;4384if (idopts->p11_module_name != NULL) {4385k5_buf_add_fmt(&buf, "%smodule_name=%s", n++ ? ":" : "",4386idopts->p11_module_name);4387}4388if (idopts->token_label != NULL) {4389k5_buf_add_fmt(&buf, "%stoken=%s", n++ ? ":" : "",4390idopts->token_label);4391}4392if (idopts->cert_label != NULL) {4393k5_buf_add_fmt(&buf, "%scertlabel=%s", n++ ? ":" : "",4394idopts->cert_label);4395}4396if (idopts->cert_id_string != NULL) {4397k5_buf_add_fmt(&buf, "%scertid=%s", n++ ? ":" : "",4398idopts->cert_id_string);4399}4400if (idopts->slotid != PK_NOSLOT) {4401k5_buf_add_fmt(&buf, "%sslotid=%ld", n++ ? ":" : "",4402(long)idopts->slotid);4403}4404return k5_buf_cstring(&buf);4405}44064407static krb5_error_code4408load_one_cert(krb5_context context, CK_FUNCTION_LIST_PTR p11,4409CK_SESSION_HANDLE session, pkinit_identity_opts *idopts,4410pkinit_cred_info *cred_out)4411{4412krb5_error_code ret;4413CK_ATTRIBUTE attrs[2];4414CK_BYTE_PTR cert = NULL, cert_id = NULL;4415CK_RV rv;4416const unsigned char *cp;4417CK_OBJECT_HANDLE obj;4418CK_ULONG count;4419X509 *x = NULL;4420pkinit_cred_info cred;44214422*cred_out = NULL;44234424/* Look for X.509 cert. */4425rv = p11->C_FindObjects(session, &obj, 1, &count);4426if (rv != CKR_OK || count <= 0)4427return 0;44284429/* Get cert and id len. */4430attrs[0].type = CKA_VALUE;4431attrs[0].pValue = NULL;4432attrs[0].ulValueLen = 0;4433attrs[1].type = CKA_ID;4434attrs[1].pValue = NULL;4435attrs[1].ulValueLen = 0;4436rv = p11->C_GetAttributeValue(session, obj, attrs, 2);4437if (rv != CKR_OK && rv != CKR_BUFFER_TOO_SMALL) {4438ret = p11err(context, rv, "C_GetAttributeValue");4439goto cleanup;4440}44414442/* Allocate buffers and read the cert and id. */4443cert = k5alloc(attrs[0].ulValueLen + 1, &ret);4444if (cert == NULL)4445goto cleanup;4446cert_id = k5alloc(attrs[1].ulValueLen + 1, &ret);4447if (cert_id == NULL)4448goto cleanup;4449attrs[0].type = CKA_VALUE;4450attrs[0].pValue = cert;4451attrs[1].type = CKA_ID;4452attrs[1].pValue = cert_id;4453rv = p11->C_GetAttributeValue(session, obj, attrs, 2);4454if (rv != CKR_OK) {4455ret = p11err(context, rv, "C_GetAttributeValue");4456goto cleanup;4457}44584459pkiDebug("cert: size %d, id %d, idlen %d\n", (int)attrs[0].ulValueLen,4460(int)cert_id[0], (int)attrs[1].ulValueLen);44614462cp = (unsigned char *)cert;4463x = d2i_X509(NULL, &cp, (int)attrs[0].ulValueLen);4464if (x == NULL) {4465ret = oerr(context, 0,4466_("Failed to decode X509 certificate from PKCS11 token"));4467goto cleanup;4468}44694470cred = k5alloc(sizeof(struct _pkinit_cred_info), &ret);4471if (cred == NULL)4472goto cleanup;44734474cred->name = reassemble_pkcs11_name(idopts);4475cred->cert = x;4476cred->key = NULL;4477cred->cert_id = cert_id;4478cred->cert_id_len = attrs[1].ulValueLen;44794480*cred_out = cred;4481cert_id = NULL;4482ret = 0;44834484cleanup:4485free(cert);4486free(cert_id);4487return ret;4488}44894490static krb5_error_code4491pkinit_get_certs_pkcs11(krb5_context context,4492pkinit_plg_crypto_context plg_cryptoctx,4493pkinit_req_crypto_context req_cryptoctx,4494pkinit_identity_opts *idopts,4495pkinit_identity_crypto_context id_cryptoctx,4496krb5_principal princ)4497{4498CK_OBJECT_CLASS cls;4499CK_ATTRIBUTE attrs[4];4500CK_CERTIFICATE_TYPE certtype;4501int i;4502unsigned int nattrs;4503krb5_error_code ret;4504CK_RV rv;45054506/* Copy stuff from idopts -> id_cryptoctx */4507if (idopts->p11_module_name != NULL) {4508free(id_cryptoctx->p11_module_name);4509id_cryptoctx->p11_module_name = strdup(idopts->p11_module_name);4510if (id_cryptoctx->p11_module_name == NULL)4511return ENOMEM;4512}4513if (idopts->token_label != NULL) {4514id_cryptoctx->token_label = strdup(idopts->token_label);4515if (id_cryptoctx->token_label == NULL)4516return ENOMEM;4517}4518if (idopts->cert_label != NULL) {4519id_cryptoctx->cert_label = strdup(idopts->cert_label);4520if (id_cryptoctx->cert_label == NULL)4521return ENOMEM;4522}4523/* Convert the ascii cert_id string into a binary blob */4524if (idopts->cert_id_string != NULL) {4525ret = k5_hex_decode(idopts->cert_id_string, &id_cryptoctx->cert_id,4526&id_cryptoctx->cert_id_len);4527if (ret) {4528pkiDebug("Failed to convert certid string [%s]\n",4529idopts->cert_id_string);4530return ret;4531}4532}4533id_cryptoctx->slotid = idopts->slotid;4534id_cryptoctx->pkcs11_method = 1;45354536ret = pkinit_open_session(context, id_cryptoctx);4537if (ret)4538return ret;4539if (id_cryptoctx->defer_id_prompt) {4540/*4541* We need to reset all of the PKCS#11 state, so that the next time we4542* poke at it, it'll be in as close to the state it was in after we4543* loaded it the first time as we can make it.4544*/4545pkinit_fini_pkcs11(id_cryptoctx);4546pkinit_init_pkcs11(id_cryptoctx);4547return 0;4548}45494550cls = CKO_CERTIFICATE;4551attrs[0].type = CKA_CLASS;4552attrs[0].pValue = &cls;4553attrs[0].ulValueLen = sizeof(cls);45544555certtype = CKC_X_509;4556attrs[1].type = CKA_CERTIFICATE_TYPE;4557attrs[1].pValue = &certtype;4558attrs[1].ulValueLen = sizeof(certtype);45594560nattrs = 2;45614562/* If a cert id and/or label were given, use them too */4563if (id_cryptoctx->cert_id_len > 0) {4564attrs[nattrs].type = CKA_ID;4565attrs[nattrs].pValue = id_cryptoctx->cert_id;4566attrs[nattrs].ulValueLen = id_cryptoctx->cert_id_len;4567nattrs++;4568}4569if (id_cryptoctx->cert_label != NULL) {4570attrs[nattrs].type = CKA_LABEL;4571attrs[nattrs].pValue = id_cryptoctx->cert_label;4572attrs[nattrs].ulValueLen = strlen(id_cryptoctx->cert_label);4573nattrs++;4574}45754576rv = id_cryptoctx->p11->C_FindObjectsInit(id_cryptoctx->session, attrs,4577nattrs);4578if (rv != CKR_OK) {4579ret = p11err(context, rv, "C_FindObjectsInit");4580return KRB5KDC_ERR_PREAUTH_FAILED;4581}45824583for (i = 0; i < MAX_CREDS_ALLOWED; i++) {4584ret = load_one_cert(context, id_cryptoctx->p11, id_cryptoctx->session,4585idopts, &id_cryptoctx->creds[i]);4586if (ret)4587return ret;4588if (id_cryptoctx->creds[i] == NULL)4589break;4590}4591if (i == MAX_CREDS_ALLOWED)4592return KRB5KDC_ERR_PREAUTH_FAILED;45934594id_cryptoctx->p11->C_FindObjectsFinal(id_cryptoctx->session);45954596/* Check if we found no certs. */4597if (id_cryptoctx->creds[0] == NULL)4598return KRB5KDC_ERR_PREAUTH_FAILED;4599return 0;4600}46014602#endif /* !WITHOUT_PKCS11 */460346044605static void4606free_cred_info(krb5_context context,4607pkinit_identity_crypto_context id_cryptoctx,4608struct _pkinit_cred_info *cred)4609{4610if (cred != NULL) {4611if (cred->cert != NULL)4612X509_free(cred->cert);4613if (cred->key != NULL)4614EVP_PKEY_free(cred->key);4615#ifndef WITHOUT_PKCS114616free(cred->cert_id);4617#endif4618free(cred->name);4619free(cred);4620}4621}46224623krb5_error_code4624crypto_free_cert_info(krb5_context context,4625pkinit_plg_crypto_context plg_cryptoctx,4626pkinit_req_crypto_context req_cryptoctx,4627pkinit_identity_crypto_context id_cryptoctx)4628{4629int i;46304631if (id_cryptoctx == NULL)4632return EINVAL;46334634for (i = 0; i < MAX_CREDS_ALLOWED; i++) {4635if (id_cryptoctx->creds[i] != NULL) {4636free_cred_info(context, id_cryptoctx, id_cryptoctx->creds[i]);4637id_cryptoctx->creds[i] = NULL;4638}4639}4640return 0;4641}46424643krb5_error_code4644crypto_load_certs(krb5_context context,4645pkinit_plg_crypto_context plg_cryptoctx,4646pkinit_req_crypto_context req_cryptoctx,4647pkinit_identity_opts *idopts,4648pkinit_identity_crypto_context id_cryptoctx,4649krb5_principal princ,4650krb5_boolean defer_id_prompts)4651{4652krb5_error_code retval;46534654id_cryptoctx->defer_id_prompt = defer_id_prompts;46554656switch(idopts->idtype) {4657case IDTYPE_FILE:4658retval = pkinit_get_certs_fs(context, plg_cryptoctx,4659req_cryptoctx, idopts,4660id_cryptoctx, princ);4661break;4662case IDTYPE_DIR:4663retval = pkinit_get_certs_dir(context, plg_cryptoctx,4664req_cryptoctx, idopts,4665id_cryptoctx, princ);4666break;4667#ifndef WITHOUT_PKCS114668case IDTYPE_PKCS11:4669retval = pkinit_get_certs_pkcs11(context, plg_cryptoctx,4670req_cryptoctx, idopts,4671id_cryptoctx, princ);4672break;4673#endif4674case IDTYPE_PKCS12:4675retval = pkinit_get_certs_pkcs12(context, plg_cryptoctx,4676req_cryptoctx, idopts,4677id_cryptoctx, princ);4678break;4679default:4680retval = EINVAL;4681}4682if (retval)4683goto cleanup;46844685cleanup:4686return retval;4687}46884689/*4690* Get certificate Key Usage and Extended Key Usage4691*/4692static krb5_error_code4693crypto_retrieve_X509_key_usage(krb5_context context,4694pkinit_plg_crypto_context plgcctx,4695pkinit_req_crypto_context reqcctx,4696X509 *x,4697unsigned int *ret_ku_bits,4698unsigned int *ret_eku_bits)4699{4700krb5_error_code retval = 0;4701int i;4702unsigned int eku_bits = 0, ku_bits = 0;4703ASN1_BIT_STRING *usage = NULL;47044705if (ret_ku_bits == NULL && ret_eku_bits == NULL)4706return EINVAL;47074708if (ret_eku_bits)4709*ret_eku_bits = 0;4710else {4711pkiDebug("%s: EKUs not requested, not checking\n", __FUNCTION__);4712goto check_kus;4713}47144715/* Start with Extended Key usage */4716i = X509_get_ext_by_NID(x, NID_ext_key_usage, -1);4717if (i >= 0) {4718EXTENDED_KEY_USAGE *eku;47194720eku = X509_get_ext_d2i(x, NID_ext_key_usage, NULL, NULL);4721if (eku) {4722for (i = 0; i < sk_ASN1_OBJECT_num(eku); i++) {4723ASN1_OBJECT *certoid;4724certoid = sk_ASN1_OBJECT_value(eku, i);4725if ((OBJ_cmp(certoid, plgcctx->id_pkinit_KPClientAuth)) == 0)4726eku_bits |= PKINIT_EKU_PKINIT;4727else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_ms_smartcard_login))) == 0)4728eku_bits |= PKINIT_EKU_MSSCLOGIN;4729else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_client_auth))) == 0)4730eku_bits |= PKINIT_EKU_CLIENTAUTH;4731else if ((OBJ_cmp(certoid, OBJ_nid2obj(NID_email_protect))) == 0)4732eku_bits |= PKINIT_EKU_EMAILPROTECTION;4733}4734EXTENDED_KEY_USAGE_free(eku);4735}4736}4737pkiDebug("%s: returning eku 0x%08x\n", __FUNCTION__, eku_bits);4738*ret_eku_bits = eku_bits;47394740check_kus:4741/* Now the Key Usage bits */4742if (ret_ku_bits)4743*ret_ku_bits = 0;4744else {4745pkiDebug("%s: KUs not requested, not checking\n", __FUNCTION__);4746goto out;4747}47484749/* Make sure usage exists before checking bits */4750X509_check_ca(x);4751usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL);4752if (usage) {4753if (!ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE))4754ku_bits |= PKINIT_KU_DIGITALSIGNATURE;4755if (!ku_reject(x, X509v3_KU_KEY_ENCIPHERMENT))4756ku_bits |= PKINIT_KU_KEYENCIPHERMENT;4757ASN1_BIT_STRING_free(usage);4758}47594760pkiDebug("%s: returning ku 0x%08x\n", __FUNCTION__, ku_bits);4761*ret_ku_bits = ku_bits;4762retval = 0;4763out:4764return retval;4765}47664767static krb5_error_code4768rfc2253_name(X509_NAME *name, char **str_out)4769{4770BIO *b = NULL;4771char *str;47724773*str_out = NULL;4774b = BIO_new(BIO_s_mem());4775if (b == NULL)4776return ENOMEM;4777if (X509_NAME_print_ex(b, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0)4778goto error;4779str = calloc(BIO_number_written(b) + 1, 1);4780if (str == NULL)4781goto error;4782BIO_read(b, str, BIO_number_written(b));4783BIO_free(b);4784*str_out = str;4785return 0;47864787error:4788BIO_free(b);4789return ENOMEM;4790}47914792/*4793* Get number of certificates available after crypto_load_certs()4794*/4795static krb5_error_code4796crypto_cert_get_count(pkinit_identity_crypto_context id_cryptoctx,4797size_t *cert_count)4798{4799size_t count;48004801*cert_count = 0;4802if (id_cryptoctx == NULL || id_cryptoctx->creds[0] == NULL)4803return EINVAL;48044805for (count = 0;4806count <= MAX_CREDS_ALLOWED && id_cryptoctx->creds[count] != NULL;4807count++);4808*cert_count = count;4809return 0;4810}48114812void4813crypto_cert_free_matching_data(krb5_context context,4814pkinit_cert_matching_data *md)4815{4816size_t i;48174818if (md == NULL)4819return;4820free(md->subject_dn);4821free(md->issuer_dn);4822for (i = 0; md->sans != NULL && md->sans[i] != NULL; i++)4823krb5_free_principal(context, md->sans[i]);4824free(md->sans);4825for (i = 0; md->upns != NULL && md->upns[i] != NULL; i++)4826free(md->upns[i]);4827free(md->upns);4828free(md);4829}48304831/*4832* Free certificate matching data.4833*/4834void4835crypto_cert_free_matching_data_list(krb5_context context,4836pkinit_cert_matching_data **list)4837{4838size_t i;48394840for (i = 0; list != NULL && list[i] != NULL; i++)4841crypto_cert_free_matching_data(context, list[i]);4842free(list);4843}48444845/*4846* Get certificate matching data for cert.4847*/4848static krb5_error_code4849get_matching_data(krb5_context context,4850pkinit_plg_crypto_context plg_cryptoctx,4851pkinit_req_crypto_context req_cryptoctx, X509 *cert,4852pkinit_cert_matching_data **md_out)4853{4854krb5_error_code ret = ENOMEM;4855pkinit_cert_matching_data *md = NULL;48564857*md_out = NULL;48584859md = calloc(1, sizeof(*md));4860if (md == NULL)4861goto cleanup;48624863ret = rfc2253_name(X509_get_subject_name(cert), &md->subject_dn);4864if (ret)4865goto cleanup;4866ret = rfc2253_name(X509_get_issuer_name(cert), &md->issuer_dn);4867if (ret)4868goto cleanup;48694870/* Get the SAN data. */4871ret = crypto_retrieve_X509_sans(context, plg_cryptoctx, req_cryptoctx,4872cert, &md->sans, &md->upns, NULL);4873if (ret)4874goto cleanup;48754876/* Get the KU and EKU data. */4877ret = crypto_retrieve_X509_key_usage(context, plg_cryptoctx,4878req_cryptoctx, cert, &md->ku_bits,4879&md->eku_bits);4880if (ret)4881goto cleanup;48824883*md_out = md;4884md = NULL;48854886cleanup:4887crypto_cert_free_matching_data(context, md);4888return ret;4889}48904891krb5_error_code4892crypto_cert_get_matching_data(krb5_context context,4893pkinit_plg_crypto_context plg_cryptoctx,4894pkinit_req_crypto_context req_cryptoctx,4895pkinit_identity_crypto_context id_cryptoctx,4896pkinit_cert_matching_data ***md_out)4897{4898krb5_error_code ret;4899pkinit_cert_matching_data **md_list = NULL;4900size_t count, i;49014902ret = crypto_cert_get_count(id_cryptoctx, &count);4903if (ret)4904goto cleanup;49054906md_list = calloc(count + 1, sizeof(*md_list));4907if (md_list == NULL) {4908ret = ENOMEM;4909goto cleanup;4910}49114912for (i = 0; i < count; i++) {4913ret = get_matching_data(context, plg_cryptoctx, req_cryptoctx,4914id_cryptoctx->creds[i]->cert, &md_list[i]);4915if (ret) {4916pkiDebug("%s: crypto_cert_get_matching_data error %d, %s\n",4917__FUNCTION__, ret, error_message(ret));4918goto cleanup;4919}4920}49214922*md_out = md_list;4923md_list = NULL;49244925cleanup:4926crypto_cert_free_matching_data_list(context, md_list);4927return ret;4928}49294930/*4931* Set the certificate in idctx->creds[cred_index] as the selected certificate,4932* stealing pointers from it.4933*/4934krb5_error_code4935crypto_cert_select(krb5_context context, pkinit_identity_crypto_context idctx,4936size_t cred_index)4937{4938pkinit_cred_info ci = NULL;49394940if (cred_index >= MAX_CREDS_ALLOWED || idctx->creds[cred_index] == NULL)4941return ENOENT;49424943ci = idctx->creds[cred_index];49444945idctx->my_cert = ci->cert;4946ci->cert = NULL;49474948/* hang on to the selected credential name */4949free(idctx->identity);4950if (ci->name != NULL)4951idctx->identity = strdup(ci->name);4952else4953idctx->identity = NULL;49544955if (idctx->pkcs11_method != 1) {4956idctx->my_key = ci->key;4957ci->key = NULL; /* Don't free it twice */4958}4959#ifndef WITHOUT_PKCS114960else {4961idctx->cert_id = ci->cert_id;4962ci->cert_id = NULL; /* Don't free it twice */4963idctx->cert_id_len = ci->cert_id_len;4964}4965#endif4966return 0;4967}49684969/*4970* Choose the default certificate as "the chosen one"4971*/4972krb5_error_code4973crypto_cert_select_default(krb5_context context,4974pkinit_plg_crypto_context plg_cryptoctx,4975pkinit_req_crypto_context req_cryptoctx,4976pkinit_identity_crypto_context id_cryptoctx)4977{4978krb5_error_code retval;4979size_t cert_count;49804981retval = crypto_cert_get_count(id_cryptoctx, &cert_count);4982if (retval)4983return retval;49844985if (cert_count != 1) {4986TRACE_PKINIT_NO_DEFAULT_CERT(context, cert_count);4987return EINVAL;4988}49894990return crypto_cert_select(context, id_cryptoctx, 0);4991}4992499349944995static krb5_error_code4996load_cas_and_crls(krb5_context context,4997pkinit_plg_crypto_context plg_cryptoctx,4998pkinit_req_crypto_context req_cryptoctx,4999pkinit_identity_crypto_context id_cryptoctx,5000int catype,5001char *filename)5002{5003STACK_OF(X509_INFO) *sk = NULL;5004STACK_OF(X509) *ca_certs = NULL;5005STACK_OF(X509_CRL) *ca_crls = NULL;5006BIO *in = NULL;5007krb5_error_code retval = ENOMEM;5008int i = 0;50095010/* If there isn't already a stack in the context,5011* create a temporary one now */5012switch(catype) {5013case CATYPE_ANCHORS:5014if (id_cryptoctx->trustedCAs != NULL)5015ca_certs = id_cryptoctx->trustedCAs;5016else {5017ca_certs = sk_X509_new_null();5018if (ca_certs == NULL)5019return ENOMEM;5020}5021break;5022case CATYPE_INTERMEDIATES:5023if (id_cryptoctx->intermediateCAs != NULL)5024ca_certs = id_cryptoctx->intermediateCAs;5025else {5026ca_certs = sk_X509_new_null();5027if (ca_certs == NULL)5028return ENOMEM;5029}5030break;5031case CATYPE_CRLS:5032if (id_cryptoctx->revoked != NULL)5033ca_crls = id_cryptoctx->revoked;5034else {5035ca_crls = sk_X509_CRL_new_null();5036if (ca_crls == NULL)5037return ENOMEM;5038}5039break;5040default:5041return ENOTSUP;5042}50435044if (!(in = BIO_new_file(filename, "r"))) {5045retval = oerr(context, 0, _("Cannot open file '%s'"), filename);5046goto cleanup;5047}50485049/* This loads from a file, a stack of x509/crl/pkey sets */5050if ((sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL) {5051pkiDebug("%s: error reading file '%s'\n", __FUNCTION__, filename);5052retval = oerr(context, 0, _("Cannot read file '%s'"), filename);5053goto cleanup;5054}50555056/* scan over the stack created from loading the file contents,5057* weed out duplicates, and push new ones onto the return stack5058*/5059for (i = 0; i < sk_X509_INFO_num(sk); i++) {5060X509_INFO *xi = sk_X509_INFO_value(sk, i);5061if (xi != NULL && xi->x509 != NULL && catype != CATYPE_CRLS) {5062int j = 0, size = sk_X509_num(ca_certs), flag = 0;50635064if (!size) {5065sk_X509_push(ca_certs, xi->x509);5066xi->x509 = NULL;5067continue;5068}5069for (j = 0; j < size; j++) {5070X509 *x = sk_X509_value(ca_certs, j);5071flag = X509_cmp(x, xi->x509);5072if (flag == 0)5073break;5074else5075continue;5076}5077if (flag != 0) {5078sk_X509_push(ca_certs, X509_dup(xi->x509));5079}5080} else if (xi != NULL && xi->crl != NULL && catype == CATYPE_CRLS) {5081int j = 0, size = sk_X509_CRL_num(ca_crls), flag = 0;5082if (!size) {5083sk_X509_CRL_push(ca_crls, xi->crl);5084xi->crl = NULL;5085continue;5086}5087for (j = 0; j < size; j++) {5088X509_CRL *x = sk_X509_CRL_value(ca_crls, j);5089flag = X509_CRL_cmp(x, xi->crl);5090if (flag == 0)5091break;5092else5093continue;5094}5095if (flag != 0) {5096sk_X509_CRL_push(ca_crls, X509_CRL_dup(xi->crl));5097}5098}5099}51005101/* If we added something and there wasn't a stack in the5102* context before, add the temporary stack to the context.5103*/5104switch(catype) {5105case CATYPE_ANCHORS:5106if (sk_X509_num(ca_certs) == 0) {5107TRACE_PKINIT_NO_CA_ANCHOR(context, filename);5108if (id_cryptoctx->trustedCAs == NULL)5109sk_X509_free(ca_certs);5110} else {5111if (id_cryptoctx->trustedCAs == NULL)5112id_cryptoctx->trustedCAs = ca_certs;5113}5114break;5115case CATYPE_INTERMEDIATES:5116if (sk_X509_num(ca_certs) == 0) {5117TRACE_PKINIT_NO_CA_INTERMEDIATE(context, filename);5118if (id_cryptoctx->intermediateCAs == NULL)5119sk_X509_free(ca_certs);5120} else {5121if (id_cryptoctx->intermediateCAs == NULL)5122id_cryptoctx->intermediateCAs = ca_certs;5123}5124break;5125case CATYPE_CRLS:5126if (sk_X509_CRL_num(ca_crls) == 0) {5127TRACE_PKINIT_NO_CRL(context, filename);5128if (id_cryptoctx->revoked == NULL)5129sk_X509_CRL_free(ca_crls);5130} else {5131if (id_cryptoctx->revoked == NULL)5132id_cryptoctx->revoked = ca_crls;5133}5134break;5135default:5136/* Should have been caught above! */5137retval = EINVAL;5138goto cleanup;5139break;5140}51415142retval = 0;51435144cleanup:5145if (in != NULL)5146BIO_free(in);5147if (sk != NULL)5148sk_X509_INFO_pop_free(sk, X509_INFO_free);51495150return retval;5151}51525153static krb5_error_code5154load_cas_and_crls_dir(krb5_context context,5155pkinit_plg_crypto_context plg_cryptoctx,5156pkinit_req_crypto_context req_cryptoctx,5157pkinit_identity_crypto_context id_cryptoctx,5158int catype,5159char *dirname)5160{5161krb5_error_code retval = EINVAL;5162char **fnames = NULL, *filename;5163int i;51645165if (dirname == NULL)5166return EINVAL;51675168retval = k5_dir_filenames(dirname, &fnames);5169if (retval)5170return retval;51715172for (i = 0; fnames[i] != NULL; i++) {5173/* Ignore anything starting with a dot */5174if (fnames[i][0] == '.')5175continue;51765177retval = k5_path_join(dirname, fnames[i], &filename);5178if (retval)5179goto cleanup;51805181retval = load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,5182id_cryptoctx, catype, filename);5183free(filename);5184if (retval)5185goto cleanup;5186}51875188retval = 0;51895190cleanup:5191k5_free_filenames(fnames);5192return retval;5193}51945195krb5_error_code5196crypto_load_cas_and_crls(krb5_context context,5197pkinit_plg_crypto_context plg_cryptoctx,5198pkinit_req_crypto_context req_cryptoctx,5199pkinit_identity_opts *idopts,5200pkinit_identity_crypto_context id_cryptoctx,5201int idtype,5202int catype,5203char *id)5204{5205switch (idtype) {5206case IDTYPE_FILE:5207TRACE_PKINIT_LOAD_FROM_FILE(context, id);5208return load_cas_and_crls(context, plg_cryptoctx, req_cryptoctx,5209id_cryptoctx, catype, id);5210break;5211case IDTYPE_DIR:5212TRACE_PKINIT_LOAD_FROM_DIR(context, id);5213return load_cas_and_crls_dir(context, plg_cryptoctx, req_cryptoctx,5214id_cryptoctx, catype, id);5215break;5216default:5217return ENOTSUP;5218break;5219}5220}52215222static krb5_error_code5223create_identifiers_from_stack(STACK_OF(X509) *sk,5224krb5_external_principal_identifier *** ids)5225{5226int i = 0, sk_size = sk_X509_num(sk);5227krb5_external_principal_identifier **krb5_cas = NULL;5228X509 *x = NULL;5229X509_NAME *xn = NULL;5230unsigned char *p = NULL;5231int len = 0;5232PKCS7_ISSUER_AND_SERIAL *is = NULL;5233char buf[DN_BUF_LEN];52345235*ids = NULL;52365237krb5_cas = calloc(sk_size + 1, sizeof(*krb5_cas));5238if (krb5_cas == NULL)5239return ENOMEM;52405241for (i = 0; i < sk_size; i++) {5242krb5_cas[i] = malloc(sizeof(krb5_external_principal_identifier));52435244x = sk_X509_value(sk, i);52455246X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf));5247pkiDebug("#%d cert= %s\n", i, buf);52485249/* fill-in subjectName */5250krb5_cas[i]->subjectName.magic = 0;5251krb5_cas[i]->subjectName.length = 0;5252krb5_cas[i]->subjectName.data = NULL;52535254xn = X509_get_subject_name(x);5255len = i2d_X509_NAME(xn, NULL);5256if ((p = malloc((size_t) len)) == NULL)5257goto oom;5258krb5_cas[i]->subjectName.data = (char *)p;5259i2d_X509_NAME(xn, &p);5260krb5_cas[i]->subjectName.length = len;52615262/* fill-in issuerAndSerialNumber */5263krb5_cas[i]->issuerAndSerialNumber.length = 0;5264krb5_cas[i]->issuerAndSerialNumber.magic = 0;5265krb5_cas[i]->issuerAndSerialNumber.data = NULL;52665267is = PKCS7_ISSUER_AND_SERIAL_new();5268if (is == NULL)5269goto oom;5270X509_NAME_set(&is->issuer, X509_get_issuer_name(x));5271ASN1_INTEGER_free(is->serial);5272is->serial = ASN1_INTEGER_dup(X509_get_serialNumber(x));5273if (is->serial == NULL)5274goto oom;5275len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);5276p = malloc(len);5277if (p == NULL)5278goto oom;5279krb5_cas[i]->issuerAndSerialNumber.data = (char *)p;5280i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);5281krb5_cas[i]->issuerAndSerialNumber.length = len;52825283/* fill-in subjectKeyIdentifier */5284krb5_cas[i]->subjectKeyIdentifier.length = 0;5285krb5_cas[i]->subjectKeyIdentifier.magic = 0;5286krb5_cas[i]->subjectKeyIdentifier.data = NULL;52875288if (X509_get_ext_by_NID(x, NID_subject_key_identifier, -1) >= 0) {5289ASN1_OCTET_STRING *ikeyid;52905291ikeyid = X509_get_ext_d2i(x, NID_subject_key_identifier, NULL,5292NULL);5293if (ikeyid != NULL) {5294len = i2d_ASN1_OCTET_STRING(ikeyid, NULL);5295p = malloc(len);5296if (p == NULL)5297goto oom;5298krb5_cas[i]->subjectKeyIdentifier.data = (char *)p;5299i2d_ASN1_OCTET_STRING(ikeyid, &p);5300krb5_cas[i]->subjectKeyIdentifier.length = len;5301ASN1_OCTET_STRING_free(ikeyid);5302}5303}5304PKCS7_ISSUER_AND_SERIAL_free(is);5305is = NULL;5306}53075308*ids = krb5_cas;5309return 0;53105311oom:5312free_krb5_external_principal_identifier(&krb5_cas);5313PKCS7_ISSUER_AND_SERIAL_free(is);5314return ENOMEM;5315}53165317static krb5_error_code5318create_krb5_invalidCertificates(krb5_context context,5319pkinit_plg_crypto_context plg_cryptoctx,5320pkinit_req_crypto_context req_cryptoctx,5321pkinit_identity_crypto_context id_cryptoctx,5322krb5_external_principal_identifier *** ids)5323{53245325krb5_error_code retval = ENOMEM;5326STACK_OF(X509) *sk = NULL;53275328*ids = NULL;5329if (req_cryptoctx->received_cert == NULL)5330return KRB5KDC_ERR_PREAUTH_FAILED;53315332sk = sk_X509_new_null();5333if (sk == NULL)5334goto cleanup;5335sk_X509_push(sk, req_cryptoctx->received_cert);53365337retval = create_identifiers_from_stack(sk, ids);53385339sk_X509_free(sk);5340cleanup:53415342return retval;5343}53445345krb5_error_code5346create_krb5_supportedCMSTypes(krb5_context context,5347pkinit_plg_crypto_context plg_cryptoctx,5348pkinit_req_crypto_context req_cryptoctx,5349pkinit_identity_crypto_context id_cryptoctx,5350krb5_algorithm_identifier ***algs_out)5351{5352krb5_error_code ret;5353krb5_algorithm_identifier **algs = NULL;5354size_t i, count;53555356*algs_out = NULL;53575358/* Count supported OIDs and allocate list (including null terminator). */5359for (count = 0; supported_cms_algs[count] != NULL; count++);5360algs = k5calloc(count + 1, sizeof(*algs), &ret);5361if (algs == NULL)5362goto cleanup;53635364/* Add an algorithm identifier for each OID, with no parameters. */5365for (i = 0; i < count; i++) {5366algs[i] = k5alloc(sizeof(*algs[i]), &ret);5367if (algs[i] == NULL)5368goto cleanup;5369ret = krb5int_copy_data_contents(context, supported_cms_algs[i],5370&algs[i]->algorithm);5371if (ret)5372goto cleanup;5373algs[i]->parameters = empty_data();5374}53755376*algs_out = algs;5377algs = NULL;53785379cleanup:5380free_krb5_algorithm_identifiers(&algs);5381return ret;5382}53835384krb5_error_code5385create_krb5_trustedCertifiers(krb5_context context,5386pkinit_plg_crypto_context plg_cryptoctx,5387pkinit_req_crypto_context req_cryptoctx,5388pkinit_identity_crypto_context id_cryptoctx,5389krb5_external_principal_identifier *** ids)5390{53915392krb5_error_code retval = ENOMEM;5393STACK_OF(X509) *sk = id_cryptoctx->trustedCAs;53945395*ids = NULL;5396if (id_cryptoctx->trustedCAs == NULL)5397return KRB5KDC_ERR_PREAUTH_FAILED;53985399retval = create_identifiers_from_stack(sk, ids);54005401return retval;5402}54035404krb5_error_code5405create_issuerAndSerial(krb5_context context,5406pkinit_plg_crypto_context plg_cryptoctx,5407pkinit_req_crypto_context req_cryptoctx,5408pkinit_identity_crypto_context id_cryptoctx,5409unsigned char **out,5410unsigned int *out_len)5411{5412unsigned char *p = NULL;5413PKCS7_ISSUER_AND_SERIAL *is = NULL;5414int len = 0;5415krb5_error_code retval = ENOMEM;5416X509 *cert = req_cryptoctx->received_cert;54175418*out = NULL;5419*out_len = 0;5420if (req_cryptoctx->received_cert == NULL)5421return 0;54225423is = PKCS7_ISSUER_AND_SERIAL_new();5424X509_NAME_set(&is->issuer, X509_get_issuer_name(cert));5425ASN1_INTEGER_free(is->serial);5426is->serial = ASN1_INTEGER_dup(X509_get_serialNumber(cert));5427len = i2d_PKCS7_ISSUER_AND_SERIAL(is, NULL);5428if ((p = *out = malloc((size_t) len)) == NULL)5429goto cleanup;5430i2d_PKCS7_ISSUER_AND_SERIAL(is, &p);5431*out_len = len;5432retval = 0;54335434cleanup:5435X509_NAME_free(is->issuer);5436ASN1_INTEGER_free(is->serial);5437free(is);54385439return retval;5440}54415442krb5_error_code5443pkinit_process_td_trusted_certifiers(5444krb5_context context,5445pkinit_plg_crypto_context plg_cryptoctx,5446pkinit_req_crypto_context req_cryptoctx,5447pkinit_identity_crypto_context id_cryptoctx,5448krb5_external_principal_identifier **krb5_trusted_certifiers,5449int td_type)5450{5451krb5_error_code retval = ENOMEM;5452STACK_OF(X509_NAME) *sk_xn = NULL;5453X509_NAME *xn = NULL;5454PKCS7_ISSUER_AND_SERIAL *is = NULL;5455ASN1_OCTET_STRING *id = NULL;5456const unsigned char *p = NULL;5457char buf[DN_BUF_LEN];5458size_t i = 0;54595460if (td_type == TD_TRUSTED_CERTIFIERS)5461pkiDebug("received trusted certifiers\n");5462else5463pkiDebug("received invalid certificate\n");54645465sk_xn = sk_X509_NAME_new_null();5466while(krb5_trusted_certifiers[i] != NULL) {5467if (krb5_trusted_certifiers[i]->subjectName.data != NULL) {5468p = (unsigned char *)krb5_trusted_certifiers[i]->subjectName.data;5469xn = d2i_X509_NAME(NULL, &p,5470(int)krb5_trusted_certifiers[i]->subjectName.length);5471if (xn == NULL)5472goto cleanup;5473X509_NAME_oneline(xn, buf, sizeof(buf));5474if (td_type == TD_TRUSTED_CERTIFIERS)5475pkiDebug("#%d cert = %s is trusted by kdc\n", i, buf);5476else5477pkiDebug("#%d cert = %s is invalid\n", i, buf);5478sk_X509_NAME_push(sk_xn, xn);5479}54805481if (krb5_trusted_certifiers[i]->issuerAndSerialNumber.data != NULL) {5482p = (unsigned char *)5483krb5_trusted_certifiers[i]->issuerAndSerialNumber.data;5484is = d2i_PKCS7_ISSUER_AND_SERIAL(NULL, &p,5485(int)krb5_trusted_certifiers[i]->issuerAndSerialNumber.length);5486if (is == NULL)5487goto cleanup;5488X509_NAME_oneline(is->issuer, buf, sizeof(buf));5489if (td_type == TD_TRUSTED_CERTIFIERS)5490pkiDebug("#%d issuer = %s serial = %ld is trusted bu kdc\n", i,5491buf, ASN1_INTEGER_get(is->serial));5492else5493pkiDebug("#%d issuer = %s serial = %ld is invalid\n", i, buf,5494ASN1_INTEGER_get(is->serial));5495PKCS7_ISSUER_AND_SERIAL_free(is);5496}54975498if (krb5_trusted_certifiers[i]->subjectKeyIdentifier.data != NULL) {5499p = (unsigned char *)5500krb5_trusted_certifiers[i]->subjectKeyIdentifier.data;5501id = d2i_ASN1_OCTET_STRING(NULL, &p,5502(int)krb5_trusted_certifiers[i]->subjectKeyIdentifier.length);5503if (id == NULL)5504goto cleanup;5505/* XXX */5506ASN1_OCTET_STRING_free(id);5507}5508i++;5509}5510/* XXX Since we not doing anything with received trusted certifiers5511* return an error. this is the place where we can pick a different5512* client certificate based on the information in td_trusted_certifiers5513*/5514retval = KRB5KDC_ERR_PREAUTH_FAILED;5515cleanup:5516if (sk_xn != NULL)5517sk_X509_NAME_pop_free(sk_xn, X509_NAME_free);55185519return retval;5520}55215522#ifdef DEBUG_DH5523static void5524print_dh(DH * dh, char *msg)5525{5526BIO *bio_err = NULL;55275528bio_err = BIO_new(BIO_s_file());5529BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);55305531if (msg)5532BIO_puts(bio_err, (const char *)msg);5533if (dh)5534DHparams_print(bio_err, dh);55355536BIO_puts(bio_err, "private key: ");5537BN_print(bio_err, dh->priv_key);5538BIO_puts(bio_err, (const char *)"\n");5539BIO_free(bio_err);55405541}55425543static void5544print_pubkey(BIGNUM * key, char *msg)5545{5546BIO *bio_err = NULL;55475548bio_err = BIO_new(BIO_s_file());5549BIO_set_fp(bio_err, stderr, BIO_NOCLOSE | BIO_FP_TEXT);55505551if (msg)5552BIO_puts(bio_err, (const char *)msg);5553if (key)5554BN_print(bio_err, key);5555BIO_puts(bio_err, "\n");55565557BIO_free(bio_err);55585559}5560#endif55615562#ifndef WITHOUT_PKCS115563static krb5_error_code5564p11err(krb5_context context, CK_RV rv, const char *op)5565{5566krb5_error_code code = KRB5KDC_ERR_PREAUTH_FAILED;5567size_t i;5568const char *msg;55695570for (i = 0; pkcs11_errstrings[i].text != NULL; i++) {5571if (pkcs11_errstrings[i].code == rv)5572break;5573}5574msg = pkcs11_errstrings[i].text;5575if (msg == NULL)5576msg = "unknown PKCS11 error";55775578krb5_set_error_message(context, code, _("PKCS11 error (%s): %s"), op, msg);5579return code;5580}5581#endif55825583/*5584* Add an item to the pkinit_identity_crypto_context's list of deferred5585* identities.5586*/5587krb5_error_code5588crypto_set_deferred_id(krb5_context context,5589pkinit_identity_crypto_context id_cryptoctx,5590const char *identity, const char *password)5591{5592unsigned long ck_flags;55935594ck_flags = pkinit_get_deferred_id_flags(id_cryptoctx->deferred_ids,5595identity);5596return pkinit_set_deferred_id(&id_cryptoctx->deferred_ids,5597identity, ck_flags, password);5598}55995600/*5601* Retrieve a read-only copy of the pkinit_identity_crypto_context's list of5602* deferred identities, sure to be valid only until the next time someone calls5603* either pkinit_set_deferred_id() or crypto_set_deferred_id().5604*/5605const pkinit_deferred_id *5606crypto_get_deferred_ids(krb5_context context,5607pkinit_identity_crypto_context id_cryptoctx)5608{5609pkinit_deferred_id *deferred;5610const pkinit_deferred_id *ret;56115612deferred = id_cryptoctx->deferred_ids;5613ret = (const pkinit_deferred_id *)deferred;5614return ret;5615}56165617/* Return the received certificate as DER-encoded data. */5618krb5_error_code5619crypto_encode_der_cert(krb5_context context, pkinit_req_crypto_context reqctx,5620uint8_t **der_out, size_t *der_len)5621{5622int len;5623unsigned char *der, *p;56245625*der_out = NULL;5626*der_len = 0;56275628if (reqctx->received_cert == NULL)5629return EINVAL;5630p = NULL;5631len = i2d_X509(reqctx->received_cert, NULL);5632if (len <= 0)5633return EINVAL;5634p = der = malloc(len);5635if (der == NULL)5636return ENOMEM;5637if (i2d_X509(reqctx->received_cert, &p) <= 0) {5638free(der);5639return EINVAL;5640}5641*der_out = der;5642*der_len = len;5643return 0;5644}56455646/*5647* Get the certificate matching data from the request certificate.5648*/5649krb5_error_code5650crypto_req_cert_matching_data(krb5_context context,5651pkinit_plg_crypto_context plgctx,5652pkinit_req_crypto_context reqctx,5653pkinit_cert_matching_data **md_out)5654{5655*md_out = NULL;56565657if (reqctx == NULL || reqctx->received_cert == NULL)5658return ENOENT;56595660return get_matching_data(context, plgctx, reqctx, reqctx->received_cert,5661md_out);5662}56635664/*5665* Historically, the strength of PKINIT key exchange has been determined by the5666* pkinit_dh_min_bits variable, which gives a finite field size. With the5667* addition of ECDH support, we allow the string values P-256, P-384, and P-5215668* for this config variable, represented with the rough equivalent bit5669* strengths for finite fields.5670*/5671int5672parse_dh_min_bits(krb5_context context, const char *str)5673{5674char *endptr;5675long n;56765677if (str == NULL)5678return PKINIT_DEFAULT_DH_MIN_BITS;56795680n = strtol(str, &endptr, 0);5681if (endptr == str) {5682if (strcasecmp(str, "P-256") == 0)5683return PKINIT_DH_P256_BITS;5684else if (strcasecmp(str, "P-384") == 0)5685return PKINIT_DH_P384_BITS;5686else if (strcasecmp(str, "P-521") == 0)5687return PKINIT_DH_P521_BITS;5688} else {5689if (n == 1024)5690return 1024;5691else if (n > 1024 && n <= 2048)5692return 2048;5693else if (n > 2048 && n <= 4096)5694return 4096;5695}56965697TRACE_PKINIT_DH_INVALID_MIN_BITS(context, str);5698return PKINIT_DEFAULT_DH_MIN_BITS;5699}57005701/* Return the OpenSSL message digest type matching the given CMS OID, or NULL5702* if it doesn't match any of the CMS OIDs we know about. */5703static const EVP_MD *5704md_from_cms_oid(const krb5_data *alg_id)5705{5706if (data_eq(*alg_id, cms_sha1_id))5707return EVP_sha1();5708if (data_eq(*alg_id, cms_sha256_id))5709return EVP_sha256();5710if (data_eq(*alg_id, cms_sha384_id))5711return EVP_sha384();5712if (data_eq(*alg_id, cms_sha512_id))5713return EVP_sha512();5714return NULL;5715}57165717/* Compute a message digest of the given type over body, placing the result in5718* *digest_out in allocated storage. Return true on success. */5719static krb5_boolean5720make_digest(const krb5_data *body, const EVP_MD *md, krb5_data *digest_out)5721{5722krb5_error_code ret;5723krb5_data d;57245725if (md == NULL)5726return FALSE;5727ret = alloc_data(&d, EVP_MD_size(md));5728if (ret)5729return FALSE;5730if (!EVP_Digest(body->data, body->length, (uint8_t *)d.data, &d.length, md,5731NULL)) {5732free(d.data);5733return FALSE;5734}5735*digest_out = d;5736return TRUE;5737}57385739/* Return true if digest verifies for the given body and message digest5740* type. */5741static krb5_boolean5742check_digest(const krb5_data *body, const EVP_MD *md, const krb5_data *digest)5743{5744unsigned int digest_len;5745uint8_t buf[EVP_MAX_MD_SIZE];57465747if (md == NULL)5748return FALSE;5749if (!EVP_Digest(body->data, body->length, buf, &digest_len, md, NULL))5750return FALSE;5751return (digest->length == digest_len &&5752CRYPTO_memcmp(digest->data, buf, digest_len) == 0);5753}57545755krb5_error_code5756crypto_generate_checksums(krb5_context context, const krb5_data *body,5757krb5_data *cksum1_out, krb5_pachecksum2 **cksum2_out)5758{5759krb5_data cksum1 = empty_data();5760krb5_pachecksum2 *cksum2 = NULL;5761krb5_error_code ret;57625763if (!make_digest(body, EVP_sha1(), &cksum1))5764goto fail;57655766cksum2 = k5alloc(sizeof(*cksum2), &ret);5767if (cksum2 == NULL)5768goto fail;57695770if (!make_digest(body, EVP_sha256(), &cksum2->checksum))5771goto fail;57725773if (krb5int_copy_data_contents(context, &cms_sha256_id,5774&cksum2->algorithmIdentifier.algorithm))5775goto fail;57765777cksum2->algorithmIdentifier.parameters = empty_data();57785779*cksum1_out = cksum1;5780*cksum2_out = cksum2;5781return 0;57825783fail:5784krb5_free_data_contents(context, &cksum1);5785free_pachecksum2(context, &cksum2);5786return KRB5_CRYPTO_INTERNAL;5787}57885789krb5_error_code5790crypto_verify_checksums(krb5_context context, krb5_data *body,5791const krb5_data *cksum1,5792const krb5_pachecksum2 *cksum2)5793{5794const EVP_MD *md;57955796/* RFC 4556 doesn't say what error to return if the checksum doesn't match.5797* Windows returns this one. */5798if (!check_digest(body, EVP_sha1(), cksum1))5799return KRB5KRB_AP_ERR_MODIFIED;58005801if (cksum2 == NULL)5802return 0;58035804md = md_from_cms_oid(&cksum2->algorithmIdentifier.algorithm);5805if (!check_digest(body, md, &cksum2->checksum))5806return KRB5KRB_AP_ERR_MODIFIED;58075808return 0;5809}58105811#ifdef _WIN325812BOOL WINAPI5813DllMain(HANDLE hModule, DWORD fdwReason, LPVOID lpvReserved)5814{5815if (fdwReason == DLL_PROCESS_ATTACH)5816pkinit_openssl_init__auxinit();5817return TRUE;5818}5819#endif /* _WIN32 */582058215822