Module: wine Branch: master Commit: 923ac2e546e5f86afd319d944a0a249bc1fa1e4a URL: http://source.winehq.org/git/wine.git/?a=commit;h=923ac2e546e5f86afd319d944a...
Author: Hiroshi Miura miurahr@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; }