From 73d752fb1cd01a9092191c9ac52ddb8fa378065f Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> Date: Sat, 6 Dec 2014 00:26:43 +0900 Subject: [PATCH] Add --min-tls-version option The --min-tls-version option specifies minimum SSL/TLS version to enable. Possible Values: SSLv3, TLSv1, TLSv1.1, TLSv1.2 Default: TLSv1 --- doc/manual-src/en/aria2c.rst | 6 ++++++ src/AppleTLSContext.cc | 4 ++-- src/AppleTLSContext.h | 10 +++++++-- src/AppleTLSSession.cc | 32 ++++++++++++++++++++++++----- src/LibgnutlsTLSContext.cc | 7 ++++--- src/LibgnutlsTLSContext.h | 8 +++++++- src/LibgnutlsTLSSession.cc | 15 +++++++++++++- src/LibsslTLSContext.cc | 25 +++++++++++++++++----- src/LibsslTLSContext.h | 2 +- src/MultiUrlRequestInfo.cc | 8 ++++++-- src/OptionHandlerFactory.cc | 11 ++++++++++ src/TLSContext.h | 9 +++++++- src/WinTLSContext.cc | 40 ++++++++++++++++++++++++++++-------- src/WinTLSContext.h | 2 +- src/prefs.cc | 6 ++++++ src/prefs.h | 6 ++++++ src/usage_text.h | 2 ++ src/util.cc | 17 +++++++++++++++ src/util.h | 6 ++++++ 19 files changed, 183 insertions(+), 33 deletions(-) diff --git a/doc/manual-src/en/aria2c.rst b/doc/manual-src/en/aria2c.rst index 0d9c2a81..003f3cef 100644 --- a/doc/manual-src/en/aria2c.rst +++ b/doc/manual-src/en/aria2c.rst @@ -1321,6 +1321,12 @@ Advanced Options given URIs do not support resume. See :option:`--always-resume` option. Default: ``0`` +.. option:: --min-tls-version=<VERSION> + + Specify minimum SSL/TLS version to enable. + Possible Values: ``SSLv3``, ``TLSv1``, ``TLSv1.1``, ``TLSv1.2`` + Default: ``TLSv1`` + .. option:: --log-level=<LEVEL> Set log level to output. diff --git a/src/AppleTLSContext.cc b/src/AppleTLSContext.cc index 10e034fd..ae72b5b7 100644 --- a/src/AppleTLSContext.cc +++ b/src/AppleTLSContext.cc @@ -195,9 +195,9 @@ bool checkIdentity(const SecIdentityRef id, namespace aria2 { -TLSContext* TLSContext::make(TLSSessionSide side) +TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion ver) { - return new AppleTLSContext(side); + return new AppleTLSContext(side, ver); } AppleTLSContext::~AppleTLSContext() diff --git a/src/AppleTLSContext.h b/src/AppleTLSContext.h index b1d24154..6c72e5e9 100644 --- a/src/AppleTLSContext.h +++ b/src/AppleTLSContext.h @@ -49,8 +49,8 @@ namespace aria2 { class AppleTLSContext : public TLSContext { public: - AppleTLSContext(TLSSessionSide side) - : side_(side), verifyPeer_(true), credentials_(nullptr) + AppleTLSContext(TLSSessionSide side, TLSVersion ver) + : side_(side), minTLSVer_(ver), verifyPeer_(true), credentials_(nullptr) {} virtual ~AppleTLSContext(); @@ -89,8 +89,14 @@ public: SecIdentityRef getCredentials(); + TLSVersion getMinTLSVersion() const + { + return minTLSVer_; + } + private: TLSSessionSide side_; + TLSVersion minTLSVer_; bool verifyPeer_; SecIdentityRef credentials_; diff --git a/src/AppleTLSSession.cc b/src/AppleTLSSession.cc index 00c662c5..0633583f 100644 --- a/src/AppleTLSSession.cc +++ b/src/AppleTLSSession.cc @@ -362,14 +362,36 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx) return; } #if defined(__MAC_10_8) - (void)SSLSetProtocolVersionMin(sslCtx_, kSSLProtocol3); + switch (ctx->getMinTLSVersion()) { + case TLS_PROTO_SSL3: + (void)SSLSetProtocolVersionMin(sslCtx_, kSSLProtocol3); + break; + case TLS_PROTO_TLS10: + (void)SSLSetProtocolVersionMin(sslCtx_, kSSLProtocol1); + break; + case TLS_PROTO_TLS11: + (void)SSLSetProtocolVersionMin(sslCtx_, kSSLProtocol11); + break; + case TLS_PROTO_TLS12: + (void)SSLSetProtocolVersionMin(sslCtx_, kSSLProtocol12); + break; + } (void)SSLSetProtocolVersionMax(sslCtx_, kTLSProtocol12); #else (void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocolAll, false); - (void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocol3, true); - (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol1, true); - (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol11, true); - (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol12, true); + switch (ctx->getMinTLSVersion()) { + case TLS_PROTO_SSL3: + (void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocol3, true); + // fall through + case TLS_PROTO_TLS10: + (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol1, true); + // fall through + case TLS_PROTO_TLS11: + (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol11, true); + // fall through + case TLS_PROTO_TLS12: + (void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol12, true); + } #endif // BEAST diff --git a/src/LibgnutlsTLSContext.cc b/src/LibgnutlsTLSContext.cc index e62f0691..df9ef6fb 100644 --- a/src/LibgnutlsTLSContext.cc +++ b/src/LibgnutlsTLSContext.cc @@ -49,14 +49,15 @@ namespace aria2 { -TLSContext* TLSContext::make(TLSSessionSide side) +TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion ver) { - return new GnuTLSContext(side); + return new GnuTLSContext(side, ver); } -GnuTLSContext::GnuTLSContext(TLSSessionSide side) +GnuTLSContext::GnuTLSContext(TLSSessionSide side, TLSVersion ver) : certCred_(0), side_(side), + minTLSVer_(ver), verifyPeer_(true) { int r = gnutls_certificate_allocate_credentials(&certCred_); diff --git a/src/LibgnutlsTLSContext.h b/src/LibgnutlsTLSContext.h index ef2b005c..d31aa6d5 100644 --- a/src/LibgnutlsTLSContext.h +++ b/src/LibgnutlsTLSContext.h @@ -46,7 +46,7 @@ namespace aria2 { class GnuTLSContext : public TLSContext { public: - GnuTLSContext(TLSSessionSide side); + GnuTLSContext(TLSSessionSide side, TLSVersion ver); virtual ~GnuTLSContext(); @@ -79,9 +79,15 @@ public: gnutls_certificate_credentials_t getCertCred() const; + TLSVersion getMinTLSVersion() const + { + return minTLSVer_; + } + private: gnutls_certificate_credentials_t certCred_; TLSSessionSide side_; + TLSVersion minTLSVer_; bool good_; bool verifyPeer_; }; diff --git a/src/LibgnutlsTLSSession.cc b/src/LibgnutlsTLSSession.cc index bbb8d100..deda15a3 100644 --- a/src/LibgnutlsTLSSession.cc +++ b/src/LibgnutlsTLSSession.cc @@ -107,7 +107,20 @@ int GnuTLSSession::init(sock_t sockfd) // It seems err is not error message, but the argument string // which causes syntax error. const char* err; - rv_ = gnutls_priority_set_direct(sslSession_, "SECURE128:-VERS-SSL3.0", &err); + std::string pri = "SECURE128"; + switch(tlsContext_->getMinTLSVersion()) { + case TLS_PROTO_TLS12: + pri += ":-VERS-TLS1.1"; + // fall through + case TLS_PROTO_TLS11: + pri += ":-VERS-TLS1.0"; + // fall through + case TLS_PROTO_TLS10: + pri += ":-VERS-SSL3.0"; + default: + break; + }; + rv_ = gnutls_priority_set_direct(sslSession_, pri.c_str(), &err); if(rv_ != GNUTLS_E_SUCCESS) { return TLS_ERR_ERROR; } diff --git a/src/LibsslTLSContext.cc b/src/LibsslTLSContext.cc index 3ecac860..70c13a60 100644 --- a/src/LibsslTLSContext.cc +++ b/src/LibsslTLSContext.cc @@ -81,12 +81,12 @@ namespace { namespace aria2 { -TLSContext* TLSContext::make(TLSSessionSide side) +TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion minVer) { - return new OpenSSLTLSContext(side); + return new OpenSSLTLSContext(side, minVer); } -OpenSSLTLSContext::OpenSSLTLSContext(TLSSessionSide side) +OpenSSLTLSContext::OpenSSLTLSContext(TLSSessionSide side, TLSVersion minVer) : sslCtx_(nullptr), side_(side), verifyPeer_(true) @@ -100,8 +100,23 @@ OpenSSLTLSContext::OpenSSLTLSContext(TLSSessionSide side) ERR_error_string(ERR_get_error(), nullptr))); return; } - // Disable SSLv2/3 and enable all workarounds for buggy servers - SSL_CTX_set_options(sslCtx_, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 + + long ver_opts = 0; + switch(minVer) { + case TLS_PROTO_TLS12: + ver_opts |= SSL_OP_NO_TLSv1_1; + // fall through + case TLS_PROTO_TLS11: + ver_opts |= SSL_OP_NO_TLSv1; + // fall through + case TLS_PROTO_TLS10: + ver_opts |= SSL_OP_NO_SSLv3; + default: + break; + }; + + // Disable SSLv2 and enable all workarounds for buggy servers + SSL_CTX_set_options(sslCtx_, SSL_OP_ALL | SSL_OP_NO_SSLv2 | ver_opts #ifdef SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_ECDH_USE #endif // SSL_OP_SINGLE_ECDH_USE diff --git a/src/LibsslTLSContext.h b/src/LibsslTLSContext.h index 65582366..ea37e6b2 100644 --- a/src/LibsslTLSContext.h +++ b/src/LibsslTLSContext.h @@ -48,7 +48,7 @@ namespace aria2 { class OpenSSLTLSContext : public TLSContext { public: - OpenSSLTLSContext(TLSSessionSide side); + OpenSSLTLSContext(TLSSessionSide side, TLSVersion minVer); ~OpenSSLTLSContext(); diff --git a/src/MultiUrlRequestInfo.cc b/src/MultiUrlRequestInfo.cc index 7ce24b63..e8239309 100644 --- a/src/MultiUrlRequestInfo.cc +++ b/src/MultiUrlRequestInfo.cc @@ -188,7 +188,9 @@ int MultiUrlRequestInfo::prepare() } // We set server TLS context to the SocketCore before creating // DownloadEngine instance. - std::shared_ptr<TLSContext> svTlsContext(TLSContext::make(TLS_SERVER)); + auto minTLSVer = util::toTLSVersion(option_->get(PREF_MIN_TLS_VERSION)); + std::shared_ptr<TLSContext> svTlsContext + (TLSContext::make(TLS_SERVER, minTLSVer)); if(!svTlsContext->addCredentialFile (option_->get(PREF_RPC_CERTIFICATE), option_->get(PREF_RPC_PRIVATE_KEY))) { @@ -245,7 +247,9 @@ int MultiUrlRequestInfo::prepare() e_->setAuthConfigFactory(std::move(authConfigFactory)); #ifdef ENABLE_SSL - std::shared_ptr<TLSContext> clTlsContext(TLSContext::make(TLS_CLIENT)); + auto minTLSVer = util::toTLSVersion(option_->get(PREF_MIN_TLS_VERSION)); + std::shared_ptr<TLSContext> clTlsContext + (TLSContext::make(TLS_CLIENT, minTLSVer)); if(!option_->blank(PREF_CERTIFICATE)) { clTlsContext->addCredentialFile(option_->get(PREF_CERTIFICATE), option_->get(PREF_PRIVATE_KEY)); diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index dec0624a..f2f4eec9 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -584,6 +584,17 @@ std::vector<OptionHandler*> OptionHandlerFactory::createOptionHandlers() op->setChangeOptionForReserved(true); handlers.push_back(op); } +#ifdef ENABLE_SSL + { + OptionHandler* op(new ParameterOptionHandler + (PREF_MIN_TLS_VERSION, + TEXT_MIN_TLS_VERSION, + A2_V_TLS10, + { A2_V_SSL3, A2_V_TLS10, A2_V_TLS11, A2_V_TLS12 })); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } +#endif // ENABLE_SSL { OptionHandler* op(new BooleanOptionHandler (PREF_NO_CONF, diff --git a/src/TLSContext.h b/src/TLSContext.h index 64223ada..91b20965 100644 --- a/src/TLSContext.h +++ b/src/TLSContext.h @@ -46,9 +46,16 @@ enum TLSSessionSide { TLS_SERVER }; +enum TLSVersion { + TLS_PROTO_SSL3, + TLS_PROTO_TLS10, + TLS_PROTO_TLS11, + TLS_PROTO_TLS12, +}; + class TLSContext { public: - static TLSContext* make(TLSSessionSide side); + static TLSContext* make(TLSSessionSide side, TLSVersion minVer); virtual ~TLSContext() {} // private key `keyfile' must be decrypted. diff --git a/src/WinTLSContext.cc b/src/WinTLSContext.cc index ee67cfd0..d30aea52 100644 --- a/src/WinTLSContext.cc +++ b/src/WinTLSContext.cc @@ -63,28 +63,50 @@ namespace aria2 { -WinTLSContext::WinTLSContext(TLSSessionSide side) : side_(side), store_(0) +WinTLSContext::WinTLSContext(TLSSessionSide side, TLSVersion ver) + : side_(side), store_(0) { memset(&credentials_, 0, sizeof(credentials_)); credentials_.dwVersion = SCHANNEL_CRED_VERSION; + credentials_.grbitEnabledProtocols = 0; if (side_ == TLS_CLIENT) { - credentials_.grbitEnabledProtocols = - SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | - SP_PROT_TLS1_2_CLIENT; + switch (ver) { + case TLS_PROTO_SSL3: + credentials_.grbitEnabledProtocols |= SP_PROT_SSL3_CLIENT; + // fall through + case TLS_PROTO_TLS10: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_CLIENT; + // fall through + case TLS_PROTO_TLS11: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; + // fall through + case TLS_PROTO_TLS12: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; + } } else { - credentials_.grbitEnabledProtocols = - SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER | - SP_PROT_TLS1_2_SERVER; + switch (ver) { + case TLS_PROTO_SSL3: + credentials_.grbitEnabledProtocols |= SP_PROT_SSL3_SERVER; + // fall through + case TLS_PROTO_TLS10: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_SERVER; + // fall through + case TLS_PROTO_TLS11: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_1_SERVER; + // fall through + case TLS_PROTO_TLS12: + credentials_.grbitEnabledProtocols |= SP_PROT_TLS1_2_SERVER; + } } credentials_.dwMinimumCipherStrength = 128; // bit setVerifyPeer(side_ == TLS_CLIENT); } -TLSContext* TLSContext::make(TLSSessionSide side) +TLSContext* TLSContext::make(TLSSessionSide side, TLSVersion ver) { - return new WinTLSContext(side); + return new WinTLSContext(side, ver); } WinTLSContext::~WinTLSContext() diff --git a/src/WinTLSContext.h b/src/WinTLSContext.h index 7b182c6f..8ca9e3a8 100644 --- a/src/WinTLSContext.h +++ b/src/WinTLSContext.h @@ -67,7 +67,7 @@ typedef std::unique_ptr<CredHandle, cred_deleter> CredPtr; class WinTLSContext : public TLSContext { public: - WinTLSContext(TLSSessionSide side); + WinTLSContext(TLSSessionSide side, TLSVersion ver); virtual ~WinTLSContext(); diff --git a/src/prefs.cc b/src/prefs.cc index a6de66ef..2332bd57 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -169,6 +169,10 @@ const std::string V_ARC4("arc4"); const std::string V_HTTP("http"); const std::string V_HTTPS("https"); const std::string V_FTP("ftp"); +const std::string A2_V_SSL3("SSLv3"); +const std::string A2_V_TLS10("TLSv1"); +const std::string A2_V_TLS11("TLSv1.1"); +const std::string A2_V_TLS12("TLSv1.2"); PrefPtr PREF_VERSION = makePref("version"); PrefPtr PREF_HELP = makePref("help"); @@ -367,6 +371,8 @@ PrefPtr PREF_DSCP = makePref("dscp"); PrefPtr PREF_PAUSE_METADATA = makePref("pause-metadata"); // values: 1*digit PrefPtr PREF_RLIMIT_NOFILE = makePref("rlimit-nofile"); +// vlaues: SSLv3 | TLSv1 | TLSv1.1 | TLSv1.2 +PrefPtr PREF_MIN_TLS_VERSION = makePref("min-tls-version"); /** * FTP related preferences diff --git a/src/prefs.h b/src/prefs.h index 33ab67ac..a4b5bfdf 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -105,6 +105,10 @@ extern const std::string V_ARC4; extern const std::string V_HTTP; extern const std::string V_HTTPS; extern const std::string V_FTP; +extern const std::string A2_V_SSL3; +extern const std::string A2_V_TLS10; +extern const std::string A2_V_TLS11; +extern const std::string A2_V_TLS12; extern PrefPtr PREF_VERSION; extern PrefPtr PREF_HELP; @@ -304,6 +308,8 @@ extern PrefPtr PREF_DSCP; extern PrefPtr PREF_PAUSE_METADATA; // values: 1*digit extern PrefPtr PREF_RLIMIT_NOFILE; +// vlaues: SSLv3 | TLSv1 | TLSv1.1 | TLSv1.2 +extern PrefPtr PREF_MIN_TLS_VERSION; /** * FTP related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 2831b48e..1b139b13 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -1011,3 +1011,5 @@ " and the next download waiting in queue gets\n" \ " started. But be aware that seeding item is still\n" \ " recognized as active download in RPC method.") +#define TEXT_MIN_TLS_VERSION \ + _(" --min-tls-version=VERSION Specify minimum SSL/TLS version to enable.") diff --git a/src/util.cc b/src/util.cc index 0a0f3901..9332ae47 100644 --- a/src/util.cc +++ b/src/util.cc @@ -2009,6 +2009,23 @@ bool strless(const char* a, const char* b) return strcmp(a, b) < 0; } +TLSVersion toTLSVersion(const std::string& ver) +{ + if(ver == A2_V_SSL3) { + return TLS_PROTO_SSL3; + } + if(ver == A2_V_TLS10) { + return TLS_PROTO_TLS10; + } + if(ver == A2_V_TLS11) { + return TLS_PROTO_TLS11; + } + if(ver == A2_V_TLS12) { + return TLS_PROTO_TLS12; + } + return TLS_PROTO_TLS10; +} + } // namespace util } // namespace aria2 diff --git a/src/util.h b/src/util.h index d14d8fc9..ff24c279 100644 --- a/src/util.h +++ b/src/util.h @@ -64,6 +64,10 @@ #include "fmt.h" #include "prefs.h" +#ifdef ENABLE_SSL +#include "TLSContext.h" +#endif // ENABLE_SSL + #ifndef HAVE_SIGACTION # define sigset_t int #endif // HAVE_SIGACTION @@ -880,6 +884,8 @@ bool noProxyDomainMatch(const std::string& hostname, const std::string& domain); // Checks hostname matches pattern as described in RFC 6125. bool tlsHostnameMatch(const std::string& pattern, const std::string& hostname); +TLSVersion toTLSVersion(const std::string& ver); + } // namespace util } // namespace aria2