From cdc051d2478da8dc6c1076a6c01ee5d9c7de7db1 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Christensen Date: Mon, 28 Dec 2015 22:45:03 -0800 Subject: [PATCH 1/4] Add TSSslContextCreate method This method returns a new SSL Context that's configured according to the settings in records.config This is useful if an extension wants to use the TS_SSL_CERT_HOOK to control loading of SNI certificates, and still want to respect the cipher suite and related SSL settings. --- iocore/net/P_SSLUtils.h | 3 +++ iocore/net/SSLUtils.cc | 41 +++++++++++++++++++++++++++++++++++++++++ proxy/InkAPI.cc | 13 +++++++++++++ proxy/api/ts/ts.h | 4 ++++ 4 files changed, 61 insertions(+) diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h index cc58e58579f..fd4cf0c7501 100644 --- a/iocore/net/P_SSLUtils.h +++ b/iocore/net/P_SSLUtils.h @@ -117,6 +117,9 @@ extern RecRawStatBlock *ssl_rsb; // Create a default SSL server context. SSL_CTX *SSLDefaultServerContext(); +// Create a new SSL server context fully configured. +SSL_CTX *SSLCreateServerContext(const SSLConfigParams *params); + // Initialize the SSL library. void SSLInitializeLibrary(); diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 4cccff8a269..67b6397a058 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -1649,6 +1649,47 @@ ssl_set_handshake_callbacks(SSL_CTX *ctx) #endif } +SSL_CTX * +SSLCreateServerContext(const SSLConfigParams *params) { + Vec cert_list; + const ssl_user_config sslMultCertSettings; + SSL_CTX *ctx = SSLInitServerContext(params, sslMultCertSettings, cert_list); + + // The certificate callbacks are set by the caller only + // for the default certificate + SSL_CTX_set_info_callback(ctx, ssl_callback_info); + +#if TS_USE_TLS_NPN + SSL_CTX_set_next_protos_advertised_cb(ctx, SSLNetVConnection::advertise_next_protocol, NULL); +#endif /* TS_USE_TLS_NPN */ + +#if TS_USE_TLS_ALPN + SSL_CTX_set_alpn_select_cb(ctx, SSLNetVConnection::select_next_protocol, NULL); +#endif /* TS_USE_TLS_ALPN */ + + // TODO: Allow control over tickets and ticket path when using SSLCreateServerContext + ssl_context_enable_tickets(ctx, NULL); + +#ifdef HAVE_OPENSSL_OCSP_STAPLING + if (SSLConfigParams::ssl_ocsp_enabled) { + Debug("ssl", "ssl ocsp stapling is enabled"); + SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling); + } else { + Debug("ssl", "ssl ocsp stapling is disabled"); + } +#else + if (SSLConfigParams::ssl_ocsp_enabled) { + Warning("fail to enable ssl ocsp stapling, this openssl version does not support it"); + } +#endif /* HAVE_OPENSSL_OCSP_STAPLING */ + + + if (SSLConfigParams::init_ssl_ctx_cb) { + SSLConfigParams::init_ssl_ctx_cb(ctx, true); + } + return ctx; +} + static SSL_CTX * ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, const ssl_user_config &sslMultCertSettings) { diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc index 8373e1677ac..128fd36ad51 100644 --- a/proxy/InkAPI.cc +++ b/proxy/InkAPI.cc @@ -8868,6 +8868,19 @@ TSSslContextFindByAddr(struct sockaddr const *addr) return ret; } +tsapi TSSslContext +TSSslContextCreate() +{ + TSSslContext ret = NULL; + SSLConfigParams *config = SSLConfig::acquire(); + if (config != NULL) { + ret = reinterpret_cast(SSLCreateServerContext(config)); + SSLConfig::release(config); + } + return ret; +} + + tsapi int TSVConnIsSsl(TSVConn sslp) { diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h index 7ef9914fc60..2b3d047844c 100644 --- a/proxy/api/ts/ts.h +++ b/proxy/api/ts/ts.h @@ -1225,9 +1225,13 @@ tsapi TSSslConnection TSVConnSSLConnectionGet(TSVConn sslp); // Fetch a SSL context from the global lookup table tsapi TSSslContext TSSslContextFindByName(const char *name); tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const *); +// Create a new SSL context based on the settings in records.config +tsapi TSSslContext TSSslContextCreate(); + // Returns 1 if the sslp argument refers to a SSL connection tsapi int TSVConnIsSsl(TSVConn sslp); + /* -------------------------------------------------------------------------- HTTP transactions */ tsapi void TSHttpTxnHookAdd(TSHttpTxn txnp, TSHttpHookID id, TSCont contp); From 8b248f4493c0e6e53c4fbc8cbdf618fabc0e818f Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Christensen Date: Sat, 2 Jan 2016 17:20:40 -0800 Subject: [PATCH 2/4] Move SSL_CTX setup out of ssl_store_ssl_context Refactor ssl_store_ssl_context method to separate context initialization and configuration from inserting the context into the SSLCertLookup. --- iocore/net/SSLUtils.cc | 311 ++++++++++++++++++----------------------- 1 file changed, 135 insertions(+), 176 deletions(-) diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 67b6397a058..9d5f05b8103 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -1257,6 +1257,123 @@ SSLCheckServerCertNow(X509 *cert, const char *certname) } /* CheckServerCertNow() */ +static char * +asn1_strdup(ASN1_STRING *s) +{ + // Make sure we have an 8-bit encoding. + ink_assert(ASN1_STRING_type(s) == V_ASN1_IA5STRING || ASN1_STRING_type(s) == V_ASN1_UTF8STRING || + ASN1_STRING_type(s) == V_ASN1_PRINTABLESTRING || ASN1_STRING_type(s) == V_ASN1_T61STRING); + + return ats_strndup((const char *)ASN1_STRING_data(s), ASN1_STRING_length(s)); +} + +// Given a certificate and it's corresponding SSL_CTX context, insert hash +// table aliases for subject CN and subjectAltNames DNS without wildcard, +// insert trie aliases for those with wildcard. +static bool +ssl_index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname) +{ + X509_NAME *subject = NULL; + bool inserted = false; + + if (NULL == cert) { + Error("Failed to load certificate %s", certname); + lookup->is_valid = false; + return false; + } + + // Insert a key for the subject CN. + subject = X509_get_subject_name(cert); + ats_scoped_str subj_name; + if (subject) { + int pos = -1; + for (;;) { + pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos); + if (pos == -1) { + break; + } + + X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); + ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); + subj_name = asn1_strdup(cn); + + Debug("ssl", "mapping '%s' to certificate %s", (const char *)subj_name, certname); + if (lookup->insert(subj_name, cc) >= 0) + inserted = true; + } + } + +#if HAVE_OPENSSL_TS_H + // Traverse the subjectAltNames (if any) and insert additional keys for the SSL context. + GENERAL_NAMES *names = (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (names) { + unsigned count = sk_GENERAL_NAME_num(names); + for (unsigned i = 0; i < count; ++i) { + GENERAL_NAME *name; + + name = sk_GENERAL_NAME_value(names, i); + if (name->type == GEN_DNS) { + ats_scoped_str dns(asn1_strdup(name->d.dNSName)); + // only try to insert if the alternate name is not the main name + if (strcmp(dns, subj_name) != 0) { + Debug("ssl", "mapping '%s' to certificates %s", (const char *)dns, certname); + if (lookup->insert(dns, cc) >= 0) + inserted = true; + } + } + } + + GENERAL_NAMES_free(names); + } +#endif // HAVE_OPENSSL_TS_H + return inserted; +} + +// This callback function is executed while OpenSSL processes the SSL +// handshake and does SSL record layer stuff. It's used to trap +// client-initiated renegotiations and update cipher stats +static void +ssl_callback_info(const SSL *ssl, int where, int ret) +{ + Debug("ssl", "ssl_callback_info ssl: %p where: %d ret: %d", ssl, where, ret); + SSLNetVConnection *netvc = (SSLNetVConnection *)SSL_get_app_data(ssl); + + if ((where & SSL_CB_ACCEPT_LOOP) && netvc->getSSLHandShakeComplete() == true && + SSLConfigParams::ssl_allow_client_renegotiation == false) { + int state = SSL_get_state(ssl); + + if (state == SSL3_ST_SR_CLNT_HELLO_A || state == SSL23_ST_SR_CLNT_HELLO_A) { + netvc->setSSLClientRenegotiationAbort(true); + Debug("ssl", "ssl_callback_info trying to renegotiate from the client"); + } + } + if (where & SSL_CB_HANDSHAKE_DONE) { + // handshake is complete + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + if (cipher) { + const char *cipherName = SSL_CIPHER_get_name(cipher); + // lookup index of stat by name and incr count + InkHashTableValue data; + if (ink_hash_table_lookup(ssl_cipher_name_table, cipherName, &data)) { + SSL_INCREMENT_DYN_STAT((intptr_t)data); + } + } + } +} + +static void +ssl_set_handshake_callbacks(SSL_CTX *ctx) +{ +#if TS_USE_TLS_SNI +// Make sure the callbacks are set +#if TS_USE_CERT_CB + SSL_CTX_set_cert_cb(ctx, ssl_cert_callback, NULL); +#else + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_callback); +#endif +#endif +} + SSL_CTX * SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config &sslMultCertSettings, Vec &certList) { @@ -1520,140 +1637,7 @@ SSLInitServerContext(const SSLConfigParams *params, const ssl_user_config &sslMu if (!ssl_context_enable_dhe(params->dhparamsFile, ctx)) { goto fail; } - return ssl_context_enable_ecdh(ctx); - -fail: - SSL_CLEAR_PW_REFERENCES(ud, ctx) - SSL_CTX_free(ctx); - for (unsigned int i = 0; i < certList.length(); i++) { - X509_free(certList[i]); - } - - return NULL; -} - -static char * -asn1_strdup(ASN1_STRING *s) -{ - // Make sure we have an 8-bit encoding. - ink_assert(ASN1_STRING_type(s) == V_ASN1_IA5STRING || ASN1_STRING_type(s) == V_ASN1_UTF8STRING || - ASN1_STRING_type(s) == V_ASN1_PRINTABLESTRING || ASN1_STRING_type(s) == V_ASN1_T61STRING); - - return ats_strndup((const char *)ASN1_STRING_data(s), ASN1_STRING_length(s)); -} - -// Given a certificate and it's corresponding SSL_CTX context, insert hash -// table aliases for subject CN and subjectAltNames DNS without wildcard, -// insert trie aliases for those with wildcard. -static bool -ssl_index_certificate(SSLCertLookup *lookup, SSLCertContext const &cc, X509 *cert, const char *certname) -{ - X509_NAME *subject = NULL; - bool inserted = false; - - if (NULL == cert) { - Error("Failed to load certificate %s", certname); - lookup->is_valid = false; - return false; - } - - // Insert a key for the subject CN. - subject = X509_get_subject_name(cert); - ats_scoped_str subj_name; - if (subject) { - int pos = -1; - for (;;) { - pos = X509_NAME_get_index_by_NID(subject, NID_commonName, pos); - if (pos == -1) { - break; - } - - X509_NAME_ENTRY *e = X509_NAME_get_entry(subject, pos); - ASN1_STRING *cn = X509_NAME_ENTRY_get_data(e); - subj_name = asn1_strdup(cn); - - Debug("ssl", "mapping '%s' to certificate %s", (const char *)subj_name, certname); - if (lookup->insert(subj_name, cc) >= 0) - inserted = true; - } - } - -#if HAVE_OPENSSL_TS_H - // Traverse the subjectAltNames (if any) and insert additional keys for the SSL context. - GENERAL_NAMES *names = (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - if (names) { - unsigned count = sk_GENERAL_NAME_num(names); - for (unsigned i = 0; i < count; ++i) { - GENERAL_NAME *name; - - name = sk_GENERAL_NAME_value(names, i); - if (name->type == GEN_DNS) { - ats_scoped_str dns(asn1_strdup(name->d.dNSName)); - // only try to insert if the alternate name is not the main name - if (strcmp(dns, subj_name) != 0) { - Debug("ssl", "mapping '%s' to certificates %s", (const char *)dns, certname); - if (lookup->insert(dns, cc) >= 0) - inserted = true; - } - } - } - - GENERAL_NAMES_free(names); - } -#endif // HAVE_OPENSSL_TS_H - return inserted; -} - -// This callback function is executed while OpenSSL processes the SSL -// handshake and does SSL record layer stuff. It's used to trap -// client-initiated renegotiations and update cipher stats -static void -ssl_callback_info(const SSL *ssl, int where, int ret) -{ - Debug("ssl", "ssl_callback_info ssl: %p where: %d ret: %d", ssl, where, ret); - SSLNetVConnection *netvc = (SSLNetVConnection *)SSL_get_app_data(ssl); - - if ((where & SSL_CB_ACCEPT_LOOP) && netvc->getSSLHandShakeComplete() == true && - SSLConfigParams::ssl_allow_client_renegotiation == false) { - int state = SSL_get_state(ssl); - - if (state == SSL3_ST_SR_CLNT_HELLO_A || state == SSL23_ST_SR_CLNT_HELLO_A) { - netvc->setSSLClientRenegotiationAbort(true); - Debug("ssl", "ssl_callback_info trying to renegotiate from the client"); - } - } - if (where & SSL_CB_HANDSHAKE_DONE) { - // handshake is complete - const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); - if (cipher) { - const char *cipherName = SSL_CIPHER_get_name(cipher); - // lookup index of stat by name and incr count - InkHashTableValue data; - if (ink_hash_table_lookup(ssl_cipher_name_table, cipherName, &data)) { - SSL_INCREMENT_DYN_STAT((intptr_t)data); - } - } - } -} - -static void -ssl_set_handshake_callbacks(SSL_CTX *ctx) -{ -#if TS_USE_TLS_SNI -// Make sure the callbacks are set -#if TS_USE_CERT_CB - SSL_CTX_set_cert_cb(ctx, ssl_cert_callback, NULL); -#else - SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_callback); -#endif -#endif -} - -SSL_CTX * -SSLCreateServerContext(const SSLConfigParams *params) { - Vec cert_list; - const ssl_user_config sslMultCertSettings; - SSL_CTX *ctx = SSLInitServerContext(params, sslMultCertSettings, cert_list); + ssl_context_enable_ecdh(ctx); // The certificate callbacks are set by the caller only // for the default certificate @@ -1667,9 +1651,6 @@ SSLCreateServerContext(const SSLConfigParams *params) { SSL_CTX_set_alpn_select_cb(ctx, SSLNetVConnection::select_next_protocol, NULL); #endif /* TS_USE_TLS_ALPN */ - // TODO: Allow control over tickets and ticket path when using SSLCreateServerContext - ssl_context_enable_tickets(ctx, NULL); - #ifdef HAVE_OPENSSL_OCSP_STAPLING if (SSLConfigParams::ssl_ocsp_enabled) { Debug("ssl", "ssl ocsp stapling is enabled"); @@ -1683,11 +1664,27 @@ SSLCreateServerContext(const SSLConfigParams *params) { } #endif /* HAVE_OPENSSL_OCSP_STAPLING */ - if (SSLConfigParams::init_ssl_ctx_cb) { SSLConfigParams::init_ssl_ctx_cb(ctx, true); } + return ctx; + +fail: + SSL_CLEAR_PW_REFERENCES(ud, ctx) + SSL_CTX_free(ctx); + for (unsigned int i = 0; i < certList.length(); i++) { + X509_free(certList[i]); + } + + return NULL; +} + +SSL_CTX * +SSLCreateServerContext(const SSLConfigParams *params) { + const ssl_user_config sslMultCertSettings; + Vec cert_list; + return SSLInitServerContext(params, sslMultCertSettings, cert_list); } static SSL_CTX * @@ -1703,19 +1700,6 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons return ctx; } - // The certificate callbacks are set by the caller only - // for the default certificate - - SSL_CTX_set_info_callback(ctx, ssl_callback_info); - -#if TS_USE_TLS_NPN - SSL_CTX_set_next_protos_advertised_cb(ctx, SSLNetVConnection::advertise_next_protocol, NULL); -#endif /* TS_USE_TLS_NPN */ - -#if TS_USE_TLS_ALPN - SSL_CTX_set_alpn_select_cb(ctx, SSLNetVConnection::select_next_protocol, NULL); -#endif /* TS_USE_TLS_ALPN */ - const char *certname = sslMultCertSettings.cert.get(); for (unsigned i = 0; i < cert_list.length(); ++i) { if (0 > SSLCheckServerCertNow(cert_list[i], certname)) { @@ -1734,7 +1718,6 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons keyblock = ssl_context_enable_tickets(ctx, NULL); } - // Index this certificate by the specified IP(v6) address. If the address is "*", make it the default context. if (sslMultCertSettings.addr) { if (strcmp(sslMultCertSettings.addr, "*") == 0) { @@ -1765,7 +1748,6 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons #endif } - #if defined(SSL_OP_NO_TICKET) // Session tickets are enabled by default. Disable if explicitly requested. if (sslMultCertSettings.session_ticket_enabled == 0) { @@ -1774,24 +1756,6 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons } #endif -#ifdef HAVE_OPENSSL_OCSP_STAPLING - if (SSLConfigParams::ssl_ocsp_enabled) { - Debug("ssl", "ssl ocsp stapling is enabled"); - SSL_CTX_set_tlsext_status_cb(ctx, ssl_callback_ocsp_stapling); - for (unsigned i = 0; i < cert_list.length(); ++i) { - if (!ssl_stapling_init_cert(ctx, cert_list[i], certname)) { - Warning("fail to configure SSL_CTX for OCSP Stapling info for certificate at %s", (const char *)certname); - } - } - } else { - Debug("ssl", "ssl ocsp stapling is disabled"); - } -#else - if (SSLConfigParams::ssl_ocsp_enabled) { - Warning("fail to enable ssl ocsp stapling, this openssl version does not support it"); - } -#endif /* HAVE_OPENSSL_OCSP_STAPLING */ - // Insert additional mappings. Note that this maps multiple keys to the same value, so when // this code is updated to reconfigure the SSL certificates, it will need some sort of // refcounting or alternate way of avoiding double frees. @@ -1802,11 +1766,6 @@ ssl_store_ssl_context(const SSLConfigParams *params, SSLCertLookup *lookup, cons } } - if (inserted) { - if (SSLConfigParams::init_ssl_ctx_cb) { - SSLConfigParams::init_ssl_ctx_cb(ctx, true); - } - } if (!inserted) { if (ctx != NULL) { SSL_CTX_free(ctx); From 2897ecd973029d3277b0e773d3f94b6052719054 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Christensen Date: Mon, 4 Jan 2016 23:13:11 -0800 Subject: [PATCH 3/4] Change signature to TSSslContextCreate(void) --- proxy/api/ts/ts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h index 2b3d047844c..95770a07bad 100644 --- a/proxy/api/ts/ts.h +++ b/proxy/api/ts/ts.h @@ -1226,7 +1226,7 @@ tsapi TSSslConnection TSVConnSSLConnectionGet(TSVConn sslp); tsapi TSSslContext TSSslContextFindByName(const char *name); tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const *); // Create a new SSL context based on the settings in records.config -tsapi TSSslContext TSSslContextCreate(); +tsapi TSSslContext TSSslContextCreate(void); // Returns 1 if the sslp argument refers to a SSL connection tsapi int TSVConnIsSsl(TSVConn sslp); From 22fc89be93d5bb1af09d4b98667cf2cc40bdeb07 Mon Sep 17 00:00:00 2001 From: Mathias Biilmann Christensen Date: Fri, 15 Jan 2016 19:26:40 -0800 Subject: [PATCH 4/4] Add TSSslContextDestroy method --- proxy/InkAPI.cc | 6 ++++++ proxy/api/ts/ts.h | 1 + 2 files changed, 7 insertions(+) diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc index 128fd36ad51..f254b5577b0 100644 --- a/proxy/InkAPI.cc +++ b/proxy/InkAPI.cc @@ -8880,6 +8880,12 @@ TSSslContextCreate() return ret; } +tsapi void +TSSslContextDestroy(TSSslContext ctx) +{ + SSL_CTX *ssl_ctx = reinterpret_cast(ctx); + SSL_CTX_free(ssl_ctx); +} tsapi int TSVConnIsSsl(TSVConn sslp) diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h index 95770a07bad..8a3d0b483f7 100644 --- a/proxy/api/ts/ts.h +++ b/proxy/api/ts/ts.h @@ -1227,6 +1227,7 @@ tsapi TSSslContext TSSslContextFindByName(const char *name); tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const *); // Create a new SSL context based on the settings in records.config tsapi TSSslContext TSSslContextCreate(void); +tsapi void TSSslContextDestroy(TSSslContext ctx); // Returns 1 if the sslp argument refers to a SSL connection tsapi int TSVConnIsSsl(TSVConn sslp);