/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/openssl/app.c
2 103 строки
65 KB
lsh123
(xmlsec-openssl) Add OPENSSL_cleanup to make valgrind happy#1133
15 апр 2026, 16:12
Не верифицирован
15 апр 2026, 16:12
d164f69
Код
Авторство
О чём код?
/** * XML Security Library (http://www.aleksey.com/xmlsec). * * This is free software; see the Copyright file in the source distribution for precise wording. * * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_openssl_app * @brief Application support functions for OpenSSL. * @details Common functions for the xmlsec1 command-line utility for OpenSSL. */ #include "globals.h" #include <string.h> #include <stdlib.h> #include <stdio.h> #include <xmlsec/xmlsec.h> #include <xmlsec/keys.h> #include <xmlsec/transforms.h> #include <xmlsec/private.h> #include <xmlsec/errors.h> #include <xmlsec/openssl/app.h> #include <xmlsec/openssl/crypto.h> #include <xmlsec/openssl/evp.h> #include <xmlsec/openssl/keysstore.h> #include <xmlsec/openssl/x509.h> #include "openssl_compat.h" /* Windows overwrites X509_NAME and other things that break openssl */ #include <openssl/evp.h> #include <openssl/rand.h> #include <openssl/pem.h> #include <openssl/pkcs12.h> #include <openssl/conf.h> #include <openssl/x509_vfy.h> #include <openssl/x509.h> #if !defined(OPENSSL_NO_ENGINE) && (!defined(XMLSEC_OPENSSL_API_300) || defined(XMLSEC_OPENSSL3_ENGINES)) #include <openssl/engine.h> #endif /* !defined(OPENSSL_NO_ENGINE) && (!defined(XMLSEC_OPENSSL_API_300) || defined(XMLSEC_OPENSSL3_ENGINES)) */ #if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) #include <openssl/ui.h> #endif /* OPENSSL_IS_BORINGSSL */ #ifndef XMLSEC_OPENSSL_NO_STORE #include <openssl/store.h> #endif /* XMLSEC_OPENSSL_NO_STORE */ #ifdef XMLSEC_OPENSSL_API_300 #include <openssl/provider.h> #endif /* XMLSEC_OPENSSL_API_300 */ #include "../cast_helpers.h" #include "private.h" static int xmlSecOpenSSLDefaultPasswordCallback (char *buf, int bufsiz, int verify, void *userdata); static int xmlSecOpenSSLDummyPasswordCallback (char *buf, int buflen, int verify, void *userdata); static xmlSecKeyPtr xmlSecOpenSSLAppEngineKeyLoad (const char *engineName, const char *engineKeyId, xmlSecKeyDataType type, xmlSecKeyDataFormat format, const char *pwd, void* pwdCallback, void* pwdCallbackCtx); static xmlSecKeyPtr xmlSecOpenSSLAppStoreKeyLoad (const char *uri, xmlSecKeyDataType type, const char *pwd, void* pwdCallback, void* pwdCallbackCtx); /* conversion from ptr to func "the right way" */ XMLSEC_PTR_TO_FUNC_IMPL(pem_password_cb) XMLSEC_FUNC_TO_PTR_IMPL(pem_password_cb) /* helpers to overwrite global context temporarily for OpenSSL 3.0 */ #ifdef XMLSEC_OPENSSL_API_300 #define XMLSEC_OPENSSL_PUSH_LIB_CTX(on_error) \ { \ OSSL_LIB_CTX* savedDefaultLibCtx = NULL; \ savedDefaultLibCtx = OSSL_LIB_CTX_set0_default(xmlSecOpenSSLGetLibCtx()); \ if(savedDefaultLibCtx == NULL) { \ xmlSecOpenSSLError("OSSL_LIB_CTX_set0_default", NULL); \ on_error; \ } #define XMLSEC_OPENSSL_POP_LIB_CTX() \ if(savedDefaultLibCtx != NULL) { \ OSSL_LIB_CTX_set0_default(savedDefaultLibCtx); \ } \ } #else /* XMLSEC_OPENSSL_API_300 */ /* noop */ #define XMLSEC_OPENSSL_PUSH_LIB_CTX(on_error) #define XMLSEC_OPENSSL_POP_LIB_CTX() #endif /* XMLSEC_OPENSSL_API_300 */ /** * @brief Initializes the OpenSSL crypto engine. * @details General crypto engine initialization. This function is used * by the XMLSec command-line utility and is called before the * #xmlSecInit function. * * @param config the path to certs. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppInit(const char* config) { #ifdef XMLSEC_OPENSSL_API_300 /* This code can be used to check that custom xmlsec LibCtx is propagated everywhere as expected (see https://github.com/lsh123/xmlsec/issues/346) */ /* OSSL_LIB_CTX * libCtx = OSSL_LIB_CTX_new(); OSSL_PROVIDER * legacyProvider = OSSL_PROVIDER_load(libCtx, "legacy"); OSSL_PROVIDER * defaultProvider = OSSL_PROVIDER_load(libCtx, "default"); if(!libCtx || !legacyProvider || !defaultProvider) { xmlSecOpenSSLError("OSSL_LIB_CTX_new or OSSL_PROVIDER_load", NULL); goto error; } xmlSecOpenSSLSetLibCtx(libCtx); */ #endif /* XMLSEC_OPENSSL_API_300 */ int ret; uint64_t opts = 0; opts |= OPENSSL_INIT_LOAD_CRYPTO_STRINGS; opts |= OPENSSL_INIT_ADD_ALL_CIPHERS; opts |= OPENSSL_INIT_ADD_ALL_DIGESTS; opts |= OPENSSL_INIT_LOAD_CONFIG; #if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) opts |= OPENSSL_INIT_ASYNC; #endif /* !defined(OPENSSL_IS_BORINGSSL) */ #if !defined(OPENSSL_IS_BORINGSSL) && !defined(XMLSEC_OPENSSL_API_300) && !defined(OPENSSL_IS_AWSLC) opts |= OPENSSL_INIT_ENGINE_ALL_BUILTIN; #endif /* !defined(OPENSSL_IS_BORINGSSL) && !defined(XMLSEC_OPENSSL_API_300) */ ret = OPENSSL_init_crypto(opts, NULL); if(ret != 1) { xmlSecOpenSSLError("OPENSSL_init_crypto", NULL); goto error; } if((config != NULL) && (xmlSecOpenSSLSetDefaultTrustedCertsFolder(BAD_CAST config) < 0)) { xmlSecInternalError("xmlSecOpenSSLSetDefaultTrustedCertsFolder", NULL); goto error; } /* done! */ return(0); error: /* cleanup */ return(-1); } /** * @brief Shuts down the OpenSSL crypto engine. * @details General crypto engine shutdown. This function is used * by the XMLSec command-line utility and is called after the * #xmlSecShutdown function. * * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppShutdown(void) { /* debug only feature, should not be used in production, on windows OpenSSL is not happy about FILE* */ #if !defined(_MSC_VER) if(xmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled() == 1) { ERR_print_errors_fp(stderr); } #endif /* _MSC_VER */ /* OpenSSL 1.1.0+ does not require explicit cleanup but this fixes valgrind errors */ OPENSSL_cleanup(); /* done */ return(0); } /** * @brief Reads a key from a file. * @param filename the key filename. * @param type the expected key type. * @param format the key file format. * @param pwd the key file password. * @param pwdCallback the key password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppKeyLoadEx(const char *filename, xmlSecKeyDataType type, xmlSecKeyDataFormat format, const char *pwd, void* pwdCallback, void* pwdCallbackCtx ) { xmlSecKeyPtr key = NULL; xmlSecAssert2(filename != NULL, NULL); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL); if(format == xmlSecKeyDataFormatEngine) { char* buffer = NULL; char* engineName; char* engineKeyId; /* for loading key from an engine, the filename format is: * <openssl-engine>;<openssl-key-id> */ buffer = (char*)xmlStrdup(BAD_CAST filename); if(buffer == NULL) { xmlSecStrdupError(BAD_CAST filename, NULL); return(NULL); } engineName = buffer; engineKeyId = strchr(buffer, ';'); if(engineKeyId == NULL) { xmlSecInvalidStringDataError("openssl-engine-and-key", buffer, "<openssl-engine>;<openssl-key-id>", NULL); xmlFree(buffer); return(NULL); } (*engineKeyId) = '\0'; ++engineKeyId; key = xmlSecOpenSSLAppEngineKeyLoad(engineName, engineKeyId, type, format, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError2("xmlSecOpenSSLAppEngineKeyLoad", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); xmlFree(buffer); return(NULL); } xmlFree(buffer); } else if(format == xmlSecKeyDataFormatStore) { key = xmlSecOpenSSLAppStoreKeyLoad(filename, type, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError2("xmlSecOpenSSLAppStoreKeyLoad", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(NULL); } } else { BIO* bio; bio = xmlSecOpenSSLCreateReadFileBio(filename); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateReadFileBio", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(NULL); } key = xmlSecOpenSSLAppKeyLoadBIO (bio, format, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError2("xmlSecOpenSSLAppKeyLoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); BIO_free_all(bio); return(NULL); } BIO_free_all(bio); } return(key); } /** * @brief Reads a key from the memory buffer. * @param data the binary key data. * @param dataSize the size of binary key. * @param format the key file format. * @param pwd the key file password. * @param pwdCallback the key password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppKeyLoadMemory(const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format, const char *pwd, void* pwdCallback, void* pwdCallbackCtx) { BIO* bio; xmlSecKeyPtr key; xmlSecAssert2(data != NULL, NULL); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL); /* this would be a read only BIO, cast from const is ok */ bio = xmlSecOpenSSLCreateMemBufBio((void*)data, dataSize); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateMemBufBio", NULL, "dataSize=" XMLSEC_SIZE_FMT, dataSize); return(NULL); } key = xmlSecOpenSSLAppKeyLoadBIO (bio, format, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError("xmlSecOpenSSLAppKeyLoadBIO", NULL); BIO_free_all(bio); return(NULL); } BIO_free_all(bio); return(key); } /** * @brief Reads a key from an OpenSSL BIO object. * @param bio the key BIO. * @param format the key file format. * @param pwd the key file password. * @param pwdCallback the key password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppKeyLoadBIO(BIO* bio, xmlSecKeyDataFormat format, const char *pwd, void* pwdCallback, void* pwdCallbackCtx) { xmlSecKeyPtr key = NULL; xmlSecKeyDataPtr data; EVP_PKEY* pKey = NULL; pem_password_cb* pwdCb = NULL; void* pwdCbCtx = NULL; int ret; xmlSecAssert2(bio != NULL, NULL); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL); /* prep pwd callbacks */ if(pwd != NULL) { pwdCb = xmlSecOpenSSLDummyPasswordCallback; pwdCbCtx = (void*)pwd; } else { pwdCb = XMLSEC_PTR_TO_FUNC(pem_password_cb, pwdCallback); pwdCbCtx = pwdCallbackCtx; } switch(format) { case xmlSecKeyDataFormatPem: /* try to read private key first; if can't read private key then reset bio to the start of the file and try to read public key. */ pKey = PEM_read_bio_PrivateKey_ex(bio, NULL, pwdCb, pwdCbCtx, xmlSecOpenSSLGetLibCtx(), NULL); if(pKey == NULL) { (void)BIO_reset(bio); pKey = PEM_read_bio_PUBKEY_ex(bio, NULL, pwdCb, pwdCbCtx, xmlSecOpenSSLGetLibCtx(), NULL); } if(pKey == NULL) { xmlSecOpenSSLError("PEM_read_bio_PrivateKey and PEM_read_bio_PUBKEY", NULL); return(NULL); } break; case xmlSecKeyDataFormatDer: /* try to read private key first; if can't read private key then reset bio to the start of the file and try to read public key. */ pKey = d2i_PrivateKey_ex_bio(bio, NULL, xmlSecOpenSSLGetLibCtx(), NULL); if(pKey == NULL) { (void)BIO_reset(bio); XMLSEC_OPENSSL_PUSH_LIB_CTX(return(NULL)); pKey = d2i_PUBKEY_bio(bio, NULL); XMLSEC_OPENSSL_POP_LIB_CTX(); } if(pKey == NULL) { xmlSecOpenSSLError("d2i_PrivateKey_bio and d2i_PUBKEY_bio", NULL); return(NULL); } break; case xmlSecKeyDataFormatPkcs8Pem: /* read private key */ pKey = PEM_read_bio_PrivateKey_ex(bio, NULL, pwdCb, pwdCbCtx, xmlSecOpenSSLGetLibCtx(), NULL); if(pKey == NULL) { xmlSecOpenSSLError("PEM_read_bio_PrivateKey", NULL); return(NULL); } break; case xmlSecKeyDataFormatPkcs8Der: /* read private key */ XMLSEC_OPENSSL_PUSH_LIB_CTX(return(NULL)); pKey = d2i_PKCS8PrivateKey_bio(bio, NULL, pwdCb, pwdCbCtx); XMLSEC_OPENSSL_POP_LIB_CTX(); if(pKey == NULL) { xmlSecOpenSSLError("d2i_PKCS8PrivateKey_bio", NULL); return(NULL); } break; #ifndef XMLSEC_NO_X509 case xmlSecKeyDataFormatPkcs12: key = xmlSecOpenSSLAppPkcs12LoadBIO(bio, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError("xmlSecOpenSSLAppPkcs12LoadBIO", NULL); return(NULL); } return(key); case xmlSecKeyDataFormatCertPem: case xmlSecKeyDataFormatCertDer: key = xmlSecOpenSSLAppKeyFromCertLoadBIO(bio, format); if(key == NULL) { xmlSecInternalError("xmlSecOpenSSLAppKeyFromCertLoadBIO", NULL); return(NULL); } return(key); #endif /* XMLSEC_NO_X509 */ default: xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_FORMAT, NULL, "format=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(format)); return(NULL); } data = xmlSecOpenSSLEvpKeyAdopt(pKey); if(data == NULL) { xmlSecInternalError("xmlSecOpenSSLEvpKeyAdopt", NULL); EVP_PKEY_free(pKey); return(NULL); } key = xmlSecKeyCreate(); if(key == NULL) { xmlSecInternalError("xmlSecKeyCreate", xmlSecKeyDataGetName(data)); xmlSecKeyDataDestroy(data); return(NULL); } ret = xmlSecKeySetValue(key, data); if(ret < 0) { xmlSecInternalError("xmlSecKeySetValue", xmlSecKeyDataGetName(data)); xmlSecKeyDestroy(key); xmlSecKeyDataDestroy(data); return(NULL); } return(key); } static xmlSecKeyPtr xmlSecOpenSSLAppEngineKeyLoad(const char *engineName, const char *engineKeyId, xmlSecKeyDataType type, xmlSecKeyDataFormat format, const char *pwd, void* pwdCallback, void* pwdCallbackCtx ) { #if !defined(OPENSSL_NO_ENGINE) && (!defined(XMLSEC_OPENSSL_API_300) || defined(XMLSEC_OPENSSL3_ENGINES)) UI_METHOD * ui_method = NULL; void * pwdCbCtx; #ifndef XMLSEC_OPENSSL_NO_PWD_CALLBACK pem_password_cb * pwdCb; #endif /* XMLSEC_OPENSSL_NO_PWD_CALLBACK */ ENGINE* engine = NULL; xmlSecKeyPtr key = NULL; xmlSecKeyDataPtr data = NULL; EVP_PKEY* pKey = NULL; int engineInit = 0; int ret; xmlSecAssert2(engineName != NULL, NULL); xmlSecAssert2(engineKeyId != NULL, NULL); xmlSecAssert2(format == xmlSecKeyDataFormatEngine, NULL); #ifndef XMLSEC_OPENSSL_NO_PWD_CALLBACK /* prep pwd callbacks */ if(pwd != NULL) { pwdCb = xmlSecOpenSSLDummyPasswordCallback; pwdCbCtx = (void*)pwd; } else { pwdCb = XMLSEC_PTR_TO_FUNC(pem_password_cb, pwdCallback); pwdCbCtx = pwdCallbackCtx; } ui_method = UI_UTIL_wrap_read_pem_callback(pwdCb, 0); if(ui_method == NULL) { xmlSecOpenSSLError("UI_UTIL_wrap_read_pem_callback", NULL); goto done; } #else /* XMLSEC_OPENSSL_NO_PWD_CALLBACK */ UNREFERENCED_PARAMETER(pwd); UNREFERENCED_PARAMETER(pwdCallback); UNREFERENCED_PARAMETER(pwdCallbackCtx); ui_method = UI_OpenSSL(); if(ui_method == NULL) { xmlSecOpenSSLError("UI_OpenSSL", NULL); goto done; } pwdCbCtx = NULL; #endif /* XMLSEC_OPENSSL_NO_PWD_CALLBACK */ /* load and initialize the engine */ engine = ENGINE_by_id(engineName); if(engine == NULL) { engine = ENGINE_by_id("dynamic"); if(engine != NULL) { if(ENGINE_ctrl_cmd_string(engine, "SO_PATH", engineName, 0) <= 0) { xmlSecOpenSSLError("ENGINE_ctrl_cmd_string(SO_PATH)", NULL); goto done; } if(ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0) <= 0) { xmlSecOpenSSLError("ENGINE_ctrl_cmd_string(LOAD)", NULL); goto done; } } } if(ENGINE_ctrl_cmd(engine, "SET_USER_INTERFACE", 0, (void *)UI_null(), 0, 1) < 0) { xmlSecOpenSSLError("ENGINE_ctrl_cmd_string(SET_USER_INTERFACE)", NULL); goto done; } if(!ENGINE_set_default(engine, ENGINE_METHOD_ALL)) { xmlSecOpenSSLError("ENGINE_set_default", NULL); goto done; } if(!ENGINE_init(engine)) { xmlSecOpenSSLError("ENGINE_init", NULL); goto done; } engineInit = 1; /* load private or public key */ if(type == xmlSecKeyDataTypeUnknown) { /* try both */ pKey = ENGINE_load_private_key(engine, engineKeyId, ui_method, pwdCbCtx); if(pKey == NULL) { pKey = ENGINE_load_public_key(engine, engineKeyId, ui_method, pwdCbCtx); if(pKey == NULL) { xmlSecOpenSSLError("ENGINE_load_private_key and ENGINE_load_public_key", NULL); goto done; } } } else if(type == xmlSecKeyDataTypePrivate) { pKey = ENGINE_load_private_key(engine, engineKeyId, ui_method, pwdCbCtx); if(pKey == NULL) { xmlSecOpenSSLError("ENGINE_load_private_key", NULL); goto done; } } else { pKey = ENGINE_load_public_key(engine, engineKeyId, ui_method, pwdCbCtx); if(pKey == NULL) { xmlSecOpenSSLError("ENGINE_load_public_key", NULL); goto done; } } /* create xmlsec key */ data = xmlSecOpenSSLEvpKeyAdopt(pKey); if(data == NULL) { xmlSecInternalError("xmlSecOpenSSLEvpKeyAdopt", NULL); goto done; } pKey = NULL; key = xmlSecKeyCreate(); if(key == NULL) { xmlSecInternalError("xmlSecKeyCreate", xmlSecKeyDataGetName(data)); goto done; } ret = xmlSecKeySetValue(key, data); if(ret < 0) { xmlSecInternalError("xmlSecKeySetValue", xmlSecKeyDataGetName(data)); xmlSecKeyDestroy(key); key = NULL; goto done; } data = NULL; done: /* cleanup */ if(pKey != NULL) { EVP_PKEY_free(pKey); } if(data != NULL) { xmlSecKeyDataDestroy(data); } if(engine !=NULL) { if(engineInit != 0) { ENGINE_finish(engine); } ENGINE_free(engine); } if((ui_method != NULL) && (ui_method != UI_OpenSSL())) { UI_destroy_method(ui_method); } /* result */ return(key); #else /* !defined(OPENSSL_NO_ENGINE) && (!defined(XMLSEC_OPENSSL_API_300) || defined(XMLSEC_OPENSSL3_ENGINES)) */ UNREFERENCED_PARAMETER(engineName); UNREFERENCED_PARAMETER(engineKeyId); UNREFERENCED_PARAMETER(type); UNREFERENCED_PARAMETER(format); UNREFERENCED_PARAMETER(pwd); UNREFERENCED_PARAMETER(pwdCallback); UNREFERENCED_PARAMETER(pwdCallbackCtx); xmlSecNotImplementedError("OpenSSL Engine interface is disabled during compilation"); return (NULL); #endif /* !defined(OPENSSL_NO_ENGINE) && (!defined(XMLSEC_OPENSSL_API_300) || defined(XMLSEC_OPENSSL3_ENGINES)) */ } #ifndef XMLSEC_NO_X509 /* this function will set input parameters to NULL if it adopts them */ static xmlSecKeyPtr xmlSecOpenSSLCreateKey(EVP_PKEY ** pKey, X509 ** keyCert, STACK_OF(X509) ** certs) { xmlSecKeyDataPtr keyValue = NULL; xmlSecKeyPtr key = NULL; xmlSecKeyPtr res = NULL; int ret; xmlSecAssert2(pKey != NULL, NULL); xmlSecAssert2((*pKey) != NULL, NULL); xmlSecAssert2(keyCert != NULL, NULL); xmlSecAssert2(certs != NULL, NULL); key = xmlSecKeyCreate(); if(key == NULL) { xmlSecInternalError("xmlSecKeyCreate", NULL); goto done; } /* create key value from pKey */ keyValue = xmlSecOpenSSLEvpKeyAdopt(*pKey); if(keyValue == NULL) { xmlSecInternalError("xmlSecOpenSSLEvpKeyAdopt", NULL); goto done; } (*pKey) = NULL; /* owned by keyValue now */ ret = xmlSecKeySetValue(key, keyValue); if(ret < 0) { xmlSecInternalError("xmlSecKeySetValue", NULL); goto done; } keyValue = NULL; /* owned by key now */ /* try to get key name from x509 cert */ if((*keyCert) != NULL) { const unsigned char * name = NULL; int nameLen = 0; if(name == NULL) { name = X509_alias_get0((*keyCert), &nameLen); } if(name == NULL) { name = X509_keyid_get0((*keyCert), &nameLen); } if((name != NULL) && (nameLen > 0)) { xmlSecSize nameSize = 0; XMLSEC_SAFE_CAST_INT_TO_SIZE(nameLen, nameSize, goto done, NULL); ret = xmlSecKeySetNameEx(key, name, nameSize); if(ret < 0) { xmlSecInternalError("xmlSecKeySetNameEx", NULL); goto done; } } } /* create x509 data for key */ if(((*keyCert) != NULL) || ((*certs) != NULL)) { xmlSecKeyDataPtr x509Data; x509Data = xmlSecKeyEnsureData(key, xmlSecOpenSSLKeyDataX509Id); if(x509Data == NULL) { xmlSecInternalError("xmlSecKeyEnsureData(xmlSecOpenSSLKeyDataX509Id)", NULL); goto done; } if((*keyCert) != NULL) { ret = xmlSecOpenSSLKeyDataX509AdoptKeyCert(x509Data, (*keyCert)); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyDataX509AdoptKeyCert", NULL); goto done; } (*keyCert) = NULL; /* owned by x509Data now */ } if((*certs) != NULL) { while(sk_X509_num((*certs)) > 0) { X509* cert = sk_X509_pop((*certs)); if(cert == NULL) { continue; } /* don't bother to check against keyCert, the xmlSecOpenSSLKeyDataX509AdoptCert does * it automatically */ ret = xmlSecOpenSSLKeyDataX509AdoptCert(x509Data, cert); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyDataX509AdoptCert", NULL); X509_free(cert); goto done; } cert = NULL; /* owned by x509Data now */ } } } /* success */ res = key; key = NULL; done: if(keyValue != NULL) { xmlSecKeyDataDestroy(keyValue); } if(key != NULL) { xmlSecKeyDestroy(key); } return(res); } /* returns 1 if matches, 0 if not, or a negative value on error */ static int xmlSecOpenSSLAppCheckCertMatchesKey(EVP_PKEY * pKey, X509 * cert) { EVP_PKEY * certKey; xmlSecAssert2(pKey != NULL, -1); xmlSecAssert2(cert != NULL, -1); certKey = X509_get0_pubkey(cert); if(certKey == NULL) { return(-1); } #ifndef XMLSEC_OPENSSL_API_300 if(EVP_PKEY_cmp(pKey, certKey) != 1) { return(0); } #else /* XMLSEC_OPENSSL_API_300 */ if(EVP_PKEY_eq(pKey, certKey) != 1) { return(0); } #endif /* XMLSEC_OPENSSL_API_300 */ /* matches! */ return(1); } #if !defined(XMLSEC_OPENSSL_NO_STORE) static X509 * xmlSecOpenSSLAppFindKeyCert(EVP_PKEY * pKey, STACK_OF(X509) * certs) { X509 * cert; xmlSecOpenSSLSizeT ii, size; int ret; xmlSecAssert2(pKey != NULL, NULL); xmlSecAssert2(certs != NULL, NULL); /* try find key cert */ for(ii = 0, size = sk_X509_num(certs); ii < size; ++ii) { cert = sk_X509_value(certs, ii); if(cert == NULL) { continue; } /* ignore errors, only care if cert matches */ ret = xmlSecOpenSSLAppCheckCertMatchesKey(pKey, cert); if(ret != 1) { continue; } /* found it! */ if(X509_up_ref(cert) != 1) { return(NULL); } return(cert); } /* not found */ return(NULL); } #endif /* !defined(XMLSEC_OPENSSL_NO_STORE) */ #endif /* XMLSEC_NO_X509 */ static xmlSecKeyPtr xmlSecOpenSSLAppStoreKeyLoad(const char *uri, xmlSecKeyDataType type, const char *pwd, void* pwdCallback, void* pwdCallbackCtx) { #if !defined(XMLSEC_OPENSSL_NO_STORE) && !defined(XMLSEC_NO_X509) UI_METHOD * ui_method = NULL; pem_password_cb * pwdCb; void * pwdCbCtx; OSSL_STORE_CTX * storeCtx = NULL; OSSL_STORE_INFO * info = NULL; STACK_OF(X509) * certs = NULL; X509 * cert = NULL; X509 * keyCert = NULL; EVP_PKEY * pKey = NULL; EVP_PKEY * pPrivKey = NULL; EVP_PKEY * pPubKey = NULL; xmlSecKeyPtr res = NULL; int ret; xmlSecAssert2(uri != NULL, NULL); /* prep pwd callbacks */ if(pwd != NULL) { pwdCb = xmlSecOpenSSLDummyPasswordCallback; pwdCbCtx = (void*)pwd; } else { pwdCb = XMLSEC_PTR_TO_FUNC(pem_password_cb, pwdCallback); pwdCbCtx = pwdCallbackCtx; } ui_method = UI_UTIL_wrap_read_pem_callback(pwdCb, 0); if(ui_method == NULL) { xmlSecOpenSSLError("UI_UTIL_wrap_read_pem_callback", NULL); goto done; } #if !defined(XMLSEC_OPENSSL_API_300) storeCtx = OSSL_STORE_open(uri, ui_method, pwdCbCtx, NULL, NULL); if(storeCtx == NULL) { xmlSecOpenSSLError2("OSSL_STORE_open", NULL, "uri=%s", xmlSecErrorsSafeString(uri)); goto done; } #else /* !defined(XMLSEC_OPENSSL_API_300) */ storeCtx = OSSL_STORE_open_ex(uri, xmlSecOpenSSLGetLibCtx(), NULL, ui_method, pwdCbCtx, NULL, NULL, NULL); if(storeCtx == NULL) { xmlSecOpenSSLError2("OSSL_STORE_open_ex", NULL, "uri=%s", xmlSecErrorsSafeString(uri)); goto done; } #endif /* !defined(XMLSEC_OPENSSL_API_300) */ certs = sk_X509_new_null(); if(certs == NULL) { xmlSecOpenSSLError("sk_X509_new_null", NULL); goto done; } /* load everything from store */ while (!OSSL_STORE_eof(storeCtx)) { info = OSSL_STORE_load(storeCtx); if(info == NULL) { break; } switch (OSSL_STORE_INFO_get_type(info)) { case OSSL_STORE_INFO_PKEY: /* only take the first private key */ if(pPrivKey == NULL) { pPrivKey = OSSL_STORE_INFO_get1_PKEY(info); if(pPrivKey == NULL) { xmlSecOpenSSLError("OSSL_STORE_INFO_get1_PKEY", NULL); goto done; } } break; #if defined(XMLSEC_OPENSSL_API_300) case OSSL_STORE_INFO_PUBKEY: /* only take the first public key */ if(pPubKey == NULL) { pPubKey = OSSL_STORE_INFO_get1_PUBKEY(info); if(pPubKey == NULL) { xmlSecOpenSSLError("OSSL_STORE_INFO_get1_PUBKEY", NULL); goto done; } } break; #endif /* !defined(XMLSEC_OPENSSL_API_300) */ case OSSL_STORE_INFO_CERT: cert = OSSL_STORE_INFO_get1_CERT(info); if(cert == NULL) { xmlSecOpenSSLError("OSSL_STORE_INFO_get1_CERT", NULL); goto done; } ret = sk_X509_push(certs, cert); if(ret <= 0) { xmlSecOpenSSLError("sk_X509_push", NULL); goto done; } cert = NULL; /* owned by certs now */ break; default: /* do nothing */ break; } OSSL_STORE_INFO_free(info); info = NULL; } /* what do we get? */ if(type == xmlSecKeyDataTypePrivate) { if(pPrivKey == NULL) { xmlSecOpenSSLError("Private key is not found in the store", NULL); goto done; } pKey = pPrivKey; pPrivKey = NULL; } else { if(pPrivKey != NULL) { pKey = pPrivKey; pPrivKey = NULL; } else if(pPubKey != NULL) { pKey = pPubKey; pPubKey = NULL; } else { xmlSecOpenSSLError("Neither private or public key is not found in the store", NULL); goto done; } } /* try find key cert, the result might be NULL and we are OK with it, * keyCert can't be non-NULL after the call to xmlSecOpenSSLCreateKey() * so we aren't going to free it */ keyCert = xmlSecOpenSSLAppFindKeyCert(pKey, certs); /* finally create a key, xmlSecOpenSSLCreateKey will set values to NULL as it uses them */ res = xmlSecOpenSSLCreateKey(&pKey, &keyCert, &certs); if(res == NULL) { xmlSecInternalError("xmlSecKeyAdoptData", NULL); goto done; } /* success! */ done: if(cert != NULL) { X509_free(cert); } if(pPrivKey != NULL) { EVP_PKEY_free(pPrivKey); } if(pPubKey != NULL) { EVP_PKEY_free(pPubKey); } if(pKey != NULL) { EVP_PKEY_free(pKey); } if(keyCert != NULL) { X509_free(keyCert); } if(certs != NULL) { sk_X509_pop_free(certs, X509_free); } if(info != NULL) { OSSL_STORE_INFO_free(info); } if(storeCtx != NULL) { OSSL_STORE_close(storeCtx); } if(ui_method != NULL) { UI_destroy_method(ui_method); } return(res); #else /* !defined(XMLSEC_OPENSSL_NO_STORE) && !defined(XMLSEC_NO_X509) */ xmlSecAssert2(uri != NULL, NULL); UNREFERENCED_PARAMETER(type); UNREFERENCED_PARAMETER(pwd); UNREFERENCED_PARAMETER(pwdCallback); UNREFERENCED_PARAMETER(pwdCallbackCtx); xmlSecNotImplementedError("X509 or OpenSSL Stores support is disabled during compilation"); return(NULL); #endif /* !defined(XMLSEC_OPENSSL_NO_STORE) && !defined(XMLSEC_NO_X509) */ } #ifndef XMLSEC_NO_X509 /** * @brief Reads the certificate from a file and adds to key. * @details Reads the certificate from @p filename and adds it to key. * * @param key the pointer to key. * @param filename the certificate filename. * @param format the certificate file format. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeyCertLoad(xmlSecKeyPtr key, const char* filename, xmlSecKeyDataFormat format) { BIO* bio; int ret; xmlSecAssert2(key != NULL, -1); xmlSecAssert2(filename != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); bio = xmlSecOpenSSLCreateReadFileBio(filename); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateReadFileBio", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(-1); } ret = xmlSecOpenSSLAppKeyCertLoadBIO(key, bio, format); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLAppKeyCertLoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads the certificate from memory and adds to key. * @details Reads the certificate from memory buffer and adds it to key. * * @param key the pointer to key. * @param data the certificate binary data. * @param dataSize the certificate binary data size. * @param format the certificate file format. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeyCertLoadMemory(xmlSecKeyPtr key, const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format) { BIO* bio; int ret; xmlSecAssert2(key != NULL, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); /* this would be a read only BIO, cast from const is ok */ bio = xmlSecOpenSSLCreateMemBufBio((void*)data, dataSize); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateMemBufBio", NULL, "dataSize=" XMLSEC_SIZE_FMT, dataSize); return(-1); } ret = xmlSecOpenSSLAppKeyCertLoadBIO(key, bio, format); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLAppKeyCertLoadBIO", NULL); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads the certificate from a BIO and adds to key. * @details Reads the certificate from a BIO object and adds it to key. * * @param key the pointer to key. * @param bio the certificate bio. * @param format the certificate file format. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeyCertLoadBIO(xmlSecKeyPtr key, BIO* bio, xmlSecKeyDataFormat format) { xmlSecKeyDataFormat certFormat; xmlSecKeyDataPtr x509Data = NULL; X509 *cert = NULL; int isKeyCert = 0; int ret; int res = -1; xmlSecAssert2(key != NULL, -1); xmlSecAssert2(bio != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); /* adjust cert format if needed */ switch(format) { case xmlSecKeyDataFormatPkcs8Pem: certFormat = xmlSecKeyDataFormatPem; break; case xmlSecKeyDataFormatPkcs8Der: certFormat = xmlSecKeyDataFormatDer; break; default: certFormat = format; } /* read cert */ cert = xmlSecOpenSSLX509CertLoadBIO(bio, certFormat); if(cert == NULL) { xmlSecInternalError("xmlSecOpenSSLX509CertLoadBIO", NULL); goto done; } /* add cert to key */ x509Data = xmlSecKeyEnsureData(key, xmlSecOpenSSLKeyDataX509Id); if(x509Data == NULL) { xmlSecInternalError("xmlSecKeyEnsureData", NULL); goto done; } /* do we want to add this cert as a key cert? */ if(xmlSecOpenSSLKeyDataX509GetKeyCert(x509Data) == NULL) { EVP_PKEY* pKey; /* pKey might not be set yet */ pKey = xmlSecOpenSSLKeyGetEvp(key); if(pKey != NULL) { /* ignore errors here */ ret = xmlSecOpenSSLAppCheckCertMatchesKey(pKey, cert); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLAppCheckCertMatchesKey", NULL); goto done; } if(ret == 1) { isKeyCert = 1; } } } if(isKeyCert != 0) { ret = xmlSecOpenSSLKeyDataX509AdoptKeyCert(x509Data, cert); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyDataX509AdoptKeyCert", NULL); goto done; } } else { ret = xmlSecOpenSSLKeyDataX509AdoptCert(x509Data, cert); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyDataX509AdoptCert", NULL); goto done; } } cert = NULL; /* owned by x509Data now */ /* success */ res = 0; done: if(cert != NULL) { X509_free(cert); } return(res); } /** * @brief Reads key and certificates from PKCS12 file. * @details Reads a key and all associated certificates from the PKCS12 file. * For uniformity, call xmlSecOpenSSLAppKeyLoadEx instead of this function. Pass * in format=xmlSecKeyDataFormatPkcs12. * * @param filename the PKCS12 key filename. * @param pwd the PKCS12 file password. * @param pwdCallback the password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppPkcs12Load(const char *filename, const char *pwd, void* pwdCallback, void* pwdCallbackCtx) { BIO* bio; xmlSecKeyPtr key; xmlSecAssert2(filename != NULL, NULL); bio = xmlSecOpenSSLCreateReadFileBio(filename); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateReadFileBio", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(NULL); } key = xmlSecOpenSSLAppPkcs12LoadBIO(bio, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError2("xmlSecOpenSSLAppPkcs12LoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); BIO_free_all(bio); return(NULL); } BIO_free_all(bio); return(key); } /** * @brief Reads key and certs from PKCS12 memory buffer. * @details Reads a key and all associated certificates from the PKCS12 data in the memory buffer. * For uniformity, call #xmlSecOpenSSLAppKeyLoadEx instead of this function. Pass * in format=xmlSecKeyDataFormatPkcs12. * * @param data the PKCS12 binary data. * @param dataSize the PKCS12 binary data size. * @param pwd the PKCS12 file password. * @param pwdCallback the password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppPkcs12LoadMemory(const xmlSecByte* data, xmlSecSize dataSize, const char *pwd, void* pwdCallback, void* pwdCallbackCtx) { BIO* bio; xmlSecKeyPtr key; xmlSecAssert2(data != NULL, NULL); /* this would be a read only BIO, cast from const is ok */ bio = xmlSecOpenSSLCreateMemBufBio((void*)data, dataSize); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateMemBufBio", NULL, "dataSize=" XMLSEC_SIZE_FMT, dataSize); return(NULL); } key = xmlSecOpenSSLAppPkcs12LoadBIO(bio, pwd, pwdCallback, pwdCallbackCtx); if(key == NULL) { xmlSecInternalError("xmlSecOpenSSLAppPkcs12LoadBIO", NULL); BIO_free_all(bio); return(NULL); } BIO_free_all(bio); return(key); } /** * @brief Reads key and certs from PKCS12 BIO object. * @details Reads a key and all associated certificates from the PKCS12 data in an OpenSSL BIO object. * For uniformity, call #xmlSecOpenSSLAppKeyLoadEx instead of this function. Pass * in format=xmlSecKeyDataFormatPkcs12. * * @param bio the PKCS12 key bio. * @param pwd the PKCS12 file password. * @param pwdCallback the password callback. * @param pwdCallbackCtx the user context for password callback. * @return pointer to the key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppPkcs12LoadBIO(BIO* bio, const char *pwd, void* pwdCallback XMLSEC_ATTRIBUTE_UNUSED, void* pwdCallbackCtx XMLSEC_ATTRIBUTE_UNUSED) { PKCS12 *p12 = NULL; EVP_PKEY * pKey = NULL; X509 * keyCert = NULL; STACK_OF(X509) * chain = NULL; xmlSecKeyPtr res = NULL; size_t pwdSize; int pwdLen; int ret; xmlSecAssert2(bio != NULL, NULL); UNREFERENCED_PARAMETER(pwdCallback); UNREFERENCED_PARAMETER(pwdCallbackCtx); pwdSize = (pwd != NULL) ? strlen(pwd) : 0; XMLSEC_SAFE_CAST_SIZE_T_TO_INT(pwdSize, pwdLen, return(NULL), NULL); XMLSEC_OPENSSL_PUSH_LIB_CTX(goto done); p12 = d2i_PKCS12_bio(bio, NULL); XMLSEC_OPENSSL_POP_LIB_CTX(); if(p12 == NULL) { xmlSecOpenSSLError("d2i_PKCS12_bio", NULL); goto done; } XMLSEC_OPENSSL_PUSH_LIB_CTX(goto done); ret = PKCS12_verify_mac(p12, pwd, pwdLen); XMLSEC_OPENSSL_POP_LIB_CTX(); if(ret != 1) { xmlSecOpenSSLError("PKCS12_verify_mac", NULL); goto done; } XMLSEC_OPENSSL_PUSH_LIB_CTX(goto done); ret = PKCS12_parse(p12, pwd, &pKey, &keyCert, &chain); XMLSEC_OPENSSL_POP_LIB_CTX(); if((ret != 1) || (pKey == NULL)) { xmlSecOpenSSLError("PKCS12_parse", NULL); goto done; } /* * The documentation states (http://www.openssl.org/docs/crypto/PKCS12_parse.html): * * If successful the private key will be written to "*pkey", the * corresponding certificate to "*cert" and any additional certificates * to "*ca". * * In reality, the function sometime returns in the "ca" the certificates * including the one it is already returned in "cert". We will sort this out * in the xmlSecOpenSSLKeyDataX509AdoptKeyCert() and xmlSecOpenSSLKeyDataX509AdoptCert() * functions. */ /* finally create a key, xmlSecOpenSSLCreateKey will set values to NULL as it uses them */ res = xmlSecOpenSSLCreateKey(&pKey, &keyCert, &chain); if(res == NULL) { xmlSecInternalError("xmlSecKeyAdoptData", NULL); goto done; } /* success! */ done: if(chain != NULL) { sk_X509_pop_free(chain, X509_free); } if(keyCert != NULL) { X509_free(keyCert); } if(pKey != NULL) { EVP_PKEY_free(pKey); } if(p12 != NULL) { PKCS12_free(p12); } return(res); } /** * @brief Loads public key from cert. * @param bio the BIO. * @param format the cert format. * @return pointer to key or NULL if an error occurs. */ xmlSecKeyPtr xmlSecOpenSSLAppKeyFromCertLoadBIO(BIO* bio, xmlSecKeyDataFormat format) { xmlSecKeyPtr key = NULL; xmlSecKeyDataPtr keyData = NULL; xmlSecKeyDataPtr certData; X509 * cert = NULL; int ret; xmlSecKeyPtr res = NULL; xmlSecAssert2(bio != NULL, NULL); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, NULL); /* load cert */ cert = xmlSecOpenSSLX509CertLoadBIO(bio, format); if(cert == NULL) { xmlSecInternalError("xmlSecOpenSSLX509CertLoadBIO", NULL); goto done; } /* create key */ key = xmlSecKeyCreate(); if(key == NULL) { xmlSecInternalError("xmlSecKeyCreate", NULL); goto done; } /* set key value */ keyData = xmlSecOpenSSLX509CertGetKey(cert); if(keyData == NULL) { xmlSecInternalError("xmlSecOpenSSLX509CertGetKey", NULL); goto done; } ret = xmlSecKeySetValue(key, keyData); if(ret < 0) { xmlSecInternalError("xmlSecKeySetValue", NULL); goto done; } keyData = NULL; /* owned by key now */ /* put cert as the key's cert in the x509 data */ certData = xmlSecKeyEnsureData(key, xmlSecOpenSSLKeyDataX509Id); if(certData == NULL) { xmlSecInternalError("xmlSecKeyEnsureData", NULL); goto done; } ret = xmlSecOpenSSLKeyDataX509AdoptKeyCert(certData, cert); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyDataX509AdoptKeyCert", NULL); goto done; } cert = NULL; /* owned by certData now */ /* success */ res = key; key = NULL; done: if(key != NULL) { xmlSecKeyDestroy(key); } if(keyData != NULL) { xmlSecKeyDataDestroy(keyData); } if(cert != NULL) { X509_free(cert); } return(res); } /** * @brief Reads a cert from a file and adds to the key store. * @details Reads cert from @p filename and adds to the list of trusted or known * untrusted certs in @p store. * * @param mngr the keys manager. * @param filename the certificate file. * @param format the certificate file format. * @param type the flag that indicates is the certificate in @p filename * trusted or not. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCertLoad(xmlSecKeysMngrPtr mngr, const char *filename, xmlSecKeyDataFormat format, xmlSecKeyDataType type) { BIO* bio; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); bio = xmlSecOpenSSLCreateReadFileBio(filename); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateReadFileBio", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(-1); } ret = xmlSecOpenSSLAppKeysMngrCertLoadBIO(mngr, bio, format, type); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLAppKeysMngrCertLoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads cert from buffer and adds to the key store. * @details Reads cert from binary buffer @p data and adds to the list of trusted or known * untrusted certs in @p store. * * @param mngr the keys manager. * @param data the certificate binary data. * @param dataSize the certificate binary data size. * @param format the certificate file format. * @param type the flag that indicates is the certificate trusted or not. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCertLoadMemory(xmlSecKeysMngrPtr mngr, const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format, xmlSecKeyDataType type) { BIO* bio; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(dataSize > 0, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); /* this would be a read only BIO, cast from const is ok */ bio = xmlSecOpenSSLCreateMemBufBio((void*)data, dataSize); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateMemBufBio", NULL, "dataSize=" XMLSEC_SIZE_FMT, dataSize); return(-1); } ret = xmlSecOpenSSLAppKeysMngrCertLoadBIO(mngr, bio, format, type); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLAppKeysMngrCertLoadBIO", NULL); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads cert from BIO and adds to the key store. * @details Reads cert from an OpenSSL BIO object and adds to the list of trusted or known * untrusted certs in @p store. * * @param mngr the keys manager. * @param bio the certificate BIO. * @param format the certificate file format. * @param type the flag that indicates is the certificate trusted or not. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCertLoadBIO(xmlSecKeysMngrPtr mngr, BIO* bio, xmlSecKeyDataFormat format, xmlSecKeyDataType type) { xmlSecKeyDataStorePtr x509Store; X509* cert; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(bio != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } cert = xmlSecOpenSSLX509CertLoadBIO(bio, format); if(cert == NULL) { xmlSecInternalError("xmlSecOpenSSLX509CertLoadBIO", NULL); return(-1); } ret = xmlSecOpenSSLX509StoreAdoptCert(x509Store, cert, type); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLX509StoreAdoptCert", NULL); X509_free(cert); return(-1); } return(0); } /** * @brief Reads CRLs from a file and adds to the store. * @details Reads crl from @p filename and adds to the list of crls in @p store. * * @param mngr the keys manager. * @param filename the CRL file. * @param format the CRL file format.. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCrlLoad(xmlSecKeysMngrPtr mngr, const char *filename, xmlSecKeyDataFormat format) { BIO* bio; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); bio = xmlSecOpenSSLCreateReadFileBio(filename); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateReadFileBio", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(-1); } ret = xmlSecOpenSSLAppKeysMngrCrlLoadBIO(mngr, bio, format); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLAppKeysMngrCrlLoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads CRLs from memory and adds to the store. * @details Reads crl from binary buffer @p data and adds to the list of crls in @p store. * * @param mngr the keys manager. * @param data the CRL binary data. * @param dataSize the CRL binary data size. * @param format the CRL format. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCrlLoadMemory(xmlSecKeysMngrPtr mngr, const xmlSecByte* data, xmlSecSize dataSize, xmlSecKeyDataFormat format) { BIO* bio; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(dataSize > 0, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); /* this would be a read only BIO, cast from const is ok */ bio = xmlSecOpenSSLCreateMemBufBio((void*)data, dataSize); if(bio == NULL) { xmlSecInternalError2("xmlSecOpenSSLCreateMemBufBio", NULL, "dataSize=" XMLSEC_SIZE_FMT, dataSize); return(-1); } ret = xmlSecOpenSSLAppKeysMngrCrlLoadBIO(mngr, bio, format); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLAppKeysMngrCrlLoadBIO", NULL); BIO_free_all(bio); return(-1); } BIO_free_all(bio); return(0); } /** * @brief Reads CRLs from BIO and adds to the store. * @details Reads crl from an OpenSSL BIO object and adds to the list of crls in @p store. * * @param mngr the keys manager. * @param bio the CRL BIO. * @param format the CRL file format. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrCrlLoadBIO(xmlSecKeysMngrPtr mngr, BIO* bio, xmlSecKeyDataFormat format) { xmlSecKeyDataStorePtr x509Store; X509_CRL* crl; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(bio != NULL, -1); xmlSecAssert2(format != xmlSecKeyDataFormatUnknown, -1); x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } crl = xmlSecOpenSSLX509CrlLoadBIO(bio, format); if(crl == NULL) { xmlSecInternalError("xmlSecOpenSSLX509CrlLoadBIO", NULL); return(-1); } ret = xmlSecOpenSSLX509StoreAdoptCrl(x509Store, crl); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLX509StoreAdoptCrl", NULL); X509_CRL_free(crl); return(-1); } return(0); } /** * @brief Loads and verifies a CRL from a file. * @details Atomically loads and verifies a CRL from @p filename. This function eliminates * TOCTOU (Time-of-Check/Time-of-Use) vulnerabilities by loading the CRL once into memory, * verifying it, and then adopting it to the store. * * The CRL is verified by: * 1. Checking the signature against the issuer's certificate in the store * 2. Validating thisUpdate and nextUpdate times * * @param mngr the keys manager. * @param filename the CRL filename. * @param format the CRL format (PEM or DER). * @param keyInfoCtx the key info context for verification parameters. * @return 0 on success or a negative value on error. */ int xmlSecOpenSSLAppKeysMngrCrlLoadAndVerify(xmlSecKeysMngrPtr mngr, const char *filename, xmlSecKeyDataFormat format, xmlSecKeyInfoCtxPtr keyInfoCtx ) { xmlSecKeyDataStorePtr x509Store; X509_CRL* crl = NULL; BIO* bio = NULL; int ret; int res = -1; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); /* Get X509 store from keys manager */ x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } /* Load CRL from file ONCE */ bio = BIO_new_file(filename, "rb"); if(bio == NULL) { xmlSecOpenSSLError2("BIO_new_file", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); goto done; } crl = xmlSecOpenSSLX509CrlLoadBIO(bio, format); if(crl == NULL) { xmlSecInternalError2("xmlSecOpenSSLX509CrlLoadBIO", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); goto done; } /* Verify the in-memory CRL */ ret = xmlSecOpenSSLX509StoreVerifyCrl(x509Store, crl, keyInfoCtx); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLX509StoreVerifyCrl", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); goto done; } else if(ret != 1) { /* Verification failed - treat as error */ xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_DATA, NULL, "filename=%s", xmlSecErrorsSafeString(filename)); goto done; } /* Adopt the verified in-memory CRL */ ret = xmlSecOpenSSLX509StoreAdoptCrl(x509Store, crl); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLX509StoreAdoptCrl", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); goto done; } /* Success - CRL is now owned by store, don't free it */ crl = NULL; res = 0; done: if(bio != NULL) { BIO_free(bio); } if(crl != NULL) { X509_CRL_free(crl); } return(res); } /** * @brief Reads certs from a path and adds to the store. * @details Reads cert from @p path and adds to the list of trusted certificates. * * @param mngr the keys manager. * @param path the path to trusted certificates. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrAddCertsPath(xmlSecKeysMngrPtr mngr, const char *path) { xmlSecKeyDataStorePtr x509Store; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(path != NULL, -1); x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } ret = xmlSecOpenSSLX509StoreAddCertsPath(x509Store, path); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLX509StoreAddCertsPath", NULL, "path=%s", xmlSecErrorsSafeString(path)); return(-1); } return(0); } /** * @brief Reads certs from file and adds to the store. * @details Reads certs from the file and adds to the list of trusted certificates. * It is possible for the file to contain multiple certs. * * @param mngr the keys manager. * @param filename the file containing trusted certificates. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppKeysMngrAddCertsFile(xmlSecKeysMngrPtr mngr, const char *filename) { xmlSecKeyDataStorePtr x509Store; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } ret = xmlSecOpenSSLX509StoreAddCertsFile(x509Store, filename); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLX509StoreAddCertsFile", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(-1); } return(0); } #endif /* XMLSEC_NO_X509 */ /** * @brief Initializes the default key manager for OpenSSL. * @details Initializes @p mngr with openssl keys store #xmlSecOpenSSLKeysStoreId * and a default OpenSSL crypto key data stores. * * @param mngr the pointer to keys manager. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppDefaultKeysMngrInit(xmlSecKeysMngrPtr mngr) { int ret; xmlSecAssert2(mngr != NULL, -1); /* create simple keys store if needed */ if(xmlSecKeysMngrGetKeysStore(mngr) == NULL) { xmlSecKeyStorePtr keysStore; keysStore = xmlSecKeyStoreCreate(xmlSecOpenSSLKeysStoreId); if(keysStore == NULL) { xmlSecInternalError("xmlSecKeyStoreCreate(xmlSecOpenSSLKeysStoreId)", NULL); return(-1); } ret = xmlSecKeysMngrAdoptKeysStore(mngr, keysStore); if(ret < 0) { xmlSecInternalError("xmlSecKeysMngrAdoptKeysStore", NULL); xmlSecKeyStoreDestroy(keysStore); return(-1); } } ret = xmlSecOpenSSLKeysMngrInit(mngr); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeysMngrInit", NULL); return(-1); } mngr->getKey = xmlSecKeysMngrGetKey; return(0); } /** * @brief Adds @p key to the keys manager. * @details Adds @p key to the keys manager @p mngr created with #xmlSecOpenSSLAppDefaultKeysMngrInit * function. * * @param mngr the pointer to keys manager. * @param key the pointer to key. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppDefaultKeysMngrAdoptKey(xmlSecKeysMngrPtr mngr, xmlSecKeyPtr key) { xmlSecKeyStorePtr store; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(key != NULL, -1); store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } return(xmlSecOpenSSLKeysStoreAdoptKey(store, key)); } /** * @brief Verifies @p key using the keys manager. * @details Verifies @p key with the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit * function: * - Checks that key certificate is present * - Checks that key certificate is valid * * Adds @p key to the keys manager @p mngr created with #xmlSecCryptoAppDefaultKeysMngrInit * function. * * @param mngr the pointer to keys manager. * @param key the pointer to key. * @param keyInfoCtx the key info context for verification. * @return 1 if key is verified, 0 otherwise, or a negative value if an error occurs. */ int xmlSecOpenSSLAppDefaultKeysMngrVerifyKey(xmlSecKeysMngrPtr mngr, xmlSecKeyPtr key, xmlSecKeyInfoCtxPtr keyInfoCtx) { #ifndef XMLSEC_NO_X509 xmlSecKeyDataStorePtr x509Store; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(key != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); x509Store = xmlSecKeysMngrGetDataStore(mngr, xmlSecOpenSSLX509StoreId); if(x509Store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetDataStore(xmlSecOpenSSLX509StoreId)", NULL); return(-1); } return(xmlSecOpenSSLX509StoreVerifyKey(x509Store, key, keyInfoCtx)); #else /* XMLSEC_NO_X509 */ xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(key != NULL, -1); xmlSecAssert2(keyInfoCtx != NULL, -1); xmlSecNotImplementedError("X509 support is disabled during compilation"); return(-1); #endif /* XMLSEC_NO_X509 */ } /** * @brief Loads the XML keys file into the keys manager. * @details Loads XML keys file from @p uri to the keys manager @p mngr created * with #xmlSecOpenSSLAppDefaultKeysMngrInit function. * * @param mngr the pointer to keys manager. * @param uri the uri. * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppDefaultKeysMngrLoad(xmlSecKeysMngrPtr mngr, const char* uri) { xmlSecKeyStorePtr store; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(uri != NULL, -1); store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } ret = xmlSecOpenSSLKeysStoreLoad(store, uri, mngr); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLKeysStoreLoad", NULL, "uri=%s", xmlSecErrorsSafeString(uri)); return(-1); } return(0); } /** * @brief Saves keys from @p mngr to XML keys file. * @param mngr the pointer to keys manager. * @param filename the destination filename. * @param type the type of keys to save (public/private/symmetric). * @return 0 on success or a negative value otherwise. */ int xmlSecOpenSSLAppDefaultKeysMngrSave(xmlSecKeysMngrPtr mngr, const char* filename, xmlSecKeyDataType type) { xmlSecKeyStorePtr store; int ret; xmlSecAssert2(mngr != NULL, -1); xmlSecAssert2(filename != NULL, -1); store = xmlSecKeysMngrGetKeysStore(mngr); if(store == NULL) { xmlSecInternalError("xmlSecKeysMngrGetKeysStore", NULL); return(-1); } ret = xmlSecOpenSSLKeysStoreSave(store, filename, type); if(ret < 0) { xmlSecInternalError2("xmlSecOpenSSLKeysStoreSave", NULL, "filename=%s", xmlSecErrorsSafeString(filename)); return(-1); } return(0); } /** * @brief Gets default password callback. * * @return default password callback. */ void* xmlSecOpenSSLAppGetDefaultPwdCallback(void) { return XMLSEC_FUNC_TO_PTR(pem_password_cb, xmlSecOpenSSLDefaultPasswordCallback); } static int xmlSecOpenSSLDefaultPasswordCallback(char *buf, int buflen, int verify, void *userdata) { char* filename = (char*)userdata; char* buf2; xmlSecSize bufsize; char prompt[2048]; int ii, ret; xmlSecAssert2(buf != NULL, -1); /* try 3 times */ for(ii = 0; ii < 3; ii++) { if(filename != NULL) { ret = xmlStrPrintf(BAD_CAST prompt, sizeof(prompt), "Enter password for \"%s\" file: ", filename); } else { ret = xmlStrPrintf(BAD_CAST prompt, sizeof(prompt), "Enter password: "); } if(ret < 0) { xmlSecXmlError("xmlStrPrintf", NULL); return(-1); } ret = EVP_read_pw_string(buf, buflen, prompt, 0); if(ret != 0) { xmlSecOpenSSLError("EVP_read_pw_string", NULL); return(-1); } /* if we don't need to verify password then we are done */ if(verify == 0) { size_t sz; int len; sz = strlen(buf); XMLSEC_SAFE_CAST_SIZE_T_TO_INT(sz, len, return(-1), NULL); return(len); } if(filename != NULL) { ret = xmlStrPrintf(BAD_CAST prompt, sizeof(prompt), "Enter password for \"%s\" file again: ", filename); } else { ret = xmlStrPrintf(BAD_CAST prompt, sizeof(prompt), "Enter password again: "); } if(ret < 0) { xmlSecXmlError("xmlStrPrintf", NULL); return(-1); } XMLSEC_SAFE_CAST_INT_TO_SIZE(buflen, bufsize, return(-1), NULL); buf2 = (char*)xmlMalloc(bufsize); if(buf2 == NULL) { xmlSecMallocError(bufsize, NULL); return(-1); } ret = EVP_read_pw_string(buf2, buflen, (char*)prompt, 0); if(ret != 0) { xmlSecOpenSSLError("EVP_read_pw_string", NULL); memset(buf2, 0, bufsize); xmlFree(buf2); return(-1); } /* check if passwords match */ if(strcmp(buf, buf2) == 0) { size_t sz; int len; sz = strlen(buf); memset(buf2, 0, bufsize); xmlFree(buf2); XMLSEC_SAFE_CAST_SIZE_T_TO_INT(sz, len, return(-1), NULL); return(len); } /* try again */ memset(buf2, 0, bufsize); xmlFree(buf2); } return(-1); } static int xmlSecOpenSSLDummyPasswordCallback(char *buf, int bufLen, int verify XMLSEC_ATTRIBUTE_UNUSED, void *userdata) { xmlSecSize bufSize; char* password; size_t passwordSize; int passwordLen; UNREFERENCED_PARAMETER(verify); xmlSecAssert2(buf != NULL, -1); xmlSecAssert2(bufLen > 1, -1); password = (char*)userdata; if(password == NULL) { return(-1); } passwordSize = strlen(password); XMLSEC_SAFE_CAST_SIZE_T_TO_INT(passwordSize, passwordLen, return(-1), NULL); if(passwordLen + 1 > bufLen) { return(-1); } XMLSEC_SAFE_CAST_INT_TO_SIZE(bufLen, bufSize, return(-1), NULL); #if defined(_MSC_VER) strcpy_s(buf, bufSize, password); #else /* defined(_MSC_VER) */ strncpy(buf, password, bufSize); buf[bufLen - 1] = '\0'; /* ensure \0 terminated */ #endif /* defined(_MSC_VER) */ return (passwordLen); }