Make the minimum TLS protocol version configurable
authorUwe Ebel <kobmaki@aol.com>
Mon, 1 Aug 2016 03:32:47 +0000 (05:32 +0200)
committerGunnar Beutner <gunnar.beutner@netways.de>
Wed, 3 Aug 2016 05:46:50 +0000 (07:46 +0200)
The ApiListener accepts all TLS versions that the underlying
OpenSSL library supports. This patch give the ability to restrict
the connection to a minimum TLS version.

fixes #11292

Signed-off-by: Gunnar Beutner <gunnar.beutner@netways.de>
doc/6-object-types.md
lib/base/tlsutility.cpp
lib/base/tlsutility.hpp
lib/remote/apilistener.cpp
lib/remote/apilistener.hpp
lib/remote/apilistener.ti

index 3b87ff8a17dc8302c5ab3dceddf2d05d45f24373..049fc5e6af2ee663d7104e05e0df678f16336f74 100644 (file)
@@ -51,6 +51,7 @@ Configuration Attributes:
   accept\_config            |**Optional.** Accept zone configuration. Defaults to `false`.
   accept\_commands          |**Optional.** Accept remote commands. Defaults to `false`.
   cipher\_list             |**Optional.** Cipher list that is allowed.
+  tls\_protocolmin          |**Optional.** Minimum TLS protocol version. Must be one of `TLSv1`, `TLSv1.1` or `TLSv1.2`. Defaults to `TLSv1`.
 
 ## <a id="objecttype-apiuser"></a> ApiUser
 
index 0315a1dbd89a7253b90eed279c53c5236f318d6f..057fc1840f761a029e3886d1bb2ce6eb02ddd336 100644 (file)
@@ -181,6 +181,28 @@ void SetCipherListToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const
        }
 }
 
+/**
+ * Set the minimum TLS protocol version to the specified SSL context.
+ *
+ * @param context The ssl context.
+ * @param tlsProtocolmin The minimum TLS protocol version.
+ */
+void SetTlsProtocolminToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& tlsProtocolmin)
+{
+       long flags = SSL_CTX_get_options(context.get());
+
+       flags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+
+       if (tlsProtocolmin == SSL_TXT_TLSV1_1)
+               flags |= SSL_OP_NO_TLSv1;
+       else if (tlsProtocolmin == SSL_TXT_TLSV1_2)
+               flags |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
+       else if (tlsProtocolmin != SSL_TXT_TLSV1)
+               BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid TLS protocol version specified."));
+
+       SSL_CTX_set_options(context.get(), flags);
+}
+
 /**
  * Loads a CRL and appends its certificates to the specified SSL context.
  *
index 6a41d481890860d24c35377f59b817e15f16df75..aef59af44c1904dfad0445dd165b2210cda6db75 100644 (file)
@@ -41,6 +41,7 @@ void I2_BASE_API InitializeOpenSSL(void);
 boost::shared_ptr<SSL_CTX> I2_BASE_API MakeSSLContext(const String& pubkey = String(), const String& privkey = String(), const String& cakey = String());
 void I2_BASE_API AddCRLToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& crlPath);
 void I2_BASE_API SetCipherListToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& cipherList);
+void I2_BASE_API SetTlsProtocolminToSSLContext(const boost::shared_ptr<SSL_CTX>& context, const String& tlsProtocolmin);
 String I2_BASE_API GetCertificateCN(const boost::shared_ptr<X509>& certificate);
 boost::shared_ptr<X509> I2_BASE_API GetX509Certificate(const String& pemfile);
 int I2_BASE_API MakeX509CSR(const String& cn, const String& keyfile, const String& csrfile = String(), const String& certfile = String(), const String& serialFile = String(), bool ca = false);
index e1b002528924ff3e64cdfb6e2067f782f5f5a024..370c47a1a6808f75f86c35b69096cab630ab526a 100644 (file)
@@ -104,6 +104,14 @@ void ApiListener::OnConfigLoaded(void)
                            + GetCipherList() + "'.", GetDebugInfo()));
                }
        }
+
+       if (!GetTlsProtocolmin().IsEmpty()){
+               try {
+                       SetTlsProtocolminToSSLContext(m_SSLContext, GetTlsProtocolmin());
+               } catch (const std::exception&) {
+                       BOOST_THROW_EXCEPTION(ScriptError("Cannot set minimum TLS protocol version to SSL context with tls_protocolmin: '" + GetTlsProtocolmin() + "'.", GetDebugInfo()));
+               }
+       }
 }
 
 void ApiListener::OnAllConfigLoaded(void)
@@ -1171,3 +1179,14 @@ Endpoint::Ptr ApiListener::GetLocalEndpoint(void) const
 {
        return m_LocalEndpoint;
 }
+
+void ApiListener::ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override
+{
+       ObjectImpl<ApiListener>::ValidateTlsProtocolmin(value, utils);
+
+       if (value != SSL_TXT_TLSV1 && value != SSL_TXT_TLSV1_1 &&
+           value != SSL_TXT_TLSV1_2) {
+               BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("tls_protocolmin"), "Invalid TLS version. "
+                   "Must be one of '" SSL_TXT_TLSV1 "', '" SSL_TXT_TLSV1_1 "' or '" SSL_TXT_TLSV1_2 "'"));
+       }
+}
index 3f9a325a85febea9f5affc98076077d4b2e7b00e..4c1771361f5983f6f998c8c26b6b9d8b6b15fbc7 100644 (file)
@@ -105,6 +105,8 @@ protected:
        virtual void OnAllConfigLoaded(void) override;
        virtual void Start(bool runtimeCreated) override;
 
+       virtual void ValidateTlsProtocolmin(const String& value, const ValidationUtils& utils) override;
+
 private:
        boost::shared_ptr<SSL_CTX> m_SSLContext;
        std::set<TcpSocket::Ptr> m_Servers;
index 91e4b0e516b21a5953d3067a41e2357fa249c700..fee11f2c8c7939eb141ade535f01f5e7fc72301e 100644 (file)
@@ -35,6 +35,9 @@ class ApiListener : ConfigObject
        [config] String cipher_list {
                default {{{ return "ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL"; }}}
        };
+       [config] String tls_protocolmin {
+               default {{{ return "TLSv1"; }}}
+       };
 
        [config] String bind_host;
        [config] String bind_port {