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