#include "config.h" #include "libssl.hh" #ifdef HAVE_LIBSSL #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBSODIUM #include #endif /* HAVE_LIBSODIUM */ #undef CERT #include "misc.hh" #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL) /* OpenSSL < 1.1.0 needs support for threading/locking in the calling application. */ #include "lock.hh" static std::vector openssllocks; extern "C" { static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line) { if (mode & CRYPTO_LOCK) { openssllocks.at(type).lock(); } else { openssllocks.at(type).unlock(); } } static unsigned long openssl_pthreads_id_callback() { return (unsigned long)pthread_self(); } } static void openssl_thread_setup() { openssllocks = std::vector(CRYPTO_num_locks()); CRYPTO_set_id_callback(&openssl_pthreads_id_callback); CRYPTO_set_locking_callback(&openssl_pthreads_locking_callback); } static void openssl_thread_cleanup() { CRYPTO_set_locking_callback(nullptr); openssllocks.clear(); } #endif /* (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2090100fL) */ static std::atomic s_users; static int s_ticketsKeyIndex{-1}; static int s_countersIndex{-1}; static int s_keyLogIndex{-1}; void registerOpenSSLUser() { if (s_users.fetch_add(1) == 0) { #ifdef HAVE_OPENSSL_INIT_CRYPTO /* load the default configuration file (or one specified via OPENSSL_CONF), which can then be used to load engines */ OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, nullptr); #endif #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER && LIBRESSL_VERSION_NUMBER < 0x2090100fL)) SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); openssl_thread_setup(); #endif s_ticketsKeyIndex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); if (s_ticketsKeyIndex == -1) { throw std::runtime_error("Error getting an index for tickets key"); } s_countersIndex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); if (s_countersIndex == -1) { throw std::runtime_error("Error getting an index for counters"); } s_keyLogIndex = SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); if (s_keyLogIndex == -1) { throw std::runtime_error("Error getting an index for TLS key logging"); } } } void unregisterOpenSSLUser() { if (s_users.fetch_sub(1) == 1) { #if (OPENSSL_VERSION_NUMBER < 0x1010000fL || (defined LIBRESSL_VERSION_NUMBER && LIBRESSL_VERSION_NUMBER < 0x2090100fL)) ERR_free_strings(); EVP_cleanup(); CONF_modules_finish(); CONF_modules_free(); CONF_modules_unload(1); CRYPTO_cleanup_all_ex_data(); openssl_thread_cleanup(); #endif } } void* libssl_get_ticket_key_callback_data(SSL* s) { SSL_CTX* sslCtx = SSL_get_SSL_CTX(s); if (sslCtx == nullptr) { return nullptr; } return SSL_CTX_get_ex_data(sslCtx, s_ticketsKeyIndex); } void libssl_set_ticket_key_callback_data(SSL_CTX* ctx, void* data) { SSL_CTX_set_ex_data(ctx, s_ticketsKeyIndex, data); } int libssl_ticket_key_callback(SSL *s, OpenSSLTLSTicketKeysRing& keyring, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc) { if (enc) { const auto key = keyring.getEncryptionKey(); if (key == nullptr) { return -1; } return key->encrypt(keyName, iv, ectx, hctx); } bool activeEncryptionKey = false; const auto key = keyring.getDecryptionKey(keyName, activeEncryptionKey); if (key == nullptr) { /* we don't know this key, just create a new ticket */ return 0; } if (key->decrypt(iv, ectx, hctx) == false) { return -1; } if (!activeEncryptionKey) { /* this key is not active, please encrypt the ticket content with the currently active one */ return 2; } return 1; } static long libssl_server_name_callback(SSL* ssl, int* al, void* arg) { (void) al; (void) arg; if (SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) { return SSL_TLSEXT_ERR_OK; } return SSL_TLSEXT_ERR_NOACK; } static void libssl_info_callback(const SSL *ssl, int where, int ret) { SSL_CTX* sslCtx = SSL_get_SSL_CTX(ssl); if (sslCtx == nullptr) { return; } TLSErrorCounters* counters = reinterpret_cast(SSL_CTX_get_ex_data(sslCtx, s_countersIndex)); if (counters == nullptr) { return; } if (where & SSL_CB_ALERT) { const long lastError = ERR_peek_last_error(); switch (ERR_GET_REASON(lastError)) { #ifdef SSL_R_DH_KEY_TOO_SMALL case SSL_R_DH_KEY_TOO_SMALL: ++counters->d_dhKeyTooSmall; break; #endif /* SSL_R_DH_KEY_TOO_SMALL */ case SSL_R_NO_SHARED_CIPHER: ++counters->d_noSharedCipher; break; case SSL_R_UNKNOWN_PROTOCOL: ++counters->d_unknownProtocol; break; case SSL_R_UNSUPPORTED_PROTOCOL: #ifdef SSL_R_VERSION_TOO_LOW case SSL_R_VERSION_TOO_LOW: #endif /* SSL_R_VERSION_TOO_LOW */ ++counters->d_unsupportedProtocol; break; case SSL_R_INAPPROPRIATE_FALLBACK: ++counters->d_inappropriateFallBack; break; case SSL_R_UNKNOWN_CIPHER_TYPE: ++counters->d_unknownCipherType; break; case SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE: ++counters->d_unknownKeyExchangeType; break; case SSL_R_UNSUPPORTED_ELLIPTIC_CURVE: ++counters->d_unsupportedEC; break; default: break; } } } void libssl_set_error_counters_callback(std::unique_ptr& ctx, TLSErrorCounters* counters) { SSL_CTX_set_ex_data(ctx.get(), s_countersIndex, counters); SSL_CTX_set_info_callback(ctx.get(), libssl_info_callback); } int libssl_ocsp_stapling_callback(SSL* ssl, const std::map& ocspMap) { auto pkey = SSL_get_privatekey(ssl); if (pkey == nullptr) { return SSL_TLSEXT_ERR_NOACK; } /* look for an OCSP response for the corresponding private key type (RSA, ECDSA..) */ const auto& data = ocspMap.find(EVP_PKEY_base_id(pkey)); if (data == ocspMap.end()) { return SSL_TLSEXT_ERR_NOACK; } /* we need to allocate a copy because OpenSSL will free the pointer passed to SSL_set_tlsext_status_ocsp_resp() */ void* copy = OPENSSL_malloc(data->second.size()); if (copy == nullptr) { return SSL_TLSEXT_ERR_NOACK; } memcpy(copy, data->second.data(), data->second.size()); SSL_set_tlsext_status_ocsp_resp(ssl, copy, data->second.size()); return SSL_TLSEXT_ERR_OK; } static bool libssl_validate_ocsp_response(const std::string& response) { auto responsePtr = reinterpret_cast(response.data()); std::unique_ptr resp(d2i_OCSP_RESPONSE(nullptr, &responsePtr, response.size()), OCSP_RESPONSE_free); if (resp == nullptr) { throw std::runtime_error("Unable to parse OCSP response"); } int status = OCSP_response_status(resp.get()); if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { throw std::runtime_error("OCSP response status is not successful: " + std::to_string(status)); } std::unique_ptr basic(OCSP_response_get1_basic(resp.get()), OCSP_BASICRESP_free); if (basic == nullptr) { throw std::runtime_error("Error getting a basic OCSP response"); } if (OCSP_resp_count(basic.get()) != 1) { throw std::runtime_error("More than one single response in an OCSP basic response"); } auto singleResponse = OCSP_resp_get0(basic.get(), 0); if (singleResponse == nullptr) { throw std::runtime_error("Error getting a single response from the basic OCSP response"); } int reason; ASN1_GENERALIZEDTIME* revTime = nullptr; ASN1_GENERALIZEDTIME* thisUpdate = nullptr; ASN1_GENERALIZEDTIME* nextUpdate = nullptr; auto singleResponseStatus = OCSP_single_get0_status(singleResponse, &reason, &revTime, &thisUpdate, &nextUpdate); if (singleResponseStatus != V_OCSP_CERTSTATUS_GOOD) { throw std::runtime_error("Invalid status for OCSP single response (" + std::to_string(singleResponseStatus) + ")"); } if (thisUpdate == nullptr || nextUpdate == nullptr) { throw std::runtime_error("Error getting validity of OCSP single response"); } auto validityResult = OCSP_check_validity(thisUpdate, nextUpdate, /* 5 minutes of leeway */ 5 * 60, -1); if (validityResult == 0) { throw std::runtime_error("OCSP single response is not yet, or no longer, valid"); } return true; } std::map libssl_load_ocsp_responses(const std::vector& ocspFiles, std::vector keyTypes) { std::map ocspResponses; if (ocspFiles.size() > keyTypes.size()) { throw std::runtime_error("More OCSP files than certificates and keys loaded!"); } size_t count = 0; for (const auto& filename : ocspFiles) { std::ifstream file(filename, std::ios::binary); std::string content; while(file) { char buffer[4096]; file.read(buffer, sizeof(buffer)); if (file.bad()) { file.close(); throw std::runtime_error("Unable to load OCSP response from '" + filename + "'"); } content.append(buffer, file.gcount()); } file.close(); try { libssl_validate_ocsp_response(content); ocspResponses.insert({keyTypes.at(count), std::move(content)}); } catch (const std::exception& e) { throw std::runtime_error("Error checking the validity of OCSP response from '" + filename + "': " + e.what()); } ++count; } return ocspResponses; } int libssl_get_last_key_type(std::unique_ptr& ctx) { #ifdef HAVE_SSL_CTX_GET0_PRIVATEKEY auto pkey = SSL_CTX_get0_privatekey(ctx.get()); #else auto temp = std::unique_ptr(SSL_new(ctx.get()), SSL_free); if (!temp) { return -1; } auto pkey = SSL_get_privatekey(temp.get()); #endif if (!pkey) { return -1; } return EVP_PKEY_base_id(pkey); } #ifdef HAVE_OCSP_BASIC_SIGN bool libssl_generate_ocsp_response(const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin) { const EVP_MD* rmd = EVP_sha256(); auto fp = std::unique_ptr(fopen(certFile.c_str(), "r"), fclose); if (!fp) { throw std::runtime_error("Unable to open '" + certFile + "' when loading the certificate to generate an OCSP response"); } auto cert = std::unique_ptr(PEM_read_X509_AUX(fp.get(), nullptr, nullptr, nullptr), X509_free); fp = std::unique_ptr(fopen(caCert.c_str(), "r"), fclose); if (!fp) { throw std::runtime_error("Unable to open '" + caCert + "' when loading the issuer certificate to generate an OCSP response"); } auto issuer = std::unique_ptr(PEM_read_X509_AUX(fp.get(), nullptr, nullptr, nullptr), X509_free); fp = std::unique_ptr(fopen(caKey.c_str(), "r"), fclose); if (!fp) { throw std::runtime_error("Unable to open '" + caKey + "' when loading the issuer key to generate an OCSP response"); } auto issuerKey = std::unique_ptr(PEM_read_PrivateKey(fp.get(), nullptr, nullptr, nullptr), EVP_PKEY_free); fp.reset(); auto bs = std::unique_ptr(OCSP_BASICRESP_new(), OCSP_BASICRESP_free); auto thisupd = std::unique_ptr(X509_gmtime_adj(nullptr, 0), ASN1_TIME_free); auto nextupd = std::unique_ptr(X509_time_adj_ex(nullptr, ndays, nmin * 60, nullptr), ASN1_TIME_free); auto cid = std::unique_ptr(OCSP_cert_to_id(rmd, cert.get(), issuer.get()), OCSP_CERTID_free); OCSP_basic_add1_status(bs.get(), cid.get(), V_OCSP_CERTSTATUS_GOOD, 0, nullptr, thisupd.get(), nextupd.get()); if (OCSP_basic_sign(bs.get(), issuer.get(), issuerKey.get(), rmd, nullptr, OCSP_NOCERTS) != 1) { throw std::runtime_error("Error while signing the OCSP response"); } auto resp = std::unique_ptr(OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs.get()), OCSP_RESPONSE_free); auto bio = std::unique_ptr(BIO_new_file(outFile.c_str(), "wb"), BIO_vfree); if (!bio) { throw std::runtime_error("Error opening file for writing the OCSP response"); } // i2d_OCSP_RESPONSE_bio(bio.get(), resp.get()) is unusable from C++ because of an invalid cast ASN1_i2d_bio((i2d_of_void*)i2d_OCSP_RESPONSE, bio.get(), (unsigned char*)resp.get()); return true; } #endif /* HAVE_OCSP_BASIC_SIGN */ LibsslTLSVersion libssl_tls_version_from_string(const std::string& str) { if (str == "tls1.0") { return LibsslTLSVersion::TLS10; } if (str == "tls1.1") { return LibsslTLSVersion::TLS11; } if (str == "tls1.2") { return LibsslTLSVersion::TLS12; } if (str == "tls1.3") { return LibsslTLSVersion::TLS13; } throw std::runtime_error("Unknown TLS version '" + str); } const std::string& libssl_tls_version_to_string(LibsslTLSVersion version) { static const std::map versions = { { LibsslTLSVersion::TLS10, "tls1.0" }, { LibsslTLSVersion::TLS11, "tls1.1" }, { LibsslTLSVersion::TLS12, "tls1.2" }, { LibsslTLSVersion::TLS13, "tls1.3" } }; const auto& it = versions.find(version); if (it == versions.end()) { throw std::runtime_error("Unknown TLS version (" + std::to_string((int)version) + ")"); } return it->second; } bool libssl_set_min_tls_version(std::unique_ptr& ctx, LibsslTLSVersion version) { #if defined(HAVE_SSL_CTX_SET_MIN_PROTO_VERSION) || defined(SSL_CTX_set_min_proto_version) /* These functions have been introduced in 1.1.0, and the use of SSL_OP_NO_* is deprecated Warning: SSL_CTX_set_min_proto_version is a function-like macro in OpenSSL */ int vers; switch(version) { case LibsslTLSVersion::TLS10: vers = TLS1_VERSION; break; case LibsslTLSVersion::TLS11: vers = TLS1_1_VERSION; break; case LibsslTLSVersion::TLS12: vers = TLS1_2_VERSION; break; case LibsslTLSVersion::TLS13: #ifdef TLS1_3_VERSION vers = TLS1_3_VERSION; #else return false; #endif /* TLS1_3_VERSION */ break; default: return false; } if (SSL_CTX_set_min_proto_version(ctx.get(), vers) != 1) { return false; } return true; #else long vers = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; switch(version) { case LibsslTLSVersion::TLS10: break; case LibsslTLSVersion::TLS11: vers |= SSL_OP_NO_TLSv1; break; case LibsslTLSVersion::TLS12: vers |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; break; case LibsslTLSVersion::TLS13: vers |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; break; default: return false; } long options = SSL_CTX_get_options(ctx.get()); SSL_CTX_set_options(ctx.get(), options | vers); return true; #endif } OpenSSLTLSTicketKeysRing::OpenSSLTLSTicketKeysRing(size_t capacity) { d_ticketKeys.write_lock()->set_capacity(capacity); } OpenSSLTLSTicketKeysRing::~OpenSSLTLSTicketKeysRing() { } void OpenSSLTLSTicketKeysRing::addKey(std::shared_ptr newKey) { d_ticketKeys.write_lock()->push_front(newKey); } std::shared_ptr OpenSSLTLSTicketKeysRing::getEncryptionKey() { return d_ticketKeys.read_lock()->front(); } std::shared_ptr OpenSSLTLSTicketKeysRing::getDecryptionKey(unsigned char name[TLS_TICKETS_KEY_NAME_SIZE], bool& activeKey) { auto keys = d_ticketKeys.read_lock(); for (auto& key : *keys) { if (key->nameMatches(name)) { activeKey = (key == keys->front()); return key; } } return nullptr; } size_t OpenSSLTLSTicketKeysRing::getKeysCount() { return d_ticketKeys.read_lock()->size(); } void OpenSSLTLSTicketKeysRing::loadTicketsKeys(const std::string& keyFile) { bool keyLoaded = false; std::ifstream file(keyFile); try { do { auto newKey = std::make_shared(file); addKey(newKey); keyLoaded = true; } while (!file.fail()); } catch (const std::exception& e) { /* if we haven't been able to load at least one key, fail */ if (!keyLoaded) { throw; } } file.close(); } void OpenSSLTLSTicketKeysRing::rotateTicketsKey(time_t now) { auto newKey = std::make_shared(); addKey(newKey); } OpenSSLTLSTicketKey::OpenSSLTLSTicketKey() { if (RAND_bytes(d_name, sizeof(d_name)) != 1) { throw std::runtime_error("Error while generating the name of the OpenSSL TLS ticket key"); } if (RAND_bytes(d_cipherKey, sizeof(d_cipherKey)) != 1) { throw std::runtime_error("Error while generating the cipher key of the OpenSSL TLS ticket key"); } if (RAND_bytes(d_hmacKey, sizeof(d_hmacKey)) != 1) { throw std::runtime_error("Error while generating the HMAC key of the OpenSSL TLS ticket key"); } #ifdef HAVE_LIBSODIUM sodium_mlock(d_name, sizeof(d_name)); sodium_mlock(d_cipherKey, sizeof(d_cipherKey)); sodium_mlock(d_hmacKey, sizeof(d_hmacKey)); #endif /* HAVE_LIBSODIUM */ } OpenSSLTLSTicketKey::OpenSSLTLSTicketKey(std::ifstream& file) { file.read(reinterpret_cast(d_name), sizeof(d_name)); file.read(reinterpret_cast(d_cipherKey), sizeof(d_cipherKey)); file.read(reinterpret_cast(d_hmacKey), sizeof(d_hmacKey)); if (file.fail()) { throw std::runtime_error("Unable to load a ticket key from the OpenSSL tickets key file"); } #ifdef HAVE_LIBSODIUM sodium_mlock(d_name, sizeof(d_name)); sodium_mlock(d_cipherKey, sizeof(d_cipherKey)); sodium_mlock(d_hmacKey, sizeof(d_hmacKey)); #endif /* HAVE_LIBSODIUM */ } OpenSSLTLSTicketKey::~OpenSSLTLSTicketKey() { #ifdef HAVE_LIBSODIUM sodium_munlock(d_name, sizeof(d_name)); sodium_munlock(d_cipherKey, sizeof(d_cipherKey)); sodium_munlock(d_hmacKey, sizeof(d_hmacKey)); #else OPENSSL_cleanse(d_name, sizeof(d_name)); OPENSSL_cleanse(d_cipherKey, sizeof(d_cipherKey)); OPENSSL_cleanse(d_hmacKey, sizeof(d_hmacKey)); #endif /* HAVE_LIBSODIUM */ } bool OpenSSLTLSTicketKey::nameMatches(const unsigned char name[TLS_TICKETS_KEY_NAME_SIZE]) const { return (memcmp(d_name, name, sizeof(d_name)) == 0); } int OpenSSLTLSTicketKey::encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx) const { memcpy(keyName, d_name, sizeof(d_name)); if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) != 1) { return -1; } if (EVP_EncryptInit_ex(ectx, TLS_TICKETS_CIPHER_ALGO(), nullptr, d_cipherKey, iv) != 1) { return -1; } if (HMAC_Init_ex(hctx, d_hmacKey, sizeof(d_hmacKey), TLS_TICKETS_MAC_ALGO(), nullptr) != 1) { return -1; } return 1; } bool OpenSSLTLSTicketKey::decrypt(const unsigned char* iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx) const { if (HMAC_Init_ex(hctx, d_hmacKey, sizeof(d_hmacKey), TLS_TICKETS_MAC_ALGO(), nullptr) != 1) { return false; } if (EVP_DecryptInit_ex(ectx, TLS_TICKETS_CIPHER_ALGO(), nullptr, d_cipherKey, iv) != 1) { return false; } return true; } std::unique_ptr libssl_init_server_context(const TLSConfig& config, std::map& ocspResponses) { auto ctx = std::unique_ptr(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); int sslOptions = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE; if (!config.d_enableTickets || config.d_numberOfTicketsKeys == 0) { /* for TLS 1.3 this means no stateless tickets, but stateful tickets might still be issued, which is something we don't want. */ sslOptions |= SSL_OP_NO_TICKET; /* really disable all tickets */ #ifdef HAVE_SSL_CTX_SET_NUM_TICKETS SSL_CTX_set_num_tickets(ctx.get(), 0); #endif /* HAVE_SSL_CTX_SET_NUM_TICKETS */ } if (config.d_sessionTimeout > 0) { SSL_CTX_set_timeout(ctx.get(), config.d_sessionTimeout); } if (config.d_preferServerCiphers) { sslOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; #ifdef SSL_OP_PRIORITIZE_CHACHA sslOptions |= SSL_OP_PRIORITIZE_CHACHA; #endif /* SSL_OP_PRIORITIZE_CHACHA */ } if (!config.d_enableRenegotiation) { #ifdef SSL_OP_NO_RENEGOTIATION sslOptions |= SSL_OP_NO_RENEGOTIATION; #elif defined(SSL_OP_NO_CLIENT_RENEGOTIATION) sslOptions |= SSL_OP_NO_CLIENT_RENEGOTIATION; #endif } SSL_CTX_set_options(ctx.get(), sslOptions); if (!libssl_set_min_tls_version(ctx, config.d_minTLSVersion)) { throw std::runtime_error("Failed to set the minimum version to '" + libssl_tls_version_to_string(config.d_minTLSVersion)); } #ifdef SSL_CTX_set_ecdh_auto SSL_CTX_set_ecdh_auto(ctx.get(), 1); #endif if (config.d_maxStoredSessions == 0) { /* disable stored sessions entirely */ SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF); } else { /* use the internal built-in cache to store sessions */ SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_SERVER); SSL_CTX_sess_set_cache_size(ctx.get(), config.d_maxStoredSessions); } #ifdef SSL_MODE_RELEASE_BUFFERS if (config.d_releaseBuffers) { SSL_CTX_set_mode(ctx.get(), SSL_MODE_RELEASE_BUFFERS); } #endif /* we need to set this callback to acknowledge the server name sent by the client, otherwise it will not stored in the session and will not be accessible when the session is resumed, causing SSL_get_servername to return nullptr */ SSL_CTX_set_tlsext_servername_callback(ctx.get(), &libssl_server_name_callback); std::vector keyTypes; /* load certificate and private key */ for (const auto& pair : config.d_certKeyPairs) { if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.first.c_str()) != 1) { ERR_print_errors_fp(stderr); throw std::runtime_error("An error occurred while trying to load the TLS server certificate file: " + pair.first); } if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) { ERR_print_errors_fp(stderr); throw std::runtime_error("An error occurred while trying to load the TLS server private key file: " + pair.second); } if (SSL_CTX_check_private_key(ctx.get()) != 1) { ERR_print_errors_fp(stderr); throw std::runtime_error("The key from '" + pair.second + "' does not match the certificate from '" + pair.first + "'"); } /* store the type of the new key, we might need it later to select the right OCSP stapling response */ auto keyType = libssl_get_last_key_type(ctx); if (keyType < 0) { throw std::runtime_error("The key from '" + pair.second + "' has an unknown type"); } keyTypes.push_back(keyType); } if (!config.d_ocspFiles.empty()) { try { ocspResponses = libssl_load_ocsp_responses(config.d_ocspFiles, keyTypes); } catch(const std::exception& e) { throw std::runtime_error("Unable to load OCSP responses: " + std::string(e.what())); } } if (!config.d_ciphers.empty() && SSL_CTX_set_cipher_list(ctx.get(), config.d_ciphers.c_str()) != 1) { throw std::runtime_error("The TLS ciphers could not be set: " + config.d_ciphers); } #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES if (!config.d_ciphers13.empty() && SSL_CTX_set_ciphersuites(ctx.get(), config.d_ciphers13.c_str()) != 1) { throw std::runtime_error("The TLS 1.3 ciphers could not be set: " + config.d_ciphers13); } #endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */ return ctx; } #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK static void libssl_key_log_file_callback(const SSL* ssl, const char* line) { SSL_CTX* sslCtx = SSL_get_SSL_CTX(ssl); if (sslCtx == nullptr) { return; } auto fp = reinterpret_cast(SSL_CTX_get_ex_data(sslCtx, s_keyLogIndex)); if (fp == nullptr) { return; } fprintf(fp, "%s\n", line); fflush(fp); } #endif /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */ std::unique_ptr libssl_set_key_log_file(std::unique_ptr& ctx, const std::string& logFile) { #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK int fd = open(logFile.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0600); if (fd == -1) { unixDie("Error opening TLS log file '" + logFile + "'"); } auto fp = std::unique_ptr(fdopen(fd, "a"), fclose); if (!fp) { int error = errno; // close might clobber errno close(fd); throw std::runtime_error("Error opening TLS log file '" + logFile + "': " + stringerror(error)); } SSL_CTX_set_ex_data(ctx.get(), s_keyLogIndex, fp.get()); SSL_CTX_set_keylog_callback(ctx.get(), &libssl_key_log_file_callback); return fp; #else return std::unique_ptr(nullptr, fclose); #endif /* HAVE_SSL_CTX_SET_KEYLOG_CALLBACK */ } /* called in a client context, if the client advertised more than one ALPN values and the server returned more than one as well, to select the one to use. */ void libssl_set_npn_select_callback(SSL_CTX* ctx, int (*cb)(SSL* s, unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen, void* arg), void* arg) { #ifdef HAVE_SSL_CTX_SET_NEXT_PROTO_SELECT_CB SSL_CTX_set_next_proto_select_cb(ctx, cb, arg); #endif } void libssl_set_alpn_select_callback(SSL_CTX* ctx, int (*cb)(SSL* s, const unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen, void* arg), void* arg) { #ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB SSL_CTX_set_alpn_select_cb(ctx, cb, arg); #endif } bool libssl_set_alpn_protos(SSL_CTX* ctx, const std::vector>& protos) { #ifdef HAVE_SSL_CTX_SET_ALPN_PROTOS std::vector wire; for (const auto& proto : protos) { if (proto.size() > std::numeric_limits::max()) { throw std::runtime_error("Invalid ALPN value"); } uint8_t length = proto.size(); wire.push_back(length); wire.insert(wire.end(), proto.begin(), proto.end()); } return SSL_CTX_set_alpn_protos(ctx, wire.data(), wire.size()) == 0; #else return false; #endif } std::string libssl_get_error_string() { BIO *mem = BIO_new(BIO_s_mem()); ERR_print_errors(mem); char *p; size_t len = BIO_get_mem_data(mem, &p); std::string msg(p, len); // replace newlines by / if (msg.back() == '\n') { msg.pop_back(); } std::replace(msg.begin(), msg.end(), '\n', '/'); BIO_free(mem); return msg; } #endif /* HAVE_LIBSSL */