Hiroshi Miura : wininet: Add a TLS fallback mechanism.

Alexandre Julliard julliard at winehq.org
Thu Nov 1 14:42:59 CDT 2012


Module: wine
Branch: master
Commit: 923ac2e546e5f86afd319d944a0a249bc1fa1e4a
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=923ac2e546e5f86afd319d944a0a249bc1fa1e4a

Author: Hiroshi Miura <miurahr at linux.com>
Date:   Wed Oct 31 22:29:26 2012 +0900

wininet: Add a TLS fallback mechanism.

---

 dlls/wininet/netconnection.c |  166 ++++++++++++++++++++++++++++++++----------
 1 files changed, 127 insertions(+), 39 deletions(-)

diff --git a/dlls/wininet/netconnection.c b/dlls/wininet/netconnection.c
index c944ff3..b746b54 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,50 @@ static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
     return ret;
 }
 
+static long get_tls_option(void) {
+    long tls_option = SSL_OP_NO_SSLv2; /* disable SSLv2 for security reason, secur32/Schannel(GnuTLS) don't support it */
+#ifdef SSL_OP_NO_TLSv1_2
+    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};
+
+    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);
 #endif
+    return tls_option;
+}
+#endif /* SONAME_LIBSSL */
 
 static CRITICAL_SECTION init_ssl_cs;
 static CRITICAL_SECTION_DEBUG init_ssl_cs_debug =
@@ -491,8 +536,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 +581,18 @@ 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);
+    pSSL_CTX_set_options(ctx, get_tls_option());
     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 +633,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 +686,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 +833,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 +848,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 +894,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;
 }




More information about the wine-cvs mailing list