diff --git a/3rdParty/CMakeLists.txt b/3rdParty/CMakeLists.txt index e12949f34d..d64114e3a1 100644 --- a/3rdParty/CMakeLists.txt +++ b/3rdParty/CMakeLists.txt @@ -89,22 +89,17 @@ endif () set(SKIP_INSTALL_ALL ON) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/zlib/zlib-1.2.8) -################################################################################ -## SNAPPY -################################################################################ - - ################################################################################ ## ROCKSDB ################################################################################ if (USE_ROCKSDB) if (${JEMALLOC_FOUND}) -# set(USE_JEMALLOC_DEFAULT 1) +# set(USE_JEMALLOC_DEFAULT 1) endif() set(SKIP_INSTALL_ALL ON) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/snappy/google-snappy-d53de18) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/rocksdb/rocksdb/) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/rocksdb/rocksdb) endif () ################################################################################ diff --git a/3rdParty/V8/CMakeLists.txt b/3rdParty/V8/CMakeLists.txt index 4c2e6fc068..8a0d8aef61 100644 --- a/3rdParty/V8/CMakeLists.txt +++ b/3rdParty/V8/CMakeLists.txt @@ -364,6 +364,11 @@ else () set(V8_CXXFLAGS "$ENV{V8_CXXFLAGS}") set(V8_LDFLAGS "$ENV{V8_LDFLAGS}") + if (CMAKE_COMPILER_IS_GNUCC) + set(V8_CFLAGS "${V8_CFLAGS} -fno-delete-null-pointer-checks") + set(V8_CXXFLAGS "${V8_CXXFLAGS} -fno-delete-null-pointer-checks") + endif () + if (USE_DEBUG_V8) set(V8_TARGET_ARCH "${V8_PROC_ARCH}.debug") else () diff --git a/CMakeLists.txt b/CMakeLists.txt index 89fe3223eb..452c0e4772 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,11 +155,12 @@ option(USE_ROCKSDB if (USE_ROCKSDB) add_definitions("-DARANGODB_ENABLE_ROCKSDB=1") + add_definitions(-DARANGODB_ROCKSDB_VERSION=\"${ROCKSDB_VERSION}\") + include_directories(${ROCKSDB_INCLUDE_DIR}) else () set(ROCKSDB_LIBS "") endif () - ################################################################################ ## EXTERNAL PROGRAMS ################################################################################ @@ -670,15 +671,6 @@ endif () include_directories(${Boost_INCLUDE_DIR}) add_definitions(-DARANGODB_BOOST_VERSION=\"${Boost_VERSION}\") -################################################################################ -## ROCKSDB -################################################################################ - -if (USE_ROCKSDB) -include_directories(${ROCKSDB_INCLUDE_DIR}) -add_definitions(-DARANGODB_ROCKSDB_VERSION=\"${ROCKSDB_VERSION}\") -endif () - ################################################################################ ## ICU ################################################################################ @@ -799,7 +791,6 @@ endif () list(INSERT SYSTEM_LIBRARIES 0 ${BT_LIBS} ${ZLIB_LIBS} - ${SNAPPY_LIBS} ${ICU_LIBS} ${OPENSSL_LIBRARIES} ${BASE_LIBS} diff --git a/arangod/Agency/Supervision.cpp b/arangod/Agency/Supervision.cpp index f393e3a0ee..88400b2806 100644 --- a/arangod/Agency/Supervision.cpp +++ b/arangod/Agency/Supervision.cpp @@ -43,7 +43,7 @@ inline void makeReport (query_t& envelope, Builder const& report) { envelope->close(); } -template<> struct Job { +template<> struct Job { Job (Node const& snapshot, Agent* agent, uint64_t jobId, std::string const& failed) { diff --git a/arangod/Agency/Supervision.h b/arangod/Agency/Supervision.h index 50fc0a91b6..fd25ad6ed8 100644 --- a/arangod/Agency/Supervision.h +++ b/arangod/Agency/Supervision.h @@ -47,7 +47,7 @@ struct JobCallback { }; enum JOB_CASE {FAILED_LEADER}; -template struct Job {}; +template struct Job {}; struct check_t { bool good; diff --git a/arangod/RestServer/RestServerFeature.cpp b/arangod/RestServer/RestServerFeature.cpp index 17948be91c..a3c6c37ecc 100644 --- a/arangod/RestServer/RestServerFeature.cpp +++ b/arangod/RestServer/RestServerFeature.cpp @@ -65,7 +65,7 @@ #include "RestServer/QueryRegistryFeature.h" #include "RestServer/ServerFeature.h" #include "Scheduler/SchedulerFeature.h" -#include "Ssl/SslFeature.h" +#include "Ssl/SslServerFeature.h" #include "V8Server/V8DealerFeature.h" #include "VocBase/server.h" @@ -250,8 +250,8 @@ void RestServerFeature::buildServers() { // ssl endpoints if (endpointList.hasSsl()) { - SslFeature* ssl = - application_features::ApplicationServer::getFeature("Ssl"); + SslServerFeature* ssl = + application_features::ApplicationServer::getFeature("SslServer"); // check the ssl context if (ssl->sslContext() == nullptr) { diff --git a/arangod/RestServer/arangod.cpp b/arangod/RestServer/arangod.cpp index 9450c67679..12ff30e237 100644 --- a/arangod/RestServer/arangod.cpp +++ b/arangod/RestServer/arangod.cpp @@ -60,6 +60,7 @@ #include "RestServer/UpgradeFeature.h" #include "Scheduler/SchedulerFeature.h" #include "Ssl/SslFeature.h" +#include "Ssl/SslServerFeature.h" #include "Statistics/StatisticsFeature.h" #include "V8Server/FoxxQueuesFeature.h" #include "V8Server/V8DealerFeature.h" @@ -136,7 +137,7 @@ static int runServer(int argc, char** argv) { "Cluster", "Daemon", "Dispatcher", "Endpoint", "FoxxQueues", "LoggerBufferFeature", "RestServer", "Server", "Scheduler", - "Ssl", "Statistics", "Supervisor"}; + "SslServer", "Statistics", "Supervisor"}; int ret = EXIT_FAILURE; @@ -173,6 +174,7 @@ static int runServer(int argc, char** argv) { server.addFeature(new ServerFeature(&server, &ret)); server.addFeature(new ShutdownFeature(&server, {"UnitTests", "Script"})); server.addFeature(new SslFeature(&server)); + server.addFeature(new SslServerFeature(&server)); server.addFeature(new StatisticsFeature(&server)); server.addFeature(new TempFeature(&server, name)); server.addFeature(new UnitTestsFeature(&server, &ret)); diff --git a/arangosh/Benchmark/arangobench.cpp b/arangosh/Benchmark/arangobench.cpp index a516332abf..b8d11881f3 100644 --- a/arangosh/Benchmark/arangobench.cpp +++ b/arangosh/Benchmark/arangobench.cpp @@ -33,6 +33,7 @@ #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "Random/RandomFeature.h" +#include "Ssl/SslFeature.h" using namespace arangodb; using namespace arangodb::application_features; @@ -56,6 +57,7 @@ int main(int argc, char* argv[]) { server.addFeature(new LoggerFeature(&server, false)); server.addFeature(new RandomFeature(&server)); server.addFeature(new ShutdownFeature(&server, {"Bench"})); + server.addFeature(new SslFeature(&server)); server.addFeature(new TempFeature(&server, "arangobench")); server.addFeature(new VersionFeature(&server)); diff --git a/arangosh/Dump/arangodump.cpp b/arangosh/Dump/arangodump.cpp index 62ce894119..ebc19ac778 100644 --- a/arangosh/Dump/arangodump.cpp +++ b/arangosh/Dump/arangodump.cpp @@ -32,6 +32,7 @@ #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "Random/RandomFeature.h" +#include "Ssl/SslFeature.h" using namespace arangodb; using namespace arangodb::application_features; @@ -53,6 +54,7 @@ int main(int argc, char* argv[]) { server.addFeature(new LoggerFeature(&server, false)); server.addFeature(new RandomFeature(&server)); server.addFeature(new ShutdownFeature(&server, {"Dump"})); + server.addFeature(new SslFeature(&server)); server.addFeature(new VersionFeature(&server)); try { diff --git a/arangosh/Import/arangoimp.cpp b/arangosh/Import/arangoimp.cpp index 425bcfc33e..fa8ce71140 100644 --- a/arangosh/Import/arangoimp.cpp +++ b/arangosh/Import/arangoimp.cpp @@ -33,6 +33,7 @@ #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "Random/RandomFeature.h" +#include "Ssl/SslFeature.h" using namespace arangodb; using namespace arangodb::application_features; @@ -54,6 +55,7 @@ int main(int argc, char* argv[]) { server.addFeature(new LoggerFeature(&server, false)); server.addFeature(new RandomFeature(&server)); server.addFeature(new ShutdownFeature(&server, {"Import"})); + server.addFeature(new SslFeature(&server)); server.addFeature(new TempFeature(&server, "arangoimp")); server.addFeature(new VersionFeature(&server)); diff --git a/arangosh/Shell/arangosh.cpp b/arangosh/Shell/arangosh.cpp index 9240dbd472..127d415aa8 100644 --- a/arangosh/Shell/arangosh.cpp +++ b/arangosh/Shell/arangosh.cpp @@ -37,6 +37,7 @@ #include "Random/RandomFeature.h" #include "Shell/ShellFeature.h" #include "Shell/V8ShellFeature.h" +#include "Ssl/SslFeature.h" using namespace arangodb; using namespace arangodb::application_features; @@ -62,6 +63,7 @@ int main(int argc, char* argv[]) { server.addFeature(new RandomFeature(&server)); server.addFeature(new ShellFeature(&server, &ret)); server.addFeature(new ShutdownFeature(&server, {"Shell"})); + server.addFeature(new SslFeature(&server)); server.addFeature(new TempFeature(&server, name)); server.addFeature(new V8PlatformFeature(&server)); server.addFeature(new V8ShellFeature(&server, name)); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 13f621575a..5529b91edd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -215,6 +215,7 @@ add_library(${LIB_ARANGO} STATIC SimpleHttpClient/SslClientConnection.cpp Ssl/SslFeature.cpp Ssl/SslInterface.cpp + Ssl/SslServerFeature.cpp Ssl/ssl-helper.cpp Utilities/LineEditor.cpp Utilities/ScriptLoader.cpp diff --git a/lib/Ssl/SslFeature.cpp b/lib/Ssl/SslFeature.cpp index b0ef234398..a9821594c3 100644 --- a/lib/Ssl/SslFeature.cpp +++ b/lib/Ssl/SslFeature.cpp @@ -22,9 +22,6 @@ #include "SslFeature.h" -#include -#include - #define OPENSSL_THREAD_DEFINES #include @@ -133,55 +130,12 @@ void opensslCleanup() { } SslFeature::SslFeature(application_features::ApplicationServer* server) - : ApplicationFeature(server, "Ssl"), - _cafile(), - _keyfile(), - _sessionCache(false), - _cipherList(), - _protocol(TLS_V1), - _options( - (long)(SSL_OP_TLS_ROLLBACK_BUG | SSL_OP_CIPHER_SERVER_PREFERENCE)), - _ecdhCurve("prime256v1"), - _sslContext(nullptr) { + : ApplicationFeature(server, "Ssl") { setOptional(true); requiresElevatedPrivileges(false); startsAfter("Logger"); } -void SslFeature::collectOptions(std::shared_ptr options) { - options->addSection("ssl", "Configure SSL communication"); - - options->addOption("--ssl.cafile", "ca file used for secure connections", - new StringParameter(&_cafile)); - - options->addOption("--ssl.keyfile", "key-file used for secure connections", - new StringParameter(&_keyfile)); - - options->addOption("--ssl.session-cache", - "enable the session cache for connections", - new BooleanParameter(&_sessionCache)); - - options->addOption("--ssl.chipher-list", - "ssl chipers to use, see OpenSSL documentation", - new StringParameter(&_cipherList)); - - std::unordered_set sslProtocols = {1, 2, 3, 4}; - - options->addOption("--ssl.protocol", - "ssl protocol (1 = SSLv2, 2 = SSLv23, 3 = SSLv3, 4 = " - "TLSv1, 5 = TLSV1.2 (recommended)", - new UInt64Parameter(&_protocol)); - - options->addHiddenOption( - "--ssl.options", "ssl connection options, see OpenSSL documentation", - new DiscreteValuesParameter(&_options, sslProtocols)); - - options->addOption( - "--ssl.ecdh-curve", - "SSL ECDH Curve, see the output of \"openssl ecparam -list_curves\"", - new StringParameter(&_ecdhCurve)); -} - void SslFeature::prepare() { SSL_library_init(); SSL_load_error_strings(); @@ -189,411 +143,12 @@ void SslFeature::prepare() { ERR_load_crypto_strings(); opensslSetup(); - - createSslContext(); -} - -void SslFeature::start() { - LOG(INFO) << "using SSL options: " << stringifySslOptions(_options); - - if (!_cipherList.empty()) { - LOG(INFO) << "using SSL cipher-list '" << _cipherList << "'"; - } } void SslFeature::stop() { - if (_sslContext != nullptr) { - SSL_CTX_free(_sslContext); - _sslContext = nullptr; - } - opensslCleanup(); ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); } - -namespace { -class BIOGuard { - public: - explicit BIOGuard(BIO* bio) : _bio(bio) {} - - ~BIOGuard() { BIO_free(_bio); } - - public: - BIO* _bio; -}; -} - -void SslFeature::createSslContext() { - // check keyfile - if (_keyfile.empty()) { - return; - } - - // validate protocol - if (_protocol <= SSL_UNKNOWN || _protocol >= SSL_LAST) { - LOG(FATAL) << "invalid SSL protocol version specified. Please use a valid " - "value for '--ssl.protocol.'"; - FATAL_ERROR_EXIT(); - } - - LOG(DEBUG) << "using SSL protocol version '" - << protocolName((protocol_e)_protocol) << "'"; - - if (!FileUtils::exists(_keyfile)) { - LOG(FATAL) << "unable to find SSL keyfile '" << _keyfile << "'"; - FATAL_ERROR_EXIT(); - } - - // create context - _sslContext = ::sslContext(protocol_e(_protocol), _keyfile); - - if (_sslContext == nullptr) { - LOG(FATAL) << "failed to create SSL context, cannot create HTTPS server"; - FATAL_ERROR_EXIT(); - } - - // set cache mode - SSL_CTX_set_session_cache_mode( - _sslContext, _sessionCache ? SSL_SESS_CACHE_SERVER : SSL_SESS_CACHE_OFF); - - if (_sessionCache) { - LOG(TRACE) << "using SSL session caching"; - } - - // set options - SSL_CTX_set_options(_sslContext, (long)_options); - - if (!_cipherList.empty()) { - if (SSL_CTX_set_cipher_list(_sslContext, _cipherList.c_str()) != 1) { - LOG(FATAL) << "cannot set SSL cipher list '" << _cipherList - << "': " << lastSSLError(); - FATAL_ERROR_EXIT(); - } - } - -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL - int sslEcdhNid; - EC_KEY* ecdhKey; - sslEcdhNid = OBJ_sn2nid(_ecdhCurve.c_str()); - - if (sslEcdhNid == 0) { - LOG(FATAL) << "SSL error: " << lastSSLError() - << " Unknown curve name: " << _ecdhCurve; - FATAL_ERROR_EXIT(); - } - - // https://www.openssl.org/docs/manmaster/apps/ecparam.html - ecdhKey = EC_KEY_new_by_curve_name(sslEcdhNid); - if (ecdhKey == nullptr) { - LOG(FATAL) << "SSL error: " << lastSSLError() - << " Unable to create curve by name: " << _ecdhCurve; - FATAL_ERROR_EXIT(); - } - - SSL_CTX_set_tmp_ecdh(_sslContext, ecdhKey); - SSL_CTX_set_options(_sslContext, SSL_OP_SINGLE_ECDH_USE); - EC_KEY_free(ecdhKey); -#endif - - // set ssl context - UniformCharacter r( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); - _rctx = r.random(SSL_MAX_SSL_SESSION_ID_LENGTH); - - int res = SSL_CTX_set_session_id_context( - _sslContext, (unsigned char const*)_rctx.c_str(), (int)_rctx.size()); - - if (res != 1) { - LOG(FATAL) << "cannot set SSL session id context '" << _rctx - << "': " << lastSSLError(); - FATAL_ERROR_EXIT(); - } - - // check CA - if (!_cafile.empty()) { - LOG(TRACE) << "trying to load CA certificates from '" << _cafile << "'"; - - int res = SSL_CTX_load_verify_locations(_sslContext, _cafile.c_str(), 0); - - if (res == 0) { - LOG(FATAL) << "cannot load CA certificates from '" << _cafile - << "': " << lastSSLError(); - FATAL_ERROR_EXIT(); - } - - STACK_OF(X509_NAME) * certNames; - - certNames = SSL_load_client_CA_file(_cafile.c_str()); - - if (certNames == nullptr) { - LOG(FATAL) << "cannot load CA certificates from '" << _cafile - << "': " << lastSSLError(); - FATAL_ERROR_EXIT(); - } - - if (Logger::logLevel() == arangodb::LogLevel::TRACE) { - for (int i = 0; i < sk_X509_NAME_num(certNames); ++i) { - X509_NAME* cert = sk_X509_NAME_value(certNames, i); - - if (cert) { - BIOGuard bout(BIO_new(BIO_s_mem())); - - X509_NAME_print_ex(bout._bio, cert, 0, - (XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | - ASN1_STRFLGS_UTF8_CONVERT) & - ~ASN1_STRFLGS_ESC_MSB); - - char* r; - long len = BIO_get_mem_data(bout._bio, &r); - - LOG(TRACE) << "name: " << std::string(r, len); - } - } - } - - SSL_CTX_set_client_CA_list(_sslContext, certNames); - } -} - -std::string SslFeature::stringifySslOptions( - uint64_t opts) const { - std::string result; - -#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG - if (opts & SSL_OP_MICROSOFT_SESS_ID_BUG) { - result.append(", SSL_OP_MICROSOFT_SESS_ID_BUG"); - } -#endif - -#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG - if (opts & SSL_OP_NETSCAPE_CHALLENGE_BUG) { - result.append(", SSL_OP_NETSCAPE_CHALLENGE_BUG"); - } -#endif - -#ifdef SSL_OP_LEGACY_SERVER_CONNECT - if (opts & SSL_OP_LEGACY_SERVER_CONNECT) { - result.append(", SSL_OP_LEGACY_SERVER_CONNECT"); - } -#endif - -#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG - if (opts & SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG) { - result.append(", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG"); - } -#endif - -#ifdef SSL_OP_TLSEXT_PADDING - if (opts & SSL_OP_TLSEXT_PADDING) { - result.append(", SSL_OP_TLSEXT_PADDING"); - } -#endif - -#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER - if (opts & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { - result.append(", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER"); - } -#endif - -#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG - if (opts & SSL_OP_SAFARI_ECDHE_ECDSA_BUG) { - result.append(", SSL_OP_SAFARI_ECDHE_ECDSA_BUG"); - } -#endif - -#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG - if (opts & SSL_OP_SSLEAY_080_CLIENT_DH_BUG) { - result.append(", SSL_OP_SSLEAY_080_CLIENT_DH_BUG"); - } -#endif - -#ifdef SSL_OP_TLS_D5_BUG - if (opts & SSL_OP_TLS_D5_BUG) { - result.append(", SSL_OP_TLS_D5_BUG"); - } -#endif - -#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG - if (opts & SSL_OP_TLS_BLOCK_PADDING_BUG) { - result.append(", SSL_OP_TLS_BLOCK_PADDING_BUG"); - } -#endif - -#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING - if (opts & SSL_OP_MSIE_SSLV2_RSA_PADDING) { - result.append(", SSL_OP_MSIE_SSLV2_RSA_PADDING"); - } -#endif - -#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG - if (opts & SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG) { - result.append(", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG"); - } -#endif - -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - if (opts & SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) { - result.append(", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); - } -#endif - -#ifdef SSL_OP_NO_QUERY_MTU - if (opts & SSL_OP_NO_QUERY_MTU) { - result.append(", SSL_OP_NO_QUERY_MTU"); - } -#endif - -#ifdef SSL_OP_COOKIE_EXCHANGE - if (opts & SSL_OP_COOKIE_EXCHANGE) { - result.append(", SSL_OP_COOKIE_EXCHANGE"); - } -#endif - -#ifdef SSL_OP_NO_TICKET - if (opts & SSL_OP_NO_TICKET) { - result.append(", SSL_OP_NO_TICKET"); - } -#endif - -#ifdef SSL_OP_CISCO_ANYCONNECT - if (opts & SSL_OP_CISCO_ANYCONNECT) { - result.append(", SSL_OP_CISCO_ANYCONNECT"); - } -#endif - -#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION - if (opts & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) { - result.append(", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); - } -#endif - -#ifdef SSL_OP_NO_COMPRESSION - if (opts & SSL_OP_NO_COMPRESSION) { - result.append(", SSL_OP_NO_COMPRESSION"); - } -#endif - -#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - if (opts & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) { - result.append(", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); - } -#endif - -#ifdef SSL_OP_SINGLE_ECDH_USE - if (opts & SSL_OP_SINGLE_ECDH_USE) { - result.append(", SSL_OP_SINGLE_ECDH_USE"); - } -#endif - -#ifdef SSL_OP_SINGLE_DH_USE - if (opts & SSL_OP_SINGLE_DH_USE) { - result.append(", SSL_OP_SINGLE_DH_USE"); - } -#endif - -#ifdef SSL_OP_EPHEMERAL_RSA - if (opts & SSL_OP_EPHEMERAL_RSA) { - result.append(", SSL_OP_EPHEMERAL_RSA"); - } -#endif - -#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE - if (opts & SSL_OP_CIPHER_SERVER_PREFERENCE) { - result.append(", SSL_OP_CIPHER_SERVER_PREFERENCE"); - } -#endif - -#ifdef SSL_OP_TLS_ROLLBACK_BUG - if (opts & SSL_OP_TLS_ROLLBACK_BUG) { - result.append(", SSL_OP_TLS_ROLLBACK_BUG"); - } -#endif - -#ifdef SSL_OP_NO_SSLv2 - if (opts & SSL_OP_NO_SSLv2) { - result.append(", SSL_OP_NO_SSLv2"); - } -#endif - -#ifdef SSL_OP_NO_SSLv3 - if (opts & SSL_OP_NO_SSLv3) { - result.append(", SSL_OP_NO_SSLv3"); - } -#endif - -#ifdef SSL_OP_NO_TLSv1 - if (opts & SSL_OP_NO_TLSv1) { - result.append(", SSL_OP_NO_TLSv1"); - } -#endif - -#ifdef SSL_OP_NO_TLSv1_2 - if (opts & SSL_OP_NO_TLSv1_2) { - result.append(", SSL_OP_NO_TLSv1_2"); - } -#endif - -#ifdef SSL_OP_NO_TLSv1_1 - if (opts & SSL_OP_NO_TLSv1_1) { - result.append(", SSL_OP_NO_TLSv1_1"); - } -#endif - -#ifdef SSL_OP_NO_DTLSv1 - if (opts & SSL_OP_NO_DTLSv1) { - result.append(", SSL_OP_NO_DTLSv1"); - } -#endif - -#ifdef SSL_OP_NO_DTLSv1_2 - if (opts & SSL_OP_NO_DTLSv1_2) { - result.append(", SSL_OP_NO_DTLSv1_2"); - } -#endif - -#ifdef SSL_OP_NO_SSL_MASK - if (opts & SSL_OP_NO_SSL_MASK) { - result.append(", SSL_OP_NO_SSL_MASK"); - } -#endif - -#ifdef SSL_OP_PKCS1_CHECK_1 - if (opts & SSL_OP_PKCS1_CHECK_1) { - result.append(", SSL_OP_PKCS1_CHECK_1"); - } -#endif - -#ifdef SSL_OP_PKCS1_CHECK_2 - if (opts & SSL_OP_PKCS1_CHECK_2) { - result.append(", SSL_OP_PKCS1_CHECK_2"); - } -#endif - -#ifdef SSL_OP_NETSCAPE_CA_DN_BUG - if (opts & SSL_OP_NETSCAPE_CA_DN_BUG) { - result.append(", SSL_OP_NETSCAPE_CA_DN_BUG"); - } -#endif - -#ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG - if (opts & SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG) { - result.append(", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG"); - } -#endif - -#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG - if (opts & SSL_OP_CRYPTOPRO_TLSEXT_BUG) { - result.append(", SSL_OP_CRYPTOPRO_TLSEXT_BUG"); - } -#endif - - if (result.empty()) { - return result; - } - - // strip initial comma - return result.substr(2); -} diff --git a/lib/Ssl/SslFeature.h b/lib/Ssl/SslFeature.h index df644e77e6..7721ecf60b 100644 --- a/lib/Ssl/SslFeature.h +++ b/lib/Ssl/SslFeature.h @@ -33,30 +33,8 @@ class SslFeature final : public application_features::ApplicationFeature { explicit SslFeature(application_features::ApplicationServer* server); public: - void collectOptions(std::shared_ptr) override final; void prepare() override final; - void start() override final; void stop() override final; - - public: - SSL_CTX* sslContext() const { return _sslContext; } - - public: - std::string _cafile; - std::string _keyfile; - bool _sessionCache; - std::string _cipherList; - uint64_t _protocol; - uint64_t _options; - std::string _ecdhCurve; - - private: - void createSslContext(); - std::string stringifySslOptions(uint64_t opts) const; - - private: - SSL_CTX* _sslContext; - std::string _rctx; }; } diff --git a/lib/Ssl/SslServerFeature.cpp b/lib/Ssl/SslServerFeature.cpp new file mode 100644 index 0000000000..e9b98da4c8 --- /dev/null +++ b/lib/Ssl/SslServerFeature.cpp @@ -0,0 +1,489 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2016 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed 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. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +//////////////////////////////////////////////////////////////////////////////// + +#include "SslServerFeature.h" + +#include "Basics/FileUtils.h" +#include "Basics/locks.h" +#include "Logger/Logger.h" +#include "ProgramOptions/ProgramOptions.h" +#include "ProgramOptions/Section.h" +#include "Random/UniformCharacter.h" +#include "Ssl/ssl-helper.h" + +using namespace arangodb; +using namespace arangodb::basics; +using namespace arangodb::options; + +SslServerFeature::SslServerFeature(application_features::ApplicationServer* server) + : ApplicationFeature(server, "SslServer"), + _cafile(), + _keyfile(), + _sessionCache(false), + _cipherList(), + _protocol(TLS_V1), + _options( + (long)(SSL_OP_TLS_ROLLBACK_BUG | SSL_OP_CIPHER_SERVER_PREFERENCE)), + _ecdhCurve("prime256v1"), + _sslContext(nullptr) { + setOptional(true); + requiresElevatedPrivileges(false); + startsAfter("Ssl"); + startsAfter("Logger"); +} + +void SslServerFeature::collectOptions(std::shared_ptr options) { + options->addSection("ssl", "Configure SSL communication"); + + options->addOption("--ssl.cafile", "ca file used for secure connections", + new StringParameter(&_cafile)); + + options->addOption("--ssl.keyfile", "key-file used for secure connections", + new StringParameter(&_keyfile)); + + options->addOption("--ssl.session-cache", + "enable the session cache for connections", + new BooleanParameter(&_sessionCache)); + + options->addOption("--ssl.chipher-list", + "ssl chipers to use, see OpenSSL documentation", + new StringParameter(&_cipherList)); + + std::unordered_set sslProtocols = {1, 2, 3, 4}; + + options->addOption("--ssl.protocol", + "ssl protocol (1 = SSLv2, 2 = SSLv23, 3 = SSLv3, 4 = " + "TLSv1, 5 = TLSV1.2 (recommended)", + new UInt64Parameter(&_protocol)); + + options->addHiddenOption( + "--ssl.options", "ssl connection options, see OpenSSL documentation", + new DiscreteValuesParameter(&_options, sslProtocols)); + + options->addOption( + "--ssl.ecdh-curve", + "SSL ECDH Curve, see the output of \"openssl ecparam -list_curves\"", + new StringParameter(&_ecdhCurve)); +} + +void SslServerFeature::prepare() { + createSslContext(); +} + +void SslServerFeature::start() { + LOG(INFO) << "using SSL options: " << stringifySslOptions(_options); + + if (!_cipherList.empty()) { + LOG(INFO) << "using SSL cipher-list '" << _cipherList << "'"; + } +} + +void SslServerFeature::stop() { + if (_sslContext != nullptr) { + SSL_CTX_free(_sslContext); + _sslContext = nullptr; + } +} + +namespace { +class BIOGuard { + public: + explicit BIOGuard(BIO* bio) : _bio(bio) {} + + ~BIOGuard() { BIO_free(_bio); } + + public: + BIO* _bio; +}; +} + +void SslServerFeature::createSslContext() { + // check keyfile + if (_keyfile.empty()) { + return; + } + + // validate protocol + if (_protocol <= SSL_UNKNOWN || _protocol >= SSL_LAST) { + LOG(FATAL) << "invalid SSL protocol version specified. Please use a valid " + "value for '--ssl.protocol.'"; + FATAL_ERROR_EXIT(); + } + + LOG(DEBUG) << "using SSL protocol version '" + << protocolName((protocol_e)_protocol) << "'"; + + if (!FileUtils::exists(_keyfile)) { + LOG(FATAL) << "unable to find SSL keyfile '" << _keyfile << "'"; + FATAL_ERROR_EXIT(); + } + + // create context + _sslContext = ::sslContext(protocol_e(_protocol), _keyfile); + + if (_sslContext == nullptr) { + LOG(FATAL) << "failed to create SSL context, cannot create HTTPS server"; + FATAL_ERROR_EXIT(); + } + + // set cache mode + SSL_CTX_set_session_cache_mode( + _sslContext, _sessionCache ? SSL_SESS_CACHE_SERVER : SSL_SESS_CACHE_OFF); + + if (_sessionCache) { + LOG(TRACE) << "using SSL session caching"; + } + + // set options + SSL_CTX_set_options(_sslContext, (long)_options); + + if (!_cipherList.empty()) { + if (SSL_CTX_set_cipher_list(_sslContext, _cipherList.c_str()) != 1) { + LOG(FATAL) << "cannot set SSL cipher list '" << _cipherList + << "': " << lastSSLError(); + FATAL_ERROR_EXIT(); + } + } + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL + int sslEcdhNid; + EC_KEY* ecdhKey; + sslEcdhNid = OBJ_sn2nid(_ecdhCurve.c_str()); + + if (sslEcdhNid == 0) { + LOG(FATAL) << "SSL error: " << lastSSLError() + << " Unknown curve name: " << _ecdhCurve; + FATAL_ERROR_EXIT(); + } + + // https://www.openssl.org/docs/manmaster/apps/ecparam.html + ecdhKey = EC_KEY_new_by_curve_name(sslEcdhNid); + if (ecdhKey == nullptr) { + LOG(FATAL) << "SSL error: " << lastSSLError() + << " Unable to create curve by name: " << _ecdhCurve; + FATAL_ERROR_EXIT(); + } + + SSL_CTX_set_tmp_ecdh(_sslContext, ecdhKey); + SSL_CTX_set_options(_sslContext, SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdhKey); +#endif + + // set ssl context + UniformCharacter r( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + _rctx = r.random(SSL_MAX_SSL_SESSION_ID_LENGTH); + + int res = SSL_CTX_set_session_id_context( + _sslContext, (unsigned char const*)_rctx.c_str(), (int)_rctx.size()); + + if (res != 1) { + LOG(FATAL) << "cannot set SSL session id context '" << _rctx + << "': " << lastSSLError(); + FATAL_ERROR_EXIT(); + } + + // check CA + if (!_cafile.empty()) { + LOG(TRACE) << "trying to load CA certificates from '" << _cafile << "'"; + + int res = SSL_CTX_load_verify_locations(_sslContext, _cafile.c_str(), 0); + + if (res == 0) { + LOG(FATAL) << "cannot load CA certificates from '" << _cafile + << "': " << lastSSLError(); + FATAL_ERROR_EXIT(); + } + + STACK_OF(X509_NAME) * certNames; + + certNames = SSL_load_client_CA_file(_cafile.c_str()); + + if (certNames == nullptr) { + LOG(FATAL) << "cannot load CA certificates from '" << _cafile + << "': " << lastSSLError(); + FATAL_ERROR_EXIT(); + } + + if (Logger::logLevel() == arangodb::LogLevel::TRACE) { + for (int i = 0; i < sk_X509_NAME_num(certNames); ++i) { + X509_NAME* cert = sk_X509_NAME_value(certNames, i); + + if (cert) { + BIOGuard bout(BIO_new(BIO_s_mem())); + + X509_NAME_print_ex(bout._bio, cert, 0, + (XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | + ASN1_STRFLGS_UTF8_CONVERT) & + ~ASN1_STRFLGS_ESC_MSB); + + char* r; + long len = BIO_get_mem_data(bout._bio, &r); + + LOG(TRACE) << "name: " << std::string(r, len); + } + } + } + + SSL_CTX_set_client_CA_list(_sslContext, certNames); + } +} + +std::string SslServerFeature::stringifySslOptions( + uint64_t opts) const { + std::string result; + +#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG + if (opts & SSL_OP_MICROSOFT_SESS_ID_BUG) { + result.append(", SSL_OP_MICROSOFT_SESS_ID_BUG"); + } +#endif + +#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG + if (opts & SSL_OP_NETSCAPE_CHALLENGE_BUG) { + result.append(", SSL_OP_NETSCAPE_CHALLENGE_BUG"); + } +#endif + +#ifdef SSL_OP_LEGACY_SERVER_CONNECT + if (opts & SSL_OP_LEGACY_SERVER_CONNECT) { + result.append(", SSL_OP_LEGACY_SERVER_CONNECT"); + } +#endif + +#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG + if (opts & SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG) { + result.append(", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG"); + } +#endif + +#ifdef SSL_OP_TLSEXT_PADDING + if (opts & SSL_OP_TLSEXT_PADDING) { + result.append(", SSL_OP_TLSEXT_PADDING"); + } +#endif + +#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER + if (opts & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { + result.append(", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER"); + } +#endif + +#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG + if (opts & SSL_OP_SAFARI_ECDHE_ECDSA_BUG) { + result.append(", SSL_OP_SAFARI_ECDHE_ECDSA_BUG"); + } +#endif + +#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG + if (opts & SSL_OP_SSLEAY_080_CLIENT_DH_BUG) { + result.append(", SSL_OP_SSLEAY_080_CLIENT_DH_BUG"); + } +#endif + +#ifdef SSL_OP_TLS_D5_BUG + if (opts & SSL_OP_TLS_D5_BUG) { + result.append(", SSL_OP_TLS_D5_BUG"); + } +#endif + +#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG + if (opts & SSL_OP_TLS_BLOCK_PADDING_BUG) { + result.append(", SSL_OP_TLS_BLOCK_PADDING_BUG"); + } +#endif + +#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING + if (opts & SSL_OP_MSIE_SSLV2_RSA_PADDING) { + result.append(", SSL_OP_MSIE_SSLV2_RSA_PADDING"); + } +#endif + +#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG + if (opts & SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG) { + result.append(", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG"); + } +#endif + +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + if (opts & SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) { + result.append(", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS"); + } +#endif + +#ifdef SSL_OP_NO_QUERY_MTU + if (opts & SSL_OP_NO_QUERY_MTU) { + result.append(", SSL_OP_NO_QUERY_MTU"); + } +#endif + +#ifdef SSL_OP_COOKIE_EXCHANGE + if (opts & SSL_OP_COOKIE_EXCHANGE) { + result.append(", SSL_OP_COOKIE_EXCHANGE"); + } +#endif + +#ifdef SSL_OP_NO_TICKET + if (opts & SSL_OP_NO_TICKET) { + result.append(", SSL_OP_NO_TICKET"); + } +#endif + +#ifdef SSL_OP_CISCO_ANYCONNECT + if (opts & SSL_OP_CISCO_ANYCONNECT) { + result.append(", SSL_OP_CISCO_ANYCONNECT"); + } +#endif + +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + if (opts & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) { + result.append(", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION"); + } +#endif + +#ifdef SSL_OP_NO_COMPRESSION + if (opts & SSL_OP_NO_COMPRESSION) { + result.append(", SSL_OP_NO_COMPRESSION"); + } +#endif + +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + if (opts & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) { + result.append(", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION"); + } +#endif + +#ifdef SSL_OP_SINGLE_ECDH_USE + if (opts & SSL_OP_SINGLE_ECDH_USE) { + result.append(", SSL_OP_SINGLE_ECDH_USE"); + } +#endif + +#ifdef SSL_OP_SINGLE_DH_USE + if (opts & SSL_OP_SINGLE_DH_USE) { + result.append(", SSL_OP_SINGLE_DH_USE"); + } +#endif + +#ifdef SSL_OP_EPHEMERAL_RSA + if (opts & SSL_OP_EPHEMERAL_RSA) { + result.append(", SSL_OP_EPHEMERAL_RSA"); + } +#endif + +#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + if (opts & SSL_OP_CIPHER_SERVER_PREFERENCE) { + result.append(", SSL_OP_CIPHER_SERVER_PREFERENCE"); + } +#endif + +#ifdef SSL_OP_TLS_ROLLBACK_BUG + if (opts & SSL_OP_TLS_ROLLBACK_BUG) { + result.append(", SSL_OP_TLS_ROLLBACK_BUG"); + } +#endif + +#ifdef SSL_OP_NO_SSLv2 + if (opts & SSL_OP_NO_SSLv2) { + result.append(", SSL_OP_NO_SSLv2"); + } +#endif + +#ifdef SSL_OP_NO_SSLv3 + if (opts & SSL_OP_NO_SSLv3) { + result.append(", SSL_OP_NO_SSLv3"); + } +#endif + +#ifdef SSL_OP_NO_TLSv1 + if (opts & SSL_OP_NO_TLSv1) { + result.append(", SSL_OP_NO_TLSv1"); + } +#endif + +#ifdef SSL_OP_NO_TLSv1_2 + if (opts & SSL_OP_NO_TLSv1_2) { + result.append(", SSL_OP_NO_TLSv1_2"); + } +#endif + +#ifdef SSL_OP_NO_TLSv1_1 + if (opts & SSL_OP_NO_TLSv1_1) { + result.append(", SSL_OP_NO_TLSv1_1"); + } +#endif + +#ifdef SSL_OP_NO_DTLSv1 + if (opts & SSL_OP_NO_DTLSv1) { + result.append(", SSL_OP_NO_DTLSv1"); + } +#endif + +#ifdef SSL_OP_NO_DTLSv1_2 + if (opts & SSL_OP_NO_DTLSv1_2) { + result.append(", SSL_OP_NO_DTLSv1_2"); + } +#endif + +#ifdef SSL_OP_NO_SSL_MASK + if (opts & SSL_OP_NO_SSL_MASK) { + result.append(", SSL_OP_NO_SSL_MASK"); + } +#endif + +#ifdef SSL_OP_PKCS1_CHECK_1 + if (opts & SSL_OP_PKCS1_CHECK_1) { + result.append(", SSL_OP_PKCS1_CHECK_1"); + } +#endif + +#ifdef SSL_OP_PKCS1_CHECK_2 + if (opts & SSL_OP_PKCS1_CHECK_2) { + result.append(", SSL_OP_PKCS1_CHECK_2"); + } +#endif + +#ifdef SSL_OP_NETSCAPE_CA_DN_BUG + if (opts & SSL_OP_NETSCAPE_CA_DN_BUG) { + result.append(", SSL_OP_NETSCAPE_CA_DN_BUG"); + } +#endif + +#ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG + if (opts & SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG) { + result.append(", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG"); + } +#endif + +#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG + if (opts & SSL_OP_CRYPTOPRO_TLSEXT_BUG) { + result.append(", SSL_OP_CRYPTOPRO_TLSEXT_BUG"); + } +#endif + + if (result.empty()) { + return result; + } + + // strip initial comma + return result.substr(2); +} diff --git a/lib/Ssl/SslServerFeature.h b/lib/Ssl/SslServerFeature.h new file mode 100644 index 0000000000..03cc5ba577 --- /dev/null +++ b/lib/Ssl/SslServerFeature.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2016 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed 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. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGODB_APPLICATION_FEATURES_SSL_SERVER_FEATURE_H +#define ARANGODB_APPLICATION_FEATURES_SSL_SERVER_FEATURE_H 1 + +#include "ApplicationFeatures/ApplicationFeature.h" + +#include + +namespace arangodb { +class SslServerFeature final : public application_features::ApplicationFeature { + public: + explicit SslServerFeature(application_features::ApplicationServer* server); + + public: + void collectOptions(std::shared_ptr) override final; + void prepare() override final; + void start() override final; + void stop() override final; + + public: + SSL_CTX* sslContext() const { return _sslContext; } + + public: + std::string _cafile; + std::string _keyfile; + bool _sessionCache; + std::string _cipherList; + uint64_t _protocol; + uint64_t _options; + std::string _ecdhCurve; + + private: + void createSslContext(); + std::string stringifySslOptions(uint64_t opts) const; + + private: + SSL_CTX* _sslContext; + std::string _rctx; +}; +} + +#endif diff --git a/lib/Ssl/ssl-helper.h b/lib/Ssl/ssl-helper.h index 12f87acf08..6a861f7cb4 100644 --- a/lib/Ssl/ssl-helper.h +++ b/lib/Ssl/ssl-helper.h @@ -27,6 +27,7 @@ #include "Basics/Common.h" #include +#include namespace arangodb {