| /** @file |
| |
| A brief file description |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /*************************** -*- Mod: C++ -*- ****************************** |
| SSLConfig.cc |
| Created On : 07/20/2000 |
| |
| Description: |
| SSL Configurations |
| ****************************************************************************/ |
| |
| #include "ts/ink_platform.h" |
| #include "ts/I_Layout.h" |
| |
| #include <cstring> |
| #include <cmath> |
| #include "P_Net.h" |
| #include "P_SSLConfig.h" |
| #include "P_SSLUtils.h" |
| #include "P_SSLCertLookup.h" |
| #include "SSLSessionCache.h" |
| #include <records/I_RecHttp.h> |
| #include <HttpConfig.h> |
| |
| int SSLConfig::configid = 0; |
| int SSLCertificateConfig::configid = 0; |
| int SSLTicketKeyConfig::configid = 0; |
| int SSLConfigParams::ssl_maxrecord = 0; |
| bool SSLConfigParams::ssl_allow_client_renegotiation = false; |
| bool SSLConfigParams::ssl_ocsp_enabled = false; |
| int SSLConfigParams::ssl_ocsp_cache_timeout = 3600; |
| int SSLConfigParams::ssl_ocsp_request_timeout = 10; |
| int SSLConfigParams::ssl_ocsp_update_period = 60; |
| int SSLConfigParams::ssl_handshake_timeout_in = 0; |
| size_t SSLConfigParams::session_cache_number_buckets = 1024; |
| bool SSLConfigParams::session_cache_skip_on_lock_contention = false; |
| size_t SSLConfigParams::session_cache_max_bucket_size = 100; |
| init_ssl_ctx_func SSLConfigParams::init_ssl_ctx_cb = nullptr; |
| load_ssl_file_func SSLConfigParams::load_ssl_file_cb = nullptr; |
| bool SSLConfigParams::sni_map_enable = false; |
| IpMap *SSLConfigParams::proxy_protocol_ipmap = nullptr; |
| |
| // TS-3534 Wiretracing for SSL Connections |
| int SSLConfigParams::ssl_wire_trace_enabled = 0; |
| char *SSLConfigParams::ssl_wire_trace_addr = nullptr; |
| IpAddr *SSLConfigParams::ssl_wire_trace_ip = nullptr; |
| int SSLConfigParams::ssl_wire_trace_percentage = 0; |
| char *SSLConfigParams::ssl_wire_trace_server_name = nullptr; |
| int SSLConfigParams::async_handshake_enabled = 0; |
| char *SSLConfigParams::engine_conf_file = nullptr; |
| |
| static ConfigUpdateHandler<SSLCertificateConfig> *sslCertUpdate; |
| |
| // Check if the ticket_key callback #define is available, and if so, enable session tickets. |
| #ifdef SSL_CTX_set_tlsext_ticket_key_cb |
| |
| #define HAVE_OPENSSL_SESSION_TICKETS 1 |
| |
| #endif /* SSL_CTX_set_tlsext_ticket_key_cb */ |
| |
| SSLConfigParams::SSLConfigParams() |
| { |
| ink_mutex_init(&ctxMapLock); |
| reset(); |
| } |
| |
| SSLConfigParams::~SSLConfigParams() |
| { |
| cleanup(); |
| ink_mutex_destroy(&ctxMapLock); |
| } |
| |
| void |
| SSLConfigInit(IpMap *global) |
| { |
| SSLConfigParams::proxy_protocol_ipmap = global; |
| } |
| |
| void |
| SSLConfigParams::reset() |
| { |
| serverCertPathOnly = serverCertChainFilename = configFilePath = serverCACertFilename = serverCACertPath = clientCertPath = |
| clientKeyPath = clientCACertFilename = clientCACertPath = cipherSuite = client_cipherSuite = dhparamsFile = serverKeyPathOnly = |
| nullptr; |
| server_tls13_cipher_suites = nullptr; |
| client_tls13_cipher_suites = nullptr; |
| client_ctx = nullptr; |
| clientCertLevel = client_verify_depth = verify_depth = clientVerify = 0; |
| ssl_ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; |
| ssl_client_ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; |
| ssl_session_cache = SSL_SESSION_CACHE_MODE_SERVER_ATS_IMPL; |
| ssl_session_cache_size = 1024 * 100; |
| ssl_session_cache_num_buckets = 1024; // Sessions per bucket is ceil(ssl_session_cache_size / ssl_session_cache_num_buckets) |
| ssl_session_cache_skip_on_contention = 0; |
| ssl_session_cache_timeout = 0; |
| ssl_session_cache_auto_clear = 1; |
| configExitOnLoadError = 1; |
| } |
| |
| void |
| SSLConfigParams::cleanup() |
| { |
| serverCertChainFilename = (char *)ats_free_null(serverCertChainFilename); |
| serverCACertFilename = (char *)ats_free_null(serverCACertFilename); |
| serverCACertPath = (char *)ats_free_null(serverCACertPath); |
| clientCertPath = (char *)ats_free_null(clientCertPath); |
| clientKeyPath = (char *)ats_free_null(clientKeyPath); |
| clientCACertFilename = (char *)ats_free_null(clientCACertFilename); |
| clientCACertPath = (char *)ats_free_null(clientCACertPath); |
| configFilePath = (char *)ats_free_null(configFilePath); |
| serverCertPathOnly = (char *)ats_free_null(serverCertPathOnly); |
| serverKeyPathOnly = (char *)ats_free_null(serverKeyPathOnly); |
| cipherSuite = (char *)ats_free_null(cipherSuite); |
| client_cipherSuite = (char *)ats_free_null(client_cipherSuite); |
| dhparamsFile = (char *)ats_free_null(dhparamsFile); |
| ssl_wire_trace_ip = (IpAddr *)ats_free_null(ssl_wire_trace_ip); |
| |
| server_tls13_cipher_suites = (char *)ats_free_null(server_tls13_cipher_suites); |
| client_tls13_cipher_suites = (char *)ats_free_null(client_tls13_cipher_suites); |
| |
| freeCTXmap(); |
| SSLReleaseContext(client_ctx); |
| reset(); |
| } |
| |
| /** set_paths_helper |
| |
| If path is *not* absolute, consider it relative to PREFIX |
| if it's empty, just take SYSCONFDIR, otherwise we can take it as-is |
| if final_path is nullptr, it will not be updated. |
| |
| XXX: Add handling for Windows? |
| */ |
| static void |
| set_paths_helper(const char *path, const char *filename, char **final_path, char **final_filename) |
| { |
| if (final_path) { |
| if (path && path[0] != '/') { |
| *final_path = ats_stringdup(RecConfigReadPrefixPath(nullptr, path)); |
| } else if (!path || path[0] == '\0') { |
| *final_path = ats_stringdup(RecConfigReadConfigDir()); |
| } else { |
| *final_path = ats_strdup(path); |
| } |
| } |
| |
| if (final_filename && path) { |
| *final_filename = filename ? ats_stringdup(Layout::get()->relative_to(path, filename)) : nullptr; |
| } |
| } |
| |
| void |
| SSLConfigParams::initialize() |
| { |
| char *serverCertRelativePath = nullptr; |
| char *ssl_server_private_key_path = nullptr; |
| char *CACertRelativePath = nullptr; |
| char *ssl_client_cert_filename = nullptr; |
| char *ssl_client_cert_path = nullptr; |
| char *ssl_client_private_key_filename = nullptr; |
| char *ssl_client_private_key_path = nullptr; |
| char *clientCACertRelativePath = nullptr; |
| char *ssl_server_ca_cert_filename = nullptr; |
| char *ssl_client_ca_cert_filename = nullptr; |
| |
| cleanup(); |
| |
| //+++++++++++++++++++++++++ Server part +++++++++++++++++++++++++++++++++ |
| verify_depth = 7; |
| |
| REC_ReadConfigInt32(clientCertLevel, "proxy.config.ssl.client.certification_level"); |
| REC_ReadConfigStringAlloc(cipherSuite, "proxy.config.ssl.server.cipher_suite"); |
| REC_ReadConfigStringAlloc(client_cipherSuite, "proxy.config.ssl.client.cipher_suite"); |
| REC_ReadConfigStringAlloc(server_tls13_cipher_suites, "proxy.config.ssl.server.TLSv1_3.cipher_suites"); |
| REC_ReadConfigStringAlloc(client_tls13_cipher_suites, "proxy.config.ssl.client.TLSv1_3.cipher_suites"); |
| |
| dhparamsFile = ats_stringdup(RecConfigReadConfigPath("proxy.config.ssl.server.dhparams_file")); |
| |
| int options; |
| int client_ssl_options = 0; |
| REC_ReadConfigInteger(options, "proxy.config.ssl.TLSv1"); |
| if (!options) { |
| ssl_ctx_options |= SSL_OP_NO_TLSv1; |
| } |
| |
| #if TS_USE_SSLV3_CLIENT |
| REC_ReadConfigInteger(client_ssl_options, "proxy.config.ssl.client.SSLv3"); |
| if (client_ssl_options) |
| ssl_client_ctx_options &= ~SSL_OP_NO_SSLv3; |
| #endif |
| REC_ReadConfigInteger(client_ssl_options, "proxy.config.ssl.client.TLSv1"); |
| if (!client_ssl_options) { |
| ssl_client_ctx_options |= SSL_OP_NO_TLSv1; |
| } |
| |
| // These are not available in all versions of OpenSSL (e.g. CentOS6). Also see http://s.apache.org/TS-2355. |
| #ifdef SSL_OP_NO_TLSv1_1 |
| REC_ReadConfigInteger(options, "proxy.config.ssl.TLSv1_1"); |
| if (!options) { |
| ssl_ctx_options |= SSL_OP_NO_TLSv1_1; |
| } |
| |
| REC_ReadConfigInteger(client_ssl_options, "proxy.config.ssl.client.TLSv1_1"); |
| if (!client_ssl_options) { |
| ssl_client_ctx_options |= SSL_OP_NO_TLSv1_1; |
| } |
| #endif |
| #ifdef SSL_OP_NO_TLSv1_2 |
| REC_ReadConfigInteger(options, "proxy.config.ssl.TLSv1_2"); |
| if (!options) { |
| ssl_ctx_options |= SSL_OP_NO_TLSv1_2; |
| } |
| |
| REC_ReadConfigInteger(client_ssl_options, "proxy.config.ssl.client.TLSv1_2"); |
| if (!client_ssl_options) { |
| ssl_client_ctx_options |= SSL_OP_NO_TLSv1_2; |
| } |
| #endif |
| |
| #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE |
| REC_ReadConfigInteger(options, "proxy.config.ssl.server.honor_cipher_order"); |
| if (options) { |
| ssl_ctx_options |= SSL_OP_CIPHER_SERVER_PREFERENCE; |
| } |
| #endif |
| |
| #ifdef SSL_OP_NO_COMPRESSION |
| /* OpenSSL >= 1.0 only */ |
| ssl_ctx_options |= SSL_OP_NO_COMPRESSION; |
| ssl_client_ctx_options |= SSL_OP_NO_COMPRESSION; |
| #elif OPENSSL_VERSION_NUMBER >= 0x00908000L |
| sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); |
| #endif |
| |
| // Enable ephemeral DH parameters for the case where we use a cipher with DH forward security. |
| #ifdef SSL_OP_SINGLE_DH_USE |
| ssl_ctx_options |= SSL_OP_SINGLE_DH_USE; |
| ssl_client_ctx_options |= SSL_OP_SINGLE_DH_USE; |
| #endif |
| |
| #ifdef SSL_OP_SINGLE_ECDH_USE |
| ssl_ctx_options |= SSL_OP_SINGLE_ECDH_USE; |
| ssl_client_ctx_options |= SSL_OP_SINGLE_ECDH_USE; |
| #endif |
| |
| // Enable all SSL compatibility workarounds. |
| ssl_ctx_options |= SSL_OP_ALL; |
| ssl_client_ctx_options |= SSL_OP_ALL; |
| |
| // According to OpenSSL source, applications must enable this if they support the Server Name extension. Since |
| // we do, then we ought to enable this. Httpd also enables this unconditionally. |
| #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
| ssl_ctx_options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; |
| ssl_client_ctx_options |= SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; |
| #endif |
| |
| REC_ReadConfigStringAlloc(serverCertChainFilename, "proxy.config.ssl.server.cert_chain.filename"); |
| REC_ReadConfigStringAlloc(serverCertRelativePath, "proxy.config.ssl.server.cert.path"); |
| set_paths_helper(serverCertRelativePath, nullptr, &serverCertPathOnly, nullptr); |
| ats_free(serverCertRelativePath); |
| |
| configFilePath = ats_stringdup(RecConfigReadConfigPath("proxy.config.ssl.server.multicert.filename")); |
| REC_ReadConfigInteger(configExitOnLoadError, "proxy.config.ssl.server.multicert.exit_on_load_fail"); |
| |
| REC_ReadConfigStringAlloc(ssl_server_private_key_path, "proxy.config.ssl.server.private_key.path"); |
| set_paths_helper(ssl_server_private_key_path, nullptr, &serverKeyPathOnly, nullptr); |
| ats_free(ssl_server_private_key_path); |
| |
| REC_ReadConfigStringAlloc(ssl_server_ca_cert_filename, "proxy.config.ssl.CA.cert.filename"); |
| REC_ReadConfigStringAlloc(CACertRelativePath, "proxy.config.ssl.CA.cert.path"); |
| set_paths_helper(CACertRelativePath, ssl_server_ca_cert_filename, &serverCACertPath, &serverCACertFilename); |
| ats_free(ssl_server_ca_cert_filename); |
| ats_free(CACertRelativePath); |
| |
| // SSL session cache configurations |
| REC_ReadConfigInteger(ssl_session_cache, "proxy.config.ssl.session_cache"); |
| REC_ReadConfigInteger(ssl_session_cache_size, "proxy.config.ssl.session_cache.size"); |
| REC_ReadConfigInteger(ssl_session_cache_num_buckets, "proxy.config.ssl.session_cache.num_buckets"); |
| REC_ReadConfigInteger(ssl_session_cache_skip_on_contention, "proxy.config.ssl.session_cache.skip_cache_on_bucket_contention"); |
| REC_ReadConfigInteger(ssl_session_cache_timeout, "proxy.config.ssl.session_cache.timeout"); |
| REC_ReadConfigInteger(ssl_session_cache_auto_clear, "proxy.config.ssl.session_cache.auto_clear"); |
| |
| SSLConfigParams::session_cache_max_bucket_size = (size_t)ceil((double)ssl_session_cache_size / ssl_session_cache_num_buckets); |
| SSLConfigParams::session_cache_skip_on_lock_contention = ssl_session_cache_skip_on_contention; |
| SSLConfigParams::session_cache_number_buckets = ssl_session_cache_num_buckets; |
| |
| if (ssl_session_cache == SSL_SESSION_CACHE_MODE_SERVER_ATS_IMPL) { |
| session_cache = new SSLSessionCache(); |
| } |
| |
| // SSL record size |
| REC_EstablishStaticConfigInt32(ssl_maxrecord, "proxy.config.ssl.max_record_size"); |
| |
| // SSL OCSP Stapling configurations |
| REC_ReadConfigInt32(ssl_ocsp_enabled, "proxy.config.ssl.ocsp.enabled"); |
| REC_EstablishStaticConfigInt32(ssl_ocsp_cache_timeout, "proxy.config.ssl.ocsp.cache_timeout"); |
| REC_EstablishStaticConfigInt32(ssl_ocsp_request_timeout, "proxy.config.ssl.ocsp.request_timeout"); |
| REC_EstablishStaticConfigInt32(ssl_ocsp_update_period, "proxy.config.ssl.ocsp.update_period"); |
| |
| REC_ReadConfigInt32(async_handshake_enabled, "proxy.config.ssl.async.handshake.enabled"); |
| REC_ReadConfigStringAlloc(engine_conf_file, "proxy.config.ssl.engine.conf_file"); |
| |
| // ++++++++++++++++++++++++ Client part ++++++++++++++++++++ |
| client_verify_depth = 7; |
| REC_EstablishStaticConfigByte(clientVerify, "proxy.config.ssl.client.verify.server"); |
| |
| ssl_client_cert_filename = nullptr; |
| ssl_client_cert_path = nullptr; |
| REC_ReadConfigStringAlloc(ssl_client_cert_filename, "proxy.config.ssl.client.cert.filename"); |
| REC_ReadConfigStringAlloc(ssl_client_cert_path, "proxy.config.ssl.client.cert.path"); |
| if (ssl_client_cert_filename && ssl_client_cert_path) { |
| set_paths_helper(ssl_client_cert_path, ssl_client_cert_filename, nullptr, &clientCertPath); |
| } |
| ats_free_null(ssl_client_cert_filename); |
| ats_free_null(ssl_client_cert_path); |
| |
| REC_ReadConfigStringAlloc(ssl_client_private_key_filename, "proxy.config.ssl.client.private_key.filename"); |
| REC_ReadConfigStringAlloc(ssl_client_private_key_path, "proxy.config.ssl.client.private_key.path"); |
| set_paths_helper(ssl_client_private_key_path, ssl_client_private_key_filename, nullptr, &clientKeyPath); |
| ats_free_null(ssl_client_private_key_filename); |
| ats_free_null(ssl_client_private_key_path); |
| |
| REC_ReadConfigStringAlloc(ssl_client_ca_cert_filename, "proxy.config.ssl.client.CA.cert.filename"); |
| REC_ReadConfigStringAlloc(clientCACertRelativePath, "proxy.config.ssl.client.CA.cert.path"); |
| set_paths_helper(clientCACertRelativePath, ssl_client_ca_cert_filename, &clientCACertPath, &clientCACertFilename); |
| ats_free(clientCACertRelativePath); |
| ats_free(ssl_client_ca_cert_filename); |
| |
| // Enable/disable sni mapping |
| REC_ReadConfigInteger(sni_map_enable, "proxy.config.ssl.sni.map.enable"); |
| |
| REC_ReadConfigInt32(ssl_allow_client_renegotiation, "proxy.config.ssl.allow_client_renegotiation"); |
| |
| // SSL Wire Trace configurations |
| REC_EstablishStaticConfigInt32(ssl_wire_trace_enabled, "proxy.config.ssl.wire_trace_enabled"); |
| if (ssl_wire_trace_enabled) { |
| // wire trace specific source ip |
| REC_EstablishStaticConfigStringAlloc(ssl_wire_trace_addr, "proxy.config.ssl.wire_trace_addr"); |
| if (ssl_wire_trace_addr) { |
| ssl_wire_trace_ip = new IpAddr(); |
| ssl_wire_trace_ip->load(ssl_wire_trace_addr); |
| } else { |
| ssl_wire_trace_ip = nullptr; |
| } |
| // wire trace percentage of requests |
| REC_EstablishStaticConfigInt32(ssl_wire_trace_percentage, "proxy.config.ssl.wire_trace_percentage"); |
| REC_EstablishStaticConfigStringAlloc(ssl_wire_trace_server_name, "proxy.config.ssl.wire_trace_server_name"); |
| } else { |
| ssl_wire_trace_addr = nullptr; |
| ssl_wire_trace_ip = nullptr; |
| ssl_wire_trace_percentage = 0; |
| ssl_wire_trace_server_name = nullptr; |
| } |
| // Enable client regardless of config file settings as remap file |
| // can cause HTTP layer to connect using SSL. But only if SSL |
| // initialization hasn't failed already. |
| client_ctx = SSLInitClientContext(this); |
| if (!client_ctx) { |
| SSLError("Can't initialize the SSL client, HTTPS in remap rules will not function"); |
| } else { |
| InsertCTX(this->clientCertPath, this->client_ctx); |
| } |
| } |
| |
| // getCTX: returns the context attached to the given certificate |
| SSL_CTX * |
| SSLConfigParams::getCTX(cchar *client_cert) const |
| { |
| ink_mutex_acquire(&ctxMapLock); |
| auto client_ctx = ctx_map.get(client_cert); |
| ink_mutex_release(&ctxMapLock); |
| return client_ctx; |
| } |
| |
| // InsertCTX hashes on the absolute path to the client certificate file and stores in the map |
| bool |
| SSLConfigParams::InsertCTX(cchar *client_cert, SSL_CTX *cctx) const |
| { |
| ink_mutex_acquire(&ctxMapLock); |
| // dup is required here to avoid the nullifying of the keys stored in the map. |
| // client_cert is coming from the overridable clientcert config retrieved by the remap plugin. |
| cchar *cert = ats_strdup(client_cert); |
| // Hashmap has no delete functionality :( |
| ctx_map.put(cert, cctx); |
| ink_mutex_release(&ctxMapLock); |
| return true; |
| } |
| |
| void |
| SSLConfigParams::printCTXmap() const |
| { |
| Vec<cchar *> keys; |
| ctx_map.get_keys(keys); |
| for (size_t i = 0; i < keys.length(); i++) { |
| Debug("ssl", "Client certificates in the map %s: %p", keys.get(i), ctx_map.get(keys.get(i))); |
| } |
| } |
| void |
| SSLConfigParams::freeCTXmap() const |
| { |
| ink_mutex_acquire(&ctxMapLock); |
| Vec<cchar *> keys; |
| ctx_map.get_keys(keys); |
| size_t n = keys.length(); |
| Debug("ssl", "freeing CTX Map"); |
| for (size_t i = 0; i < n; i++) { |
| deleteKey(keys.get(i)); |
| ats_free((char *)keys.get(i)); |
| } |
| ctx_map.clear(); |
| ink_mutex_release(&ctxMapLock); |
| } |
| // creates a new context attaching the provided certificate |
| SSL_CTX * |
| SSLConfigParams::getNewCTX(cchar *client_cert) const |
| { |
| SSL_CTX *nclient_ctx = nullptr; |
| nclient_ctx = SSLInitClientContext(this); |
| if (!nclient_ctx) { |
| SSLError("Can't initialize the SSL client, HTTPS in remap rules will not function"); |
| return nullptr; |
| } |
| if (nclient_ctx && client_cert != nullptr && client_cert[0] != '\0') { |
| if (!SSL_CTX_use_certificate_chain_file(nclient_ctx, (const char *)client_cert)) { |
| SSLError("failed to load client certificate from %s", this->clientCertPath); |
| SSLReleaseContext(nclient_ctx); |
| return nullptr; |
| } |
| } |
| return nclient_ctx; |
| } |
| |
| void |
| SSLConfigParams::deleteKey(cchar *key) const |
| { |
| SSL_CTX_free((SSL_CTX *)ctx_map.get(key)); |
| } |
| |
| SSL_CTX * |
| SSLConfigParams::getClientSSL_CTX() const |
| { |
| return client_ctx; |
| } |
| |
| void |
| SSLConfig::startup() |
| { |
| reconfigure(); |
| } |
| |
| void |
| SSLConfig::reconfigure() |
| { |
| SSLConfigParams *params; |
| params = new SSLConfigParams; |
| params->initialize(); // re-read configuration |
| configid = configProcessor.set(configid, params); |
| } |
| |
| SSLConfigParams * |
| SSLConfig::acquire() |
| { |
| return ((SSLConfigParams *)configProcessor.get(configid)); |
| } |
| |
| void |
| SSLConfig::release(SSLConfigParams *params) |
| { |
| configProcessor.release(configid, params); |
| } |
| |
| bool |
| SSLCertificateConfig::startup() |
| { |
| sslCertUpdate = new ConfigUpdateHandler<SSLCertificateConfig>(); |
| sslCertUpdate->attach("proxy.config.ssl.server.multicert.filename"); |
| sslCertUpdate->attach("proxy.config.ssl.server.cert.path"); |
| sslCertUpdate->attach("proxy.config.ssl.server.private_key.path"); |
| sslCertUpdate->attach("proxy.config.ssl.server.cert_chain.filename"); |
| sslCertUpdate->attach("proxy.config.ssl.server.session_ticket.enable"); |
| // Exit if there are problems on the certificate loading and the |
| // proxy.config.ssl.server.multicert.exit_on_load_fail is true |
| SSLConfig::scoped_config params; |
| if (!reconfigure() && params->configExitOnLoadError) { |
| Fatal("failed to load SSL certificate file, %s", params->configFilePath); |
| } |
| |
| return true; |
| } |
| |
| bool |
| SSLCertificateConfig::reconfigure() |
| { |
| bool retStatus = true; |
| SSLConfig::scoped_config params; |
| SSLCertLookup *lookup = new SSLCertLookup(); |
| |
| // Test SSL certificate loading startup. With large numbers of certificates, reloading can take time, so delay |
| // twice the healthcheck period to simulate a loading a large certificate set. |
| if (is_action_tag_set("test.multicert.delay")) { |
| const int secs = 60; |
| Debug("ssl", "delaying certificate reload by %dsecs", secs); |
| ink_hrtime_sleep(HRTIME_SECONDS(secs)); |
| } |
| |
| SSLParseCertificateConfiguration(params, lookup); |
| |
| if (!lookup->is_valid) { |
| retStatus = false; |
| } |
| // If there are errors in the certificate configs and we had wanted to exit on error |
| // we won't want to reset the config |
| if (lookup->is_valid || !params->configExitOnLoadError) { |
| configid = configProcessor.set(configid, lookup); |
| } else { |
| delete lookup; |
| } |
| |
| if (retStatus) { |
| Note("ssl_multicert.config done reloading!"); |
| } else { |
| Note("failed to reload ssl_multicert.config"); |
| } |
| |
| return retStatus; |
| } |
| |
| SSLCertLookup * |
| SSLCertificateConfig::acquire() |
| { |
| return (SSLCertLookup *)configProcessor.get(configid); |
| } |
| |
| void |
| SSLCertificateConfig::release(SSLCertLookup *lookup) |
| { |
| configProcessor.release(configid, lookup); |
| } |
| |
| bool |
| SSLTicketParams::LoadTicket() |
| { |
| cleanup(); |
| |
| #if HAVE_OPENSSL_SESSION_TICKETS |
| ssl_ticket_key_block *keyblock = nullptr; |
| |
| SSLConfig::scoped_config params; |
| time_t last_load_time = 0; |
| bool no_default_keyblock = true; |
| |
| SSLTicketKeyConfig::scoped_config ticket_params; |
| if (ticket_params) { |
| last_load_time = ticket_params->load_time; |
| no_default_keyblock = ticket_params->default_global_keyblock != nullptr; |
| } |
| |
| if (REC_ReadConfigStringAlloc(ticket_key_filename, "proxy.config.ssl.server.ticket_key.filename") == REC_ERR_OKAY && |
| ticket_key_filename != nullptr) { |
| ats_scoped_str ticket_key_path(Layout::relative_to(params->serverCertPathOnly, ticket_key_filename)); |
| // See if the file changed since we last loaded |
| struct stat sdata; |
| if (last_load_time && (stat(ticket_key_filename, &sdata) >= 0)) { |
| if (sdata.st_mtime && sdata.st_mtime <= last_load_time) { |
| Debug("ssl", "ticket key %s has not changed", ticket_key_filename); |
| // No updates since last load |
| return false; |
| } |
| } |
| keyblock = ssl_create_ticket_keyblock(ticket_key_path); |
| // Initialize if we don't have one yet |
| } else if (no_default_keyblock) { |
| keyblock = ssl_create_ticket_keyblock(nullptr); |
| } else { |
| // No need to update. Keep the previous ticket param |
| return false; |
| } |
| if (!keyblock) { |
| Error("Could not load ticket key from %s", ticket_key_filename); |
| return false; |
| } |
| default_global_keyblock = keyblock; |
| |
| Debug("ssl", "ticket key reloaded from %s", ticket_key_filename); |
| return true; |
| #endif |
| } |
| |
| void |
| SSLTicketParams::LoadTicketData(char *ticket_data, int ticket_data_len) |
| { |
| cleanup(); |
| #if HAVE_OPENSSL_SESSION_TICKETS |
| if (ticket_data != nullptr && ticket_data_len > 0) { |
| default_global_keyblock = ticket_block_create(ticket_data, ticket_data_len); |
| } else { |
| default_global_keyblock = ssl_create_ticket_keyblock(nullptr); |
| } |
| load_time = time(nullptr); |
| #endif |
| } |
| |
| void |
| SSLTicketKeyConfig::startup() |
| { |
| auto sslTicketKey = new ConfigUpdateHandler<SSLTicketKeyConfig>(); |
| |
| sslTicketKey->attach("proxy.config.ssl.server.ticket_key.filename"); |
| SSLConfig::scoped_config params; |
| if (!reconfigure() && params->configExitOnLoadError) { |
| Fatal("Failed to load SSL ticket key file"); |
| } |
| } |
| |
| bool |
| SSLTicketKeyConfig::reconfigure() |
| { |
| SSLTicketParams *ticketKey = new SSLTicketParams(); |
| |
| if (ticketKey) { |
| if (!ticketKey->LoadTicket()) { |
| delete ticketKey; |
| return false; |
| } |
| } |
| configid = configProcessor.set(configid, ticketKey); |
| return true; |
| } |
| |
| bool |
| SSLTicketKeyConfig::reconfigure_data(char *ticket_data, int ticket_data_len) |
| { |
| SSLTicketParams *ticketKey = new SSLTicketParams(); |
| if (ticketKey) { |
| ticketKey->LoadTicketData(ticket_data, ticket_data_len); |
| } |
| configid = configProcessor.set(configid, ticketKey); |
| return true; |
| } |
| |
| void |
| SSLTicketParams::cleanup() |
| { |
| ticket_block_free(default_global_keyblock); |
| ticket_key_filename = (char *)ats_free_null(ticket_key_filename); |
| } |