[PATCH] wininet: TLS fallback mechanism (take2)

Hiroshi Miura miurahr at linux.com
Thu Oct 25 17:31:03 CDT 2012


OpenSSL 1.0.x now support TLSv1.1 and TLSv1.2.
Sometimes TLSv1.1/1.2 negotiation fails bacause of combination
of server/client ssl library versions.
It fixes the error connecting Evernote server with evernote client.
http://bugs.winehq.org/show_bug.cgi?id=30598

It has also mechanism enable/disable TLSv1.1/1.2 by registry entry under
SYSTEM/CurrentControlSet/Control/SecurityProvider/SCHANNEL/Protocols/
where is as same place as Windows7.
TLSv1.1/1.2 is enabled when the registry entry does not exist.

Signed-off-by: Hiroshi Miura <miurahr at linux.com>
---
 dlls/wininet/netconnection.c |  169 ++++++++++++++++++++++++++++++++----------
 1 file changed, 130 insertions(+), 39 deletions(-)

diff --git a/dlls/wininet/netconnection.c b/dlls/wininet/netconnection.c
index c944ff3..9b5f7e7 100644
--- a/dlls/wininet/netconnection.c
+++ b/dlls/wininet/netconnection.c
@@ -124,8 +124,10 @@ MAKE_FUNCPTR(SSL_load_error_strings);
 MAKE_FUNCPTR(SSLv23_method);
 MAKE_FUNCPTR(SSL_CTX_free);
 MAKE_FUNCPTR(SSL_CTX_new);
+MAKE_FUNCPTR(SSL_CTX_ctrl);
 MAKE_FUNCPTR(SSL_new);
 MAKE_FUNCPTR(SSL_free);
+MAKE_FUNCPTR(SSL_ctrl);
 MAKE_FUNCPTR(SSL_set_fd);
 MAKE_FUNCPTR(SSL_connect);
 MAKE_FUNCPTR(SSL_shutdown);
@@ -446,7 +448,51 @@ static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
     return ret;
 }
 
+#ifdef SSL_OP_NO_TLSv1_2
+static long get_tls_option(void) {
+    long tls_option;
+    DWORD type, val, size;
+    HKEY hkey,tls12_client,tls11_client;
+    LONG res;
+    const WCHAR Schannel_Prot[] = {  /* SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCANNEL\\Protocols */
+              'S','Y','S','T','E','M','\\',
+              'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+              'C','o','n','t','r','o','l','\\',
+              'S','e','c','u','r','i','t','y','P','r','o','v','i','d','e','r','s','\\',
+              'S','C','H','A','N','N','E','L','\\',
+              'P','r','o','t','o','c','o','l','s',0 };
+     const WCHAR TLS12_Client[] = {'T','L','S',' ','1','.','2','\\','C','l','i','e','n','t',0};
+     const WCHAR TLS11_Client[] = {'T','L','S',' ','1','.','1','\\','C','l','i','e','n','t',0};
+     const WCHAR DisabledByDefault[] = {'D','i','s','a','b','l','e','d','B','y','D','e','f','a','u','l','t',0};
+
+    tls_option = SSL_OP_NO_SSLv2; /* disable SSLv2 for security reason, secur32/Schannel(GnuTLS) don't support it */
+    res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+          Schannel_Prot,
+          0, KEY_READ, &hkey);
+    if (res != ERROR_SUCCESS) { /* enabled TLSv1.1/1.2 when no registry entry */
+        return tls_option;
+    }
+    if (RegOpenKeyExW(hkey, TLS12_Client, 0, KEY_READ, &tls12_client) == ERROR_SUCCESS) {
+        size = sizeof(DWORD);
+        if (RegQueryValueExW(tls12_client, DisabledByDefault, NULL, &type,  (LPBYTE) &val, &size) == ERROR_SUCCESS
+            && type == REG_DWORD) {
+            tls_option |= val?SSL_OP_NO_TLSv1_2:0;
+        }
+        RegCloseKey(tls12_client);
+    }
+    if (RegOpenKeyExW(hkey, TLS11_Client, 0, KEY_READ, &tls11_client) == ERROR_SUCCESS) {
+        size = sizeof(DWORD);
+        if (RegQueryValueExW(tls11_client, DisabledByDefault, NULL, &type,  (LPBYTE) &val, &size) == ERROR_SUCCESS
+            && type == REG_DWORD) {
+            tls_option |= val?SSL_OP_NO_TLSv1_1:0;
+        }
+        RegCloseKey(tls11_client);
+    }
+    RegCloseKey(hkey);
+    return tls_option;
+}
 #endif
+#endif /* SONAME_LIBSSL */
 
 static CRITICAL_SECTION init_ssl_cs;
 static CRITICAL_SECTION_DEBUG init_ssl_cs_debug =
@@ -491,8 +537,10 @@ static DWORD init_openssl(void)
     DYNSSL(SSLv23_method);
     DYNSSL(SSL_CTX_free);
     DYNSSL(SSL_CTX_new);
+    DYNSSL(SSL_CTX_ctrl);
     DYNSSL(SSL_new);
     DYNSSL(SSL_free);
+    DYNSSL(SSL_ctrl);
     DYNSSL(SSL_set_fd);
     DYNSSL(SSL_connect);
     DYNSSL(SSL_shutdown);
@@ -534,12 +582,20 @@ static DWORD init_openssl(void)
     DYNCRYPTO(sk_value);
 #undef DYNCRYPTO
 
+#define pSSL_CTX_set_options(ctx,op) \
+       pSSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
+#define pSSL_set_options(ssl,op) \
+       pSSL_ctrl((ssl),SSL_CTRL_OPTIONS,(op),NULL)
+
     pSSL_library_init();
     pSSL_load_error_strings();
     pBIO_new_fp(stderr, BIO_NOCLOSE); /* FIXME: should use winedebug stuff */
 
     meth = pSSLv23_method();
     ctx = pSSL_CTX_new(meth);
+#ifdef SSL_OP_NO_TLSv1_2
+    pSSL_CTX_set_options(ctx, get_tls_option());
+#endif
     if(!pSSL_CTX_set_default_verify_paths(ctx)) {
         ERR("SSL_CTX_set_default_verify_paths failed: %s\n",
             pERR_error_string(pERR_get_error(), 0));
@@ -580,33 +636,10 @@ static DWORD init_openssl(void)
 #endif
 }
 
-DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, BOOL mask_errors, DWORD timeout, netconn_t **ret)
+static DWORD create_netconn_socket(server_t *server, netconn_t *netconn, DWORD timeout)
 {
-    netconn_t *netconn;
     int result, flag;
 
-    if(useSSL) {
-        DWORD res;
-
-        TRACE("using SSL connection\n");
-
-        EnterCriticalSection(&init_ssl_cs);
-        res = init_openssl();
-        LeaveCriticalSection(&init_ssl_cs);
-        if(res != ERROR_SUCCESS)
-            return res;
-    }
-
-    netconn = heap_alloc_zero(sizeof(*netconn));
-    if(!netconn)
-        return ERROR_OUTOFMEMORY;
-
-    netconn->useSSL = useSSL;
-    netconn->socketFD = -1;
-    netconn->security_flags = security_flags | server->security_flags;
-    netconn->mask_errors = mask_errors;
-    list_init(&netconn->pool_entry);
-
     assert(server->addr_len);
     result = netconn->socketFD = socket(server->addr.ss_family, SOCK_STREAM, 0);
     if(result != -1) {
@@ -656,11 +689,42 @@ DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, BOOL m
         WARN("setsockopt(TCP_NODELAY) failed\n");
 #endif
 
+    return ERROR_SUCCESS;
+}
+
+DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, BOOL mask_errors, DWORD timeout, netconn_t **ret)
+{
+    netconn_t *netconn;
+    int result;
+
+    if(useSSL) {
+        DWORD res;
+
+        TRACE("using SSL connection\n");
+
+        EnterCriticalSection(&init_ssl_cs);
+        res = init_openssl();
+        LeaveCriticalSection(&init_ssl_cs);
+        if(res != ERROR_SUCCESS)
+            return res;
+    }
+
+    netconn = heap_alloc_zero(sizeof(*netconn));
+    if(!netconn)
+        return ERROR_OUTOFMEMORY;
+
+    netconn->useSSL = useSSL;
+    netconn->socketFD = -1;
+    netconn->security_flags = security_flags | server->security_flags;
+    netconn->mask_errors = mask_errors;
+    list_init(&netconn->pool_entry);
+
+    result = create_netconn_socket(server, netconn, timeout);
     server_addref(server);
     netconn->server = server;
 
     *ret = netconn;
-    return ERROR_SUCCESS;
+    return result;
 }
 
 void free_netconn(netconn_t *netconn)
@@ -772,24 +836,13 @@ int sock_get_error( int err )
     return err;
 }
 
-/******************************************************************************
- * NETCON_secure_connect
- * Initiates a secure connection over an existing plaintext connection.
- */
-DWORD NETCON_secure_connect(netconn_t *connection)
-{
-    DWORD res = ERROR_NOT_SUPPORTED;
 #ifdef SONAME_LIBSSL
+static DWORD netcon_secure_connect_setup(netconn_t *connection, long tls_option)
+{
     void *ssl_s;
+    DWORD res;
     int bits;
 
-    /* can't connect if we are already connected */
-    if (connection->ssl_s)
-    {
-        ERR("already connected\n");
-        return ERROR_INTERNET_CANNOT_CONNECT;
-    }
-
     ssl_s = pSSL_new(ctx);
     if (!ssl_s)
     {
@@ -798,6 +851,7 @@ DWORD NETCON_secure_connect(netconn_t *connection)
         return ERROR_OUTOFMEMORY;
     }
 
+    pSSL_set_options(ssl_s, tls_option);
     if (!pSSL_set_fd(ssl_s, connection->socketFD))
     {
         ERR("SSL_set_fd failed: %s\n",
@@ -843,6 +897,43 @@ fail:
         pSSL_shutdown(ssl_s);
         pSSL_free(ssl_s);
     }
+    return res;
+}
+#endif
+
+/******************************************************************************
+ * NETCON_secure_connect
+ * Initiates a secure connection over an existing plaintext connection.
+ */
+DWORD NETCON_secure_connect(netconn_t *connection)
+{
+    DWORD res = ERROR_NOT_SUPPORTED;
+#ifdef SONAME_LIBSSL
+    /* can't connect if we are already connected */
+    if (connection->ssl_s)
+    {
+        ERR("already connected\n");
+        return ERROR_INTERNET_CANNOT_CONNECT;
+    }
+
+    /* connect with given TLS options */
+    res = netcon_secure_connect_setup(connection, get_tls_option());
+    if (res == ERROR_SUCCESS)
+        return res;
+
+#ifdef SSL_OP_NO_TLSv1_2
+    /* FIXME: when got version alert and FIN from server */
+    /* fallback to connect without TLSv1.1/TLSv1.2        */
+    if (res == ERROR_INTERNET_SECURITY_CHANNEL_ERROR)
+    {
+        closesocket(connection->socketFD);
+        pSSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+        res = create_netconn_socket(connection->server, connection, 500);
+        if (res != ERROR_SUCCESS)
+            return res;
+        res = netcon_secure_connect_setup(connection, get_tls_option()|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+    }
+#endif
 #endif
     return res;
 }
-- 
1.7.9.5




More information about the wine-patches mailing list