TS-841: Move SSLNetAccept and SSLNetProcessor into eponymous files
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/59bf86f1 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/59bf86f1 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/59bf86f1 Branch: refs/heads/master Commit: 59bf86f11eb2b274f7ceb106a78111d76abb0c1f Parents: 19f3673 Author: James Peach <[email protected]> Authored: Tue Feb 7 21:21:10 2012 -0800 Committer: James Peach <[email protected]> Committed: Sun Feb 19 08:46:07 2012 -0800 ---------------------------------------------------------------------- iocore/net/Makefile.am | 4 +- iocore/net/P_SSLNetProcessor.h | 4 +- iocore/net/SSLNet.cc | 476 ------------------------------- iocore/net/SSLNetAccept.cc | 86 ++++++ iocore/net/SSLNetProcessor.cc | 534 +++++++++++++++++++++++++++++++++++ iocore/net/SSLUnixNet.cc | 157 ---------- 6 files changed, 625 insertions(+), 636 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/Makefile.am ---------------------------------------------------------------------- diff --git a/iocore/net/Makefile.am b/iocore/net/Makefile.am index 2fc5c6d..e78aba2 100644 --- a/iocore/net/Makefile.am +++ b/iocore/net/Makefile.am @@ -76,9 +76,9 @@ libinknet_a_SOURCES = \ Socks.cc \ SSLCertLookup.cc \ SSLConfig.cc \ - SSLNet.cc \ + SSLNetProcessor.cc \ SSLNetVConnection.cc \ - SSLUnixNet.cc \ + SSLNetAccept.cc \ UDPIOEvent.cc \ UnixConnection.cc \ UnixNetAccept.cc \ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/P_SSLNetProcessor.h ---------------------------------------------------------------------- diff --git a/iocore/net/P_SSLNetProcessor.h b/iocore/net/P_SSLNetProcessor.h index 672cbfd..4ac07b6 100644 --- a/iocore/net/P_SSLNetProcessor.h +++ b/iocore/net/P_SSLNetProcessor.h @@ -82,6 +82,8 @@ public: SSL_CTX *client_ctx; ProxyMutex **sslMutexArray; + static EventType ET_SSL; + // // Private // @@ -96,7 +98,7 @@ public: // netProcessor connect functions. virtual UnixNetVConnection *allocateThread(EThread * t); virtual void freeThread(UnixNetVConnection * vc, EThread * t); -virtual NetAccept *createNetAccept(); + virtual NetAccept *createNetAccept(); #else // #if defined (_IOCORE_WIN32) public: http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/SSLNet.cc ---------------------------------------------------------------------- diff --git a/iocore/net/SSLNet.cc b/iocore/net/SSLNet.cc deleted file mode 100644 index 6afedff..0000000 --- a/iocore/net/SSLNet.cc +++ /dev/null @@ -1,476 +0,0 @@ -/** @file - - Common SSL initialization/cleanup fuctions from SSLNet.h - - @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. - */ - -#include "ink_config.h" - -#include "P_Net.h" -#include "I_Layout.h" -#include "openssl/engine.h" -#include "openssl/dso.h" - -void sslLockingCallback(int mode, int type, const char *file, int line); -unsigned long SSL_pthreads_thread_id(); - -bool SSLNetProcessor::open_ssl_initialized = false; - -#if TS_USE_TLS_NPN -static int -npn_advertise_protocols(SSL *ssl, - const unsigned char **out, unsigned int *outlen, void *arg) -{ - static const unsigned char protocols[] = - "\x08http/1.0" - "\x08http/1.1"; - - SSLNetProcessor * sslNetProc = (SSLNetProcessor *)arg; - - // XXX: At some point we need to figure out how to know which protocols to - // advertise. - (void)sslNetProc; - - // For currently defined protocol strings, - // see http://technotes.googlecode.com/git/nextprotoneg.html. The OpenSSL - // documentation tells us to return a string in "wire format". The draft NPN - // RFC helpfuly refuses to document the wire format. The above link says we - // need to send length-prefixed strings, but does not say how many bytes the - // length is. Nice. - *out = protocols; - *outlen = sizeof(protocols) - 1; - - // Successful return tells OpenSSL to advertise. - return SSL_TLSEXT_ERR_OK; -} -#endif /* TS_USE_TLS_NPN */ - - -int -SSL_CTX_add_extra_chain_cert_file(SSL_CTX * ctx, const char *file) -{ - BIO *in; - int ret = 0; - X509 *x = NULL; - - in = BIO_new(BIO_s_file_internal()); - if (in == NULL) { - SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB); - goto end; - } - - if (BIO_read_filename(in, file) <= 0) { - SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); - goto end; - } - - // j = ERR_R_PEM_LIB; - while ((x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata)) != NULL) { - ret = SSL_CTX_add_extra_chain_cert(ctx, x); - if (!ret) { - X509_free(x); - BIO_free(in); - return -1; - } - } -/* x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata); - if (x == NULL) { - SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, j); - goto end; - } - - ret = SSL_CTX_add_extra_chain_cert(ctx, x);*/ -end: - // if (x != NULL) X509_free(x); - if (in != NULL) - BIO_free(in); - return (ret); -} - - -void -SSLNetProcessor::cleanup(void) -{ - if (sslMutexArray) { - CRYPTO_set_locking_callback(NULL); - CRYPTO_set_id_callback(NULL); - for (int i = 0; i < CRYPTO_num_locks(); i++) { - sslMutexArray[i]->free(); - } - OPENSSL_free(sslMutexArray); - sslMutexArray = NULL; - } - - if (ctx) - SSL_CTX_free(ctx); - ctx = NULL; - if (client_ctx) - SSL_CTX_free(client_ctx); - client_ctx = NULL; -} - -void -SSLNetProcessor::initSSLLocks(void) -{ - - sslMutexArray = (ProxyMutex **) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(ProxyMutex *)); - - for (int i = 0; i < CRYPTO_num_locks(); i++) { - sslMutexArray[i] = new_ProxyMutex(); - } - CRYPTO_set_locking_callback((void (*)(int, int, const char *, int)) sslLockingCallback); - CRYPTO_set_id_callback(SSL_pthreads_thread_id); -} - -int -SSLNetProcessor::reconfigure(void) -{ - int ssl_mode = SslConfigParams::SSL_TERM_MODE_NONE, err = 0; - int sslServerEnabled = 0; - - cleanup(); - - if (!open_ssl_initialized) { - open_ssl_initialized = true; - SSL_load_error_strings(); - SSL_library_init(); - initSSLLocks(); - } - - SslConfigParams *param = sslTerminationConfig.acquire(); - ink_assert(param); - - ssl_mode = param->getTerminationMode(); - sslServerEnabled = ssl_mode & SslConfigParams::SSL_TERM_MODE_CLIENT; - - if (sslServerEnabled) { - // Only init server stuff if SSL is enabled in the config file - err = initSSL(param); - if (err == 0) { - sslCertLookup.init(param); - } else { - logSSLError("Can't initialize the SSL library, disabling SSL termination!"); - sslTerminationConfig.clearTermEnabled(); - } - } - // Enable client regardless of config file setttings as remap file - // can cause HTTP layer to connect using SSL. But only if SSL - // initialization hasn't failed already. - if (err == 0) { - err = initSSLClient(param); - if (err != 0) - logSSLError("Can't initialize the SSL client, HTTPS in remap rules will not function"); - } - - sslTerminationConfig.release(param); - return (err); -} - -void -sslLockingCallback(int mode, int type, const char *file, int line) -{ - NOWARN_UNUSED(file); - (void) line; - if (mode & CRYPTO_LOCK) { - MUTEX_TAKE_LOCK(ssl_NetProcessor.sslMutexArray[type], this_ethread()); - } else if (mode & CRYPTO_UNLOCK) - MUTEX_UNTAKE_LOCK(ssl_NetProcessor.sslMutexArray[type], this_ethread()); - else - ink_debug_assert(0); -} - -unsigned long -SSL_pthreads_thread_id() -{ - EThread *eth = this_ethread(); - return (unsigned long) (eth->id); -} - - - - -void -SSLNetProcessor::logSSLError(const char *errStr, int critical) -{ - unsigned long l; - char buf[256]; - const char *file, *data; - int line, flags; - unsigned long es; - - if (!critical) { - if (errStr) { - Debug("ssl_error", "SSL ERROR: %s.", errStr); - } else { - Debug("ssl_error", "SSL ERROR."); - } - } else { - if (errStr) { - Error("SSL ERROR: %s.", errStr); - } else { - Error("SSL ERROR."); - } - } - - es = CRYPTO_thread_id(); - while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) { - if (!critical) { - Debug("ssl_error", "SSL::%lu:%s:%s:%d:%s", es, - ERR_error_string(l, buf), file, line, (flags & ERR_TXT_STRING) ? data : ""); - } else { - Error("SSL::%lu:%s:%s:%d:%s", es, ERR_error_string(l, buf), file, line, (flags & ERR_TXT_STRING) ? data : ""); - } - } -} - -int -SSLNetProcessor::initSSL(SslConfigParams * param) -{ -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD now - const SSL_METHOD *meth = NULL; -#else - SSL_METHOD *meth = NULL; -#endif - // Note that we do not call RAND_seed() explicitly here, we depend on OpenSSL - // to do the seeding of the PRNG for us. This is the case for all platforms that - // has /dev/urandom for example. - - meth = SSLv23_server_method(); - ctx = SSL_CTX_new(meth); - if (!ctx) { - logSSLError("Cannot create new server contex."); - return (-1); - } - - return (initSSLServerCTX(param, ctx, param->serverCertPath, param->serverCertChainPath, param->serverKeyPath, true)); -} - -int -SSLNetProcessor::initSSLServerCTX(SslConfigParams * param, SSL_CTX * lCtx, - char *serverCertPtr, char *serverCaCertPtr, char *serverKeyPtr, bool defaultEnabled) -{ - int session_id_context; - int server_verify_client; - char *completeServerCertPath; - - // disable selected protocols - SSL_CTX_set_options(lCtx, param->ssl_ctx_options); - - switch (param->ssl_session_cache) { - case SslConfigParams::SSL_SESSION_CACHE_MODE_OFF: - SSL_CTX_set_session_cache_mode(lCtx, SSL_SESS_CACHE_OFF|SSL_SESS_CACHE_NO_INTERNAL); - break; - case SslConfigParams::SSL_SESSION_CACHE_MODE_SERVER: - SSL_CTX_set_session_cache_mode(lCtx, SSL_SESS_CACHE_SERVER); - SSL_CTX_sess_set_cache_size(lCtx, param->ssl_session_cache_size); - break; - } - - //might want to make configurable at some point. - verify_depth = param->verify_depth; - SSL_CTX_set_quiet_shutdown(lCtx, 1); - - if (defaultEnabled) { - if (SSL_CTX_use_certificate_file(lCtx, param->serverCertPath, SSL_FILETYPE_PEM) <= 0) { - Error ("SSL ERROR: Cannot use server certificate file: %s", param->serverCertPath); - return -2; - } - if (param->serverKeyPath != NULL) { - if (SSL_CTX_use_PrivateKey_file(lCtx, param->serverKeyPath, SSL_FILETYPE_PEM) <= 0) { - Error("SSL ERROR: Cannot use server private key file: %s", param->serverKeyPath); - return -3; - } - } else // assume key is contained in the cert file. - { - if (SSL_CTX_use_PrivateKey_file(lCtx, param->serverCertPath, SSL_FILETYPE_PEM) <= 0) { - Error("SSL ERROR: Cannot use server private key file: %s", param->serverKeyPath); - return -3; - } - } - - if (param->serverCertChainPath) { - char *completeServerCaCertPath = Layout::relative_to (param->getServerCACertPathOnly(), param->serverCertChainPath); - if (SSL_CTX_add_extra_chain_cert_file(lCtx, param->serverCertChainPath) <= 0) { - Error ("SSL ERROR: Cannot use server certificate chain file: %s", completeServerCaCertPath); - ats_free(completeServerCaCertPath); - return -2; - } - ats_free(completeServerCaCertPath); - } - } else { - completeServerCertPath = Layout::relative_to (param->getServerCertPathOnly(), serverCertPtr); - - if (SSL_CTX_use_certificate_file(lCtx, completeServerCertPath, SSL_FILETYPE_PEM) <= 0) { - Error ("SSL ERROR: Cannot use server certificate file: %s", completeServerCertPath); - ats_free(completeServerCertPath); - return -2; - } - if (serverCaCertPtr) { - char *completeServerCaCertPath = Layout::relative_to (param->getServerCACertPathOnly(), serverCaCertPtr); - if (SSL_CTX_add_extra_chain_cert_file(lCtx, completeServerCaCertPath) <= 0) { - Error ("SSL ERROR: Cannot use server certificate chain file: %s", completeServerCaCertPath); - ats_free(completeServerCaCertPath); - return -2; - } - ats_free(completeServerCaCertPath); - } - - if (serverKeyPtr == NULL) // assume private key is contained in cert obtained from multicert file. - { - if (SSL_CTX_use_PrivateKey_file(lCtx, completeServerCertPath, SSL_FILETYPE_PEM) <= 0) { - Error("SSL ERROR: Cannot use server private key file: %s", completeServerCertPath); - ats_free(completeServerCertPath); - return -3; - } - } else { - if (param->getServerKeyPathOnly() != NULL) { - char *completeServerKeyPath = Layout::get()->relative_to(param->getServerKeyPathOnly(), serverKeyPtr); - if (SSL_CTX_use_PrivateKey_file(lCtx, completeServerKeyPath, SSL_FILETYPE_PEM) <= 0) { - Error("SSL ERROR: Cannot use server private key file: %s", completeServerKeyPath); - ats_free(completeServerKeyPath); - return -3; - } - ats_free(completeServerKeyPath); - } else { - logSSLError("Empty ssl private key path in records.config."); - } - - } - ats_free(completeServerCertPath); - - - } - - if (!SSL_CTX_check_private_key(lCtx)) { - logSSLError("Server private key does not match the certificate public key"); - return -4; - } - - - if (param->clientCertLevel != 0) { - - if (param->CACertFilename != NULL && param->CACertPath != NULL) { - if ((!SSL_CTX_load_verify_locations(lCtx, param->CACertFilename, param->CACertPath)) || - (!SSL_CTX_set_default_verify_paths(lCtx))) { - logSSLError("CA Certificate file or CA Certificate path invalid"); - return -5; - } - } - - if (param->clientCertLevel == 2) - server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE; - else if (param->clientCertLevel == 1) - server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; - else // disable client cert support - { - server_verify_client = SSL_VERIFY_NONE; - Error("Illegal Client Certification Level in records.config\n"); - } - - session_id_context = 1; - - SSL_CTX_set_verify(lCtx, server_verify_client, NULL); - SSL_CTX_set_verify_depth(lCtx, verify_depth); - SSL_CTX_set_session_id_context(lCtx, (const unsigned char *) &session_id_context, sizeof session_id_context); - - SSL_CTX_set_client_CA_list(lCtx, SSL_load_client_CA_file(param->CACertFilename)); - } - - - if (param->cipherSuite != NULL) { - if (!SSL_CTX_set_cipher_list(lCtx, param->cipherSuite)) { - logSSLError("Invalid Cipher Suite in records.config"); - return -6; - } - } - -#if TS_USE_TLS_NPN - SSL_CTX_set_next_protos_advertised_cb(lCtx, npn_advertise_protocols, this); -#endif /* TS_USE_TLS_NPN */ - - return 0; - -} - -int -SSLNetProcessor::initSSLClient(SslConfigParams * param) -{ -#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD now - const SSL_METHOD *meth = NULL; -#else - SSL_METHOD *meth = NULL; -#endif - int client_verify_server; - char *clientKeyPtr = NULL; - - // Note that we do not call RAND_seed() explicitly here, we depend on OpenSSL - // to do the seeding of the PRNG for us. This is the case for all platforms that - // has /dev/urandom for example. - - client_verify_server = param->clientVerify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; - meth = SSLv23_client_method(); - client_ctx = SSL_CTX_new(meth); - - // disable selected protocols - SSL_CTX_set_options(client_ctx, param->ssl_ctx_options); - verify_depth = param->client_verify_depth; - if (!client_ctx) { - logSSLError("Cannot create new client contex."); - return (-1); - } - // if no path is given for the client private key, - // assume it is contained in the client certificate file. - clientKeyPtr = param->clientKeyPath; - if (clientKeyPtr == NULL) - clientKeyPtr = param->clientCertPath; - - if (param->clientCertPath != 0) { - if (SSL_CTX_use_certificate_file(client_ctx, param->clientCertPath, SSL_FILETYPE_PEM) <= 0) { - Error ("SSL Error: Cannot use client certificate file: %s", param->clientCertPath); - return (-2); - } - - if (SSL_CTX_use_PrivateKey_file(client_ctx, clientKeyPtr, SSL_FILETYPE_PEM) <= 0) { - Error ("SSL ERROR: Cannot use client private key file: %s", clientKeyPtr); - return (-3); - } - - if (!SSL_CTX_check_private_key(client_ctx)) { - Error("SSL ERROR: Client private key (%s) does not match the certificate public key (%s)", clientKeyPtr, param->clientCertPath); - return (-4); - } - } - - if (param->clientVerify) { - SSL_CTX_set_verify(client_ctx, client_verify_server, NULL); - /*???*/ SSL_CTX_set_verify_depth(client_ctx, verify_depth); - // ??? - - if (param->clientCACertFilename != NULL && param->clientCACertPath != NULL) { - if ((!SSL_CTX_load_verify_locations(client_ctx, param->clientCACertFilename, - param->clientCACertPath)) || - (!SSL_CTX_set_default_verify_paths(client_ctx))) { - Error("SSL ERROR: Client CA Certificate file (%s) or CA Certificate path (%s) invalid", param->clientCACertFilename, param->clientCACertPath); - return (-5); - } - } - } - return (0); -} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/SSLNetAccept.cc ---------------------------------------------------------------------- diff --git a/iocore/net/SSLNetAccept.cc b/iocore/net/SSLNetAccept.cc new file mode 100644 index 0000000..a321f89 --- /dev/null +++ b/iocore/net/SSLNetAccept.cc @@ -0,0 +1,86 @@ +/** @file + + @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. + */ + +#include "ink_config.h" +#include "P_Net.h" + +typedef int (SSLNetAccept::*SSLNetAcceptHandler) (int, void *); + +// Virtual function allows the correct +// etype to be used in NetAccept functions (ET_SSL +// or ET_NET). +EventType +SSLNetAccept::getEtype() +{ + return SSLNetProcessor::ET_SSL; +} + +// Functions all THREAD_FREE and THREAD_ALLOC to be performed +// for both SSL and regular NetVConnection transparent to +// accept functions. +UnixNetVConnection * +SSLNetAccept::allocateThread(EThread *t) +{ + return ((UnixNetVConnection *) THREAD_ALLOC(sslNetVCAllocator, t)); +} + +void +SSLNetAccept::freeThread(UnixNetVConnection *vc, EThread *t) +{ + ink_assert(!vc->from_accept_thread); + THREAD_FREE((SSLNetVConnection *) vc, sslNetVCAllocator, t); +} + +// This allocates directly on the class allocator, used for accept threads. +UnixNetVConnection * +SSLNetAccept::allocateGlobal() +{ + return (UnixNetVConnection *)sslNetVCAllocator.alloc(); +} + +void +SSLNetAccept::init_accept_per_thread() +{ + int i, n; + if (do_listen(NON_BLOCKING)) + return; + if (accept_fn == net_accept) + SET_HANDLER((SSLNetAcceptHandler) & SSLNetAccept::acceptFastEvent); + else + SET_HANDLER((SSLNetAcceptHandler) & SSLNetAccept::acceptEvent); + period = ACCEPT_PERIOD; + NetAccept *a = this; + n = eventProcessor.n_threads_for_type[SSLNetProcessor::ET_SSL]; + for (i = 0; i < n; i++) { + if (i < n - 1) { + a = NEW(new SSLNetAccept); + *a = *this; + } else + a = this; + EThread *t = eventProcessor.eventthread[SSLNetProcessor::ET_SSL][i]; + + PollDescriptor *pd = get_PollDescriptor(t); + if (ep.start(pd, this, EVENTIO_READ) < 0) + Debug("iocore_net", "error starting EventIO"); + a->mutex = get_NetHandler(t)->mutex; + t->schedule_every(a, period, etype); + } +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/SSLNetProcessor.cc ---------------------------------------------------------------------- diff --git a/iocore/net/SSLNetProcessor.cc b/iocore/net/SSLNetProcessor.cc new file mode 100644 index 0000000..c61cfc8 --- /dev/null +++ b/iocore/net/SSLNetProcessor.cc @@ -0,0 +1,534 @@ +/** @file + + @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. + */ + +#include "ink_config.h" + +#include "P_Net.h" +#include "I_Layout.h" +#include "openssl/engine.h" + +// +// Global Data +// + +SSLNetProcessor ssl_NetProcessor; +NetProcessor & sslNetProcessor = ssl_NetProcessor; + +EventType SSLNetProcessor::ET_SSL; + +void sslLockingCallback(int mode, int type, const char *file, int line); +unsigned long SSL_pthreads_thread_id(); + +bool SSLNetProcessor::open_ssl_initialized = false; + +#if TS_USE_TLS_NPN +static int +npn_advertise_protocols(SSL *ssl, + const unsigned char **out, unsigned int *outlen, void *arg) +{ + static const unsigned char protocols[] = + "\x08http/1.0" + "\x08http/1.1"; + + SSLNetProcessor * sslNetProc = (SSLNetProcessor *)arg; + + // XXX: At some point we need to figure out how to know which protocols to + // advertise. + (void)sslNetProc; + + // For currently defined protocol strings, + // see http://technotes.googlecode.com/git/nextprotoneg.html. The OpenSSL + // documentation tells us to return a string in "wire format". The draft NPN + // RFC helpfuly refuses to document the wire format. The above link says we + // need to send length-prefixed strings, but does not say how many bytes the + // length is. Nice. + *out = protocols; + *outlen = sizeof(protocols) - 1; + + // Successful return tells OpenSSL to advertise. + return SSL_TLSEXT_ERR_OK; +} +#endif /* TS_USE_TLS_NPN */ + +static int +SSL_CTX_add_extra_chain_cert_file(SSL_CTX * ctx, const char *file) +{ + BIO *in; + int ret = 0; + X509 *x = NULL; + + in = BIO_new(BIO_s_file_internal()); + if (in == NULL) { + SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); + goto end; + } + + // j = ERR_R_PEM_LIB; + while ((x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata)) != NULL) { + ret = SSL_CTX_add_extra_chain_cert(ctx, x); + if (!ret) { + X509_free(x); + BIO_free(in); + return -1; + } + } +/* x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata); + if (x == NULL) { + SSLerr(SSL_F_SSL_USE_CERTIFICATE_FILE, j); + goto end; + } + + ret = SSL_CTX_add_extra_chain_cert(ctx, x);*/ +end: + // if (x != NULL) X509_free(x); + if (in != NULL) + BIO_free(in); + return (ret); +} + +void +SSLNetProcessor::cleanup(void) +{ + if (sslMutexArray) { + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + for (int i = 0; i < CRYPTO_num_locks(); i++) { + sslMutexArray[i]->free(); + } + OPENSSL_free(sslMutexArray); + sslMutexArray = NULL; + } + + if (ctx) + SSL_CTX_free(ctx); + ctx = NULL; + if (client_ctx) + SSL_CTX_free(client_ctx); + client_ctx = NULL; +} + +void +SSLNetProcessor::initSSLLocks(void) +{ + + sslMutexArray = (ProxyMutex **) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(ProxyMutex *)); + + for (int i = 0; i < CRYPTO_num_locks(); i++) { + sslMutexArray[i] = new_ProxyMutex(); + } + CRYPTO_set_locking_callback((void (*)(int, int, const char *, int)) sslLockingCallback); + CRYPTO_set_id_callback(SSL_pthreads_thread_id); +} + +int +SSLNetProcessor::reconfigure(void) +{ + int ssl_mode = SslConfigParams::SSL_TERM_MODE_NONE, err = 0; + int sslServerEnabled = 0; + + cleanup(); + + if (!open_ssl_initialized) { + open_ssl_initialized = true; + SSL_load_error_strings(); + SSL_library_init(); + initSSLLocks(); + } + + SslConfigParams *param = sslTerminationConfig.acquire(); + ink_assert(param); + + ssl_mode = param->getTerminationMode(); + sslServerEnabled = ssl_mode & SslConfigParams::SSL_TERM_MODE_CLIENT; + + if (sslServerEnabled) { + // Only init server stuff if SSL is enabled in the config file + err = initSSL(param); + if (err == 0) { + sslCertLookup.init(param); + } else { + logSSLError("Can't initialize the SSL library, disabling SSL termination!"); + sslTerminationConfig.clearTermEnabled(); + } + } + // Enable client regardless of config file setttings as remap file + // can cause HTTP layer to connect using SSL. But only if SSL + // initialization hasn't failed already. + if (err == 0) { + err = initSSLClient(param); + if (err != 0) + logSSLError("Can't initialize the SSL client, HTTPS in remap rules will not function"); + } + + sslTerminationConfig.release(param); + return (err); +} + +void +sslLockingCallback(int mode, int type, const char *file, int line) +{ + NOWARN_UNUSED(file); + (void) line; + if (mode & CRYPTO_LOCK) { + MUTEX_TAKE_LOCK(ssl_NetProcessor.sslMutexArray[type], this_ethread()); + } else if (mode & CRYPTO_UNLOCK) + MUTEX_UNTAKE_LOCK(ssl_NetProcessor.sslMutexArray[type], this_ethread()); + else + ink_debug_assert(0); +} + +unsigned long +SSL_pthreads_thread_id() +{ + EThread *eth = this_ethread(); + return (unsigned long) (eth->id); +} + +void +SSLNetProcessor::logSSLError(const char *errStr, int critical) +{ + unsigned long l; + char buf[256]; + const char *file, *data; + int line, flags; + unsigned long es; + + if (!critical) { + if (errStr) { + Debug("ssl_error", "SSL ERROR: %s.", errStr); + } else { + Debug("ssl_error", "SSL ERROR."); + } + } else { + if (errStr) { + Error("SSL ERROR: %s.", errStr); + } else { + Error("SSL ERROR."); + } + } + + es = CRYPTO_thread_id(); + while ((l = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) { + if (!critical) { + Debug("ssl_error", "SSL::%lu:%s:%s:%d:%s", es, + ERR_error_string(l, buf), file, line, (flags & ERR_TXT_STRING) ? data : ""); + } else { + Error("SSL::%lu:%s:%s:%d:%s", es, ERR_error_string(l, buf), file, line, (flags & ERR_TXT_STRING) ? data : ""); + } + } +} + +int +SSLNetProcessor::initSSL(SslConfigParams * param) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD now + const SSL_METHOD *meth = NULL; +#else + SSL_METHOD *meth = NULL; +#endif + // Note that we do not call RAND_seed() explicitly here, we depend on OpenSSL + // to do the seeding of the PRNG for us. This is the case for all platforms that + // has /dev/urandom for example. + + meth = SSLv23_server_method(); + ctx = SSL_CTX_new(meth); + if (!ctx) { + logSSLError("Cannot create new server contex."); + return (-1); + } + + return (initSSLServerCTX(param, ctx, param->serverCertPath, param->serverCertChainPath, param->serverKeyPath, true)); +} + +int +SSLNetProcessor::initSSLServerCTX(SslConfigParams * param, SSL_CTX * lCtx, + char *serverCertPtr, char *serverCaCertPtr, char *serverKeyPtr, bool defaultEnabled) +{ + int session_id_context; + int server_verify_client; + char *completeServerCertPath; + + // disable selected protocols + SSL_CTX_set_options(lCtx, param->ssl_ctx_options); + + switch (param->ssl_session_cache) { + case SslConfigParams::SSL_SESSION_CACHE_MODE_OFF: + SSL_CTX_set_session_cache_mode(lCtx, SSL_SESS_CACHE_OFF|SSL_SESS_CACHE_NO_INTERNAL); + break; + case SslConfigParams::SSL_SESSION_CACHE_MODE_SERVER: + SSL_CTX_set_session_cache_mode(lCtx, SSL_SESS_CACHE_SERVER); + SSL_CTX_sess_set_cache_size(lCtx, param->ssl_session_cache_size); + break; + } + + //might want to make configurable at some point. + verify_depth = param->verify_depth; + SSL_CTX_set_quiet_shutdown(lCtx, 1); + + if (defaultEnabled) { + if (SSL_CTX_use_certificate_file(lCtx, param->serverCertPath, SSL_FILETYPE_PEM) <= 0) { + Error ("SSL ERROR: Cannot use server certificate file: %s", param->serverCertPath); + return -2; + } + if (param->serverKeyPath != NULL) { + if (SSL_CTX_use_PrivateKey_file(lCtx, param->serverKeyPath, SSL_FILETYPE_PEM) <= 0) { + Error("SSL ERROR: Cannot use server private key file: %s", param->serverKeyPath); + return -3; + } + } else // assume key is contained in the cert file. + { + if (SSL_CTX_use_PrivateKey_file(lCtx, param->serverCertPath, SSL_FILETYPE_PEM) <= 0) { + Error("SSL ERROR: Cannot use server private key file: %s", param->serverKeyPath); + return -3; + } + } + + if (param->serverCertChainPath) { + char *completeServerCaCertPath = Layout::relative_to (param->getServerCACertPathOnly(), param->serverCertChainPath); + if (SSL_CTX_add_extra_chain_cert_file(lCtx, param->serverCertChainPath) <= 0) { + Error ("SSL ERROR: Cannot use server certificate chain file: %s", completeServerCaCertPath); + ats_free(completeServerCaCertPath); + return -2; + } + ats_free(completeServerCaCertPath); + } + } else { + completeServerCertPath = Layout::relative_to (param->getServerCertPathOnly(), serverCertPtr); + + if (SSL_CTX_use_certificate_file(lCtx, completeServerCertPath, SSL_FILETYPE_PEM) <= 0) { + Error ("SSL ERROR: Cannot use server certificate file: %s", completeServerCertPath); + ats_free(completeServerCertPath); + return -2; + } + if (serverCaCertPtr) { + char *completeServerCaCertPath = Layout::relative_to (param->getServerCACertPathOnly(), serverCaCertPtr); + if (SSL_CTX_add_extra_chain_cert_file(lCtx, completeServerCaCertPath) <= 0) { + Error ("SSL ERROR: Cannot use server certificate chain file: %s", completeServerCaCertPath); + ats_free(completeServerCaCertPath); + return -2; + } + ats_free(completeServerCaCertPath); + } + + if (serverKeyPtr == NULL) // assume private key is contained in cert obtained from multicert file. + { + if (SSL_CTX_use_PrivateKey_file(lCtx, completeServerCertPath, SSL_FILETYPE_PEM) <= 0) { + Error("SSL ERROR: Cannot use server private key file: %s", completeServerCertPath); + ats_free(completeServerCertPath); + return -3; + } + } else { + if (param->getServerKeyPathOnly() != NULL) { + char *completeServerKeyPath = Layout::get()->relative_to(param->getServerKeyPathOnly(), serverKeyPtr); + if (SSL_CTX_use_PrivateKey_file(lCtx, completeServerKeyPath, SSL_FILETYPE_PEM) <= 0) { + Error("SSL ERROR: Cannot use server private key file: %s", completeServerKeyPath); + ats_free(completeServerKeyPath); + return -3; + } + ats_free(completeServerKeyPath); + } else { + logSSLError("Empty ssl private key path in records.config."); + } + + } + ats_free(completeServerCertPath); + + } + + if (!SSL_CTX_check_private_key(lCtx)) { + logSSLError("Server private key does not match the certificate public key"); + return -4; + } + + if (param->clientCertLevel != 0) { + + if (param->CACertFilename != NULL && param->CACertPath != NULL) { + if ((!SSL_CTX_load_verify_locations(lCtx, param->CACertFilename, param->CACertPath)) || + (!SSL_CTX_set_default_verify_paths(lCtx))) { + logSSLError("CA Certificate file or CA Certificate path invalid"); + return -5; + } + } + + if (param->clientCertLevel == 2) + server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE; + else if (param->clientCertLevel == 1) + server_verify_client = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; + else // disable client cert support + { + server_verify_client = SSL_VERIFY_NONE; + Error("Illegal Client Certification Level in records.config\n"); + } + + session_id_context = 1; + + SSL_CTX_set_verify(lCtx, server_verify_client, NULL); + SSL_CTX_set_verify_depth(lCtx, verify_depth); + SSL_CTX_set_session_id_context(lCtx, (const unsigned char *) &session_id_context, sizeof session_id_context); + + SSL_CTX_set_client_CA_list(lCtx, SSL_load_client_CA_file(param->CACertFilename)); + } + + if (param->cipherSuite != NULL) { + if (!SSL_CTX_set_cipher_list(lCtx, param->cipherSuite)) { + logSSLError("Invalid Cipher Suite in records.config"); + return -6; + } + } + +#if TS_USE_TLS_NPN + SSL_CTX_set_next_protos_advertised_cb(lCtx, npn_advertise_protocols, this); +#endif /* TS_USE_TLS_NPN */ + + return 0; + +} + +int +SSLNetProcessor::initSSLClient(SslConfigParams * param) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD now + const SSL_METHOD *meth = NULL; +#else + SSL_METHOD *meth = NULL; +#endif + int client_verify_server; + char *clientKeyPtr = NULL; + + // Note that we do not call RAND_seed() explicitly here, we depend on OpenSSL + // to do the seeding of the PRNG for us. This is the case for all platforms that + // has /dev/urandom for example. + + client_verify_server = param->clientVerify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE; + meth = SSLv23_client_method(); + client_ctx = SSL_CTX_new(meth); + + // disable selected protocols + SSL_CTX_set_options(client_ctx, param->ssl_ctx_options); + verify_depth = param->client_verify_depth; + if (!client_ctx) { + logSSLError("Cannot create new client contex."); + return (-1); + } + // if no path is given for the client private key, + // assume it is contained in the client certificate file. + clientKeyPtr = param->clientKeyPath; + if (clientKeyPtr == NULL) + clientKeyPtr = param->clientCertPath; + + if (param->clientCertPath != 0) { + if (SSL_CTX_use_certificate_file(client_ctx, param->clientCertPath, SSL_FILETYPE_PEM) <= 0) { + Error ("SSL Error: Cannot use client certificate file: %s", param->clientCertPath); + return (-2); + } + + if (SSL_CTX_use_PrivateKey_file(client_ctx, clientKeyPtr, SSL_FILETYPE_PEM) <= 0) { + Error ("SSL ERROR: Cannot use client private key file: %s", clientKeyPtr); + return (-3); + } + + if (!SSL_CTX_check_private_key(client_ctx)) { + Error("SSL ERROR: Client private key (%s) does not match the certificate public key (%s)", clientKeyPtr, param->clientCertPath); + return (-4); + } + } + + if (param->clientVerify) { + SSL_CTX_set_verify(client_ctx, client_verify_server, NULL); + /*???*/ SSL_CTX_set_verify_depth(client_ctx, verify_depth); + // ??? + + if (param->clientCACertFilename != NULL && param->clientCACertPath != NULL) { + if ((!SSL_CTX_load_verify_locations(client_ctx, param->clientCACertFilename, + param->clientCACertPath)) || + (!SSL_CTX_set_default_verify_paths(client_ctx))) { + Error("SSL ERROR: Client CA Certificate file (%s) or CA Certificate path (%s) invalid", param->clientCACertFilename, param->clientCACertPath); + return (-5); + } + } + } + return (0); +} + +int +SSLNetProcessor::start(int number_of_ssl_threads) +{ + sslTerminationConfig.startup(); + int err = reconfigure(); + + if (err != 0) { + return -1; + } + + if (number_of_ssl_threads < 1) + return -1; + + SSLNetProcessor::ET_SSL = eventProcessor.spawn_event_threads(number_of_ssl_threads, "ET_SSL"); + if (err == 0) { + err = UnixNetProcessor::start(); + } + + return err; +} + +NetAccept * +SSLNetProcessor::createNetAccept() +{ + return ((NetAccept *) NEW(new SSLNetAccept)); +} + +// Virtual function allows etype to be upgraded to ET_SSL for SSLNetProcessor. Does +// nothing for NetProcessor +void +SSLNetProcessor::upgradeEtype(EventType & etype) +{ + if (etype == ET_NET) { + etype = ET_SSL; + } +} + +// Functions all THREAD_FREE and THREAD_ALLOC to be performed +// for both SSL and regular NetVConnection transparent to +// netProcessor connect functions. Yes it looks goofy to +// have them in both places, but it saves a bunch of +// connect code from being duplicated. +UnixNetVConnection * +SSLNetProcessor::allocateThread(EThread *t) +{ + return ((UnixNetVConnection *) THREAD_ALLOC(sslNetVCAllocator, t)); +} + +void +SSLNetProcessor::freeThread(UnixNetVConnection *vc, EThread *t) +{ + ink_assert(!vc->from_accept_thread); + THREAD_FREE((SSLNetVConnection *) vc, sslNetVCAllocator, t); +} + +SSLNetProcessor::~SSLNetProcessor() +{ + cleanup(); +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/59bf86f1/iocore/net/SSLUnixNet.cc ---------------------------------------------------------------------- diff --git a/iocore/net/SSLUnixNet.cc b/iocore/net/SSLUnixNet.cc deleted file mode 100644 index a5abc1a..0000000 --- a/iocore/net/SSLUnixNet.cc +++ /dev/null @@ -1,157 +0,0 @@ -/** @file - - Implementation of an I/O Processor for network I/O for Unix. - - @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. - */ - -#include "ink_config.h" - -#include "P_Net.h" -// -// Global Data -// - -SSLNetProcessor ssl_NetProcessor; -NetProcessor & sslNetProcessor = ssl_NetProcessor; - -EventType ET_SSL; - -typedef int (SSLNetAccept::*SSLNetAcceptHandler) (int, void *); - - -int -SSLNetProcessor::start(int number_of_ssl_threads) -{ - sslTerminationConfig.startup(); - int err = reconfigure(); - - if (err != 0) { - return -1; - } - - if (number_of_ssl_threads < 1) - return -1; - - ET_SSL = eventProcessor.spawn_event_threads(number_of_ssl_threads, "ET_SSL"); - if (err == 0) - err = UnixNetProcessor::start(); - return err; -} - - -NetAccept * -SSLNetProcessor::createNetAccept() -{ - return ((NetAccept *) NEW(new SSLNetAccept)); -} - -// Virtual function allows etype to be upgraded to ET_SSL for SSLNetProcessor. Does -// nothing for NetProcessor -void -SSLNetProcessor::upgradeEtype(EventType & etype) -{ - if (etype == ET_NET) { - etype = ET_SSL; - } -} - -// Virtual function allows the correct -// etype to be used in NetAccept functions (ET_SSL -// or ET_NET). -EventType -SSLNetAccept::getEtype() -{ - return ET_SSL; -} - -// Functions all THREAD_FREE and THREAD_ALLOC to be performed -// for both SSL and regular NetVConnection transparent to -// accept functions. -UnixNetVConnection * -SSLNetAccept::allocateThread(EThread *t) -{ - return ((UnixNetVConnection *) THREAD_ALLOC(sslNetVCAllocator, t)); -} - -void -SSLNetAccept::freeThread(UnixNetVConnection *vc, EThread *t) -{ - ink_assert(!vc->from_accept_thread); - THREAD_FREE((SSLNetVConnection *) vc, sslNetVCAllocator, t); -} - -// This allocates directly on the class allocator, used for accept threads. -UnixNetVConnection * -SSLNetAccept::allocateGlobal() -{ - return (UnixNetVConnection *)sslNetVCAllocator.alloc(); -} - -// Functions all THREAD_FREE and THREAD_ALLOC to be performed -// for both SSL and regular NetVConnection transparent to -// netProcessor connect functions. Yes it looks goofy to -// have them in both places, but it saves a bunch of -// connect code from being duplicated. -UnixNetVConnection * -SSLNetProcessor::allocateThread(EThread *t) -{ - return ((UnixNetVConnection *) THREAD_ALLOC(sslNetVCAllocator, t)); -} - -void -SSLNetProcessor::freeThread(UnixNetVConnection *vc, EThread *t) -{ - ink_assert(!vc->from_accept_thread); - THREAD_FREE((SSLNetVConnection *) vc, sslNetVCAllocator, t); -} - -void -SSLNetAccept::init_accept_per_thread() -{ - int i, n; - if (do_listen(NON_BLOCKING)) - return; - if (accept_fn == net_accept) - SET_HANDLER((SSLNetAcceptHandler) & SSLNetAccept::acceptFastEvent); - else - SET_HANDLER((SSLNetAcceptHandler) & SSLNetAccept::acceptEvent); - period = ACCEPT_PERIOD; - NetAccept *a = this; - n = eventProcessor.n_threads_for_type[ET_SSL]; - for (i = 0; i < n; i++) { - if (i < n - 1) { - a = NEW(new SSLNetAccept); - *a = *this; - } else - a = this; - EThread *t = eventProcessor.eventthread[ET_SSL][i]; - - PollDescriptor *pd = get_PollDescriptor(t); - if (ep.start(pd, this, EVENTIO_READ) < 0) - Debug("iocore_net", "error starting EventIO"); - a->mutex = get_NetHandler(t)->mutex; - t->schedule_every(a, period, etype); - } -} - -SSLNetProcessor::~SSLNetProcessor() -{ - cleanup(); -}
