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
pull/311/head
Tatsuhiro Tsujikawa 2014-12-06 00:26:43 +09:00
parent 62fba76666
commit 73d752fb1c
19 changed files with 183 additions and 33 deletions

View File

@ -1321,6 +1321,12 @@ Advanced Options
given URIs do not support resume. See :option:`--always-resume` option. given URIs do not support resume. See :option:`--always-resume` option.
Default: ``0`` 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> .. option:: --log-level=<LEVEL>
Set log level to output. Set log level to output.

View File

@ -195,9 +195,9 @@ bool checkIdentity(const SecIdentityRef id,
namespace aria2 { 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() AppleTLSContext::~AppleTLSContext()

View File

@ -49,8 +49,8 @@ namespace aria2 {
class AppleTLSContext : public TLSContext class AppleTLSContext : public TLSContext
{ {
public: public:
AppleTLSContext(TLSSessionSide side) AppleTLSContext(TLSSessionSide side, TLSVersion ver)
: side_(side), verifyPeer_(true), credentials_(nullptr) : side_(side), minTLSVer_(ver), verifyPeer_(true), credentials_(nullptr)
{} {}
virtual ~AppleTLSContext(); virtual ~AppleTLSContext();
@ -89,8 +89,14 @@ public:
SecIdentityRef getCredentials(); SecIdentityRef getCredentials();
TLSVersion getMinTLSVersion() const
{
return minTLSVer_;
}
private: private:
TLSSessionSide side_; TLSSessionSide side_;
TLSVersion minTLSVer_;
bool verifyPeer_; bool verifyPeer_;
SecIdentityRef credentials_; SecIdentityRef credentials_;

View File

@ -362,14 +362,36 @@ AppleTLSSession::AppleTLSSession(AppleTLSContext* ctx)
return; return;
} }
#if defined(__MAC_10_8) #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); (void)SSLSetProtocolVersionMax(sslCtx_, kTLSProtocol12);
#else #else
(void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocolAll, false); (void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocolAll, false);
(void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocol3, true); switch (ctx->getMinTLSVersion()) {
(void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol1, true); case TLS_PROTO_SSL3:
(void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol11, true); (void)SSLSetProtocolVersionEnabled(sslCtx_, kSSLProtocol3, true);
(void)SSLSetProtocolVersionEnabled(sslCtx_, kTLSProtocol12, 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 #endif
// BEAST // BEAST

View File

@ -49,14 +49,15 @@
namespace aria2 { 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), : certCred_(0),
side_(side), side_(side),
minTLSVer_(ver),
verifyPeer_(true) verifyPeer_(true)
{ {
int r = gnutls_certificate_allocate_credentials(&certCred_); int r = gnutls_certificate_allocate_credentials(&certCred_);

View File

@ -46,7 +46,7 @@ namespace aria2 {
class GnuTLSContext : public TLSContext { class GnuTLSContext : public TLSContext {
public: public:
GnuTLSContext(TLSSessionSide side); GnuTLSContext(TLSSessionSide side, TLSVersion ver);
virtual ~GnuTLSContext(); virtual ~GnuTLSContext();
@ -79,9 +79,15 @@ public:
gnutls_certificate_credentials_t getCertCred() const; gnutls_certificate_credentials_t getCertCred() const;
TLSVersion getMinTLSVersion() const
{
return minTLSVer_;
}
private: private:
gnutls_certificate_credentials_t certCred_; gnutls_certificate_credentials_t certCred_;
TLSSessionSide side_; TLSSessionSide side_;
TLSVersion minTLSVer_;
bool good_; bool good_;
bool verifyPeer_; bool verifyPeer_;
}; };

View File

@ -107,7 +107,20 @@ int GnuTLSSession::init(sock_t sockfd)
// It seems err is not error message, but the argument string // It seems err is not error message, but the argument string
// which causes syntax error. // which causes syntax error.
const char* err; 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) { if(rv_ != GNUTLS_E_SUCCESS) {
return TLS_ERR_ERROR; return TLS_ERR_ERROR;
} }

View File

@ -81,12 +81,12 @@ namespace {
namespace aria2 { 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), : sslCtx_(nullptr),
side_(side), side_(side),
verifyPeer_(true) verifyPeer_(true)
@ -100,8 +100,23 @@ OpenSSLTLSContext::OpenSSLTLSContext(TLSSessionSide side)
ERR_error_string(ERR_get_error(), nullptr))); ERR_error_string(ERR_get_error(), nullptr)));
return; 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 #ifdef SSL_OP_SINGLE_ECDH_USE
| SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_ECDH_USE
#endif // SSL_OP_SINGLE_ECDH_USE #endif // SSL_OP_SINGLE_ECDH_USE

View File

@ -48,7 +48,7 @@ namespace aria2 {
class OpenSSLTLSContext : public TLSContext { class OpenSSLTLSContext : public TLSContext {
public: public:
OpenSSLTLSContext(TLSSessionSide side); OpenSSLTLSContext(TLSSessionSide side, TLSVersion minVer);
~OpenSSLTLSContext(); ~OpenSSLTLSContext();

View File

@ -188,7 +188,9 @@ int MultiUrlRequestInfo::prepare()
} }
// We set server TLS context to the SocketCore before creating // We set server TLS context to the SocketCore before creating
// DownloadEngine instance. // 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 if(!svTlsContext->addCredentialFile
(option_->get(PREF_RPC_CERTIFICATE), (option_->get(PREF_RPC_CERTIFICATE),
option_->get(PREF_RPC_PRIVATE_KEY))) { option_->get(PREF_RPC_PRIVATE_KEY))) {
@ -245,7 +247,9 @@ int MultiUrlRequestInfo::prepare()
e_->setAuthConfigFactory(std::move(authConfigFactory)); e_->setAuthConfigFactory(std::move(authConfigFactory));
#ifdef ENABLE_SSL #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)) { if(!option_->blank(PREF_CERTIFICATE)) {
clTlsContext->addCredentialFile(option_->get(PREF_CERTIFICATE), clTlsContext->addCredentialFile(option_->get(PREF_CERTIFICATE),
option_->get(PREF_PRIVATE_KEY)); option_->get(PREF_PRIVATE_KEY));

View File

@ -584,6 +584,17 @@ std::vector<OptionHandler*> OptionHandlerFactory::createOptionHandlers()
op->setChangeOptionForReserved(true); op->setChangeOptionForReserved(true);
handlers.push_back(op); 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 OptionHandler* op(new BooleanOptionHandler
(PREF_NO_CONF, (PREF_NO_CONF,

View File

@ -46,9 +46,16 @@ enum TLSSessionSide {
TLS_SERVER TLS_SERVER
}; };
enum TLSVersion {
TLS_PROTO_SSL3,
TLS_PROTO_TLS10,
TLS_PROTO_TLS11,
TLS_PROTO_TLS12,
};
class TLSContext { class TLSContext {
public: public:
static TLSContext* make(TLSSessionSide side); static TLSContext* make(TLSSessionSide side, TLSVersion minVer);
virtual ~TLSContext() {} virtual ~TLSContext() {}
// private key `keyfile' must be decrypted. // private key `keyfile' must be decrypted.

View File

@ -63,28 +63,50 @@
namespace aria2 { 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_)); memset(&credentials_, 0, sizeof(credentials_));
credentials_.dwVersion = SCHANNEL_CRED_VERSION; credentials_.dwVersion = SCHANNEL_CRED_VERSION;
credentials_.grbitEnabledProtocols = 0;
if (side_ == TLS_CLIENT) { if (side_ == TLS_CLIENT) {
credentials_.grbitEnabledProtocols = switch (ver) {
SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_1_CLIENT | case TLS_PROTO_SSL3:
SP_PROT_TLS1_2_CLIENT; 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 { else {
credentials_.grbitEnabledProtocols = switch (ver) {
SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER | case TLS_PROTO_SSL3:
SP_PROT_TLS1_2_SERVER; 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 credentials_.dwMinimumCipherStrength = 128; // bit
setVerifyPeer(side_ == TLS_CLIENT); 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() WinTLSContext::~WinTLSContext()

View File

@ -67,7 +67,7 @@ typedef std::unique_ptr<CredHandle, cred_deleter> CredPtr;
class WinTLSContext : public TLSContext class WinTLSContext : public TLSContext
{ {
public: public:
WinTLSContext(TLSSessionSide side); WinTLSContext(TLSSessionSide side, TLSVersion ver);
virtual ~WinTLSContext(); virtual ~WinTLSContext();

View File

@ -169,6 +169,10 @@ const std::string V_ARC4("arc4");
const std::string V_HTTP("http"); const std::string V_HTTP("http");
const std::string V_HTTPS("https"); const std::string V_HTTPS("https");
const std::string V_FTP("ftp"); 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_VERSION = makePref("version");
PrefPtr PREF_HELP = makePref("help"); PrefPtr PREF_HELP = makePref("help");
@ -367,6 +371,8 @@ PrefPtr PREF_DSCP = makePref("dscp");
PrefPtr PREF_PAUSE_METADATA = makePref("pause-metadata"); PrefPtr PREF_PAUSE_METADATA = makePref("pause-metadata");
// values: 1*digit // values: 1*digit
PrefPtr PREF_RLIMIT_NOFILE = makePref("rlimit-nofile"); 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 * FTP related preferences

View File

@ -105,6 +105,10 @@ extern const std::string V_ARC4;
extern const std::string V_HTTP; extern const std::string V_HTTP;
extern const std::string V_HTTPS; extern const std::string V_HTTPS;
extern const std::string V_FTP; 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_VERSION;
extern PrefPtr PREF_HELP; extern PrefPtr PREF_HELP;
@ -304,6 +308,8 @@ extern PrefPtr PREF_DSCP;
extern PrefPtr PREF_PAUSE_METADATA; extern PrefPtr PREF_PAUSE_METADATA;
// values: 1*digit // values: 1*digit
extern PrefPtr PREF_RLIMIT_NOFILE; extern PrefPtr PREF_RLIMIT_NOFILE;
// vlaues: SSLv3 | TLSv1 | TLSv1.1 | TLSv1.2
extern PrefPtr PREF_MIN_TLS_VERSION;
/** /**
* FTP related preferences * FTP related preferences

View File

@ -1011,3 +1011,5 @@
" and the next download waiting in queue gets\n" \ " and the next download waiting in queue gets\n" \
" started. But be aware that seeding item is still\n" \ " started. But be aware that seeding item is still\n" \
" recognized as active download in RPC method.") " recognized as active download in RPC method.")
#define TEXT_MIN_TLS_VERSION \
_(" --min-tls-version=VERSION Specify minimum SSL/TLS version to enable.")

View File

@ -2009,6 +2009,23 @@ bool strless(const char* a, const char* b)
return strcmp(a, b) < 0; 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 util
} // namespace aria2 } // namespace aria2

View File

@ -64,6 +64,10 @@
#include "fmt.h" #include "fmt.h"
#include "prefs.h" #include "prefs.h"
#ifdef ENABLE_SSL
#include "TLSContext.h"
#endif // ENABLE_SSL
#ifndef HAVE_SIGACTION #ifndef HAVE_SIGACTION
# define sigset_t int # define sigset_t int
#endif // HAVE_SIGACTION #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. // Checks hostname matches pattern as described in RFC 6125.
bool tlsHostnameMatch(const std::string& pattern, const std::string& hostname); bool tlsHostnameMatch(const std::string& pattern, const std::string& hostname);
TLSVersion toTLSVersion(const std::string& ver);
} // namespace util } // namespace util
} // namespace aria2 } // namespace aria2