winhttp: Add support for secure connections.

This commit is contained in:
Hans Leidekker 2008-08-28 13:49:19 +02:00 committed by Alexandre Julliard
parent 02ca43cbbc
commit 0b19e8559f
4 changed files with 303 additions and 5 deletions

View file

@ -19,6 +19,7 @@
#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"
#include "wine/library.h"
#include <stdarg.h>
#include <stdio.h>
@ -37,6 +38,11 @@
#ifdef HAVE_POLL_H
# include <poll.h>
#endif
#ifdef HAVE_OPENSSL_SSL_H
# include <openssl/ssl.h>
#undef FAR
#undef DSA
#endif
#include "windef.h"
#include "winbase.h"
@ -67,6 +73,42 @@ static CRITICAL_SECTION cs_gethostbyname = { &critsect_debug, -1, 0, 0, 0, 0 };
#endif
#ifdef SONAME_LIBSSL
#include <openssl/err.h>
static void *libssl_handle;
static void *libcrypto_handle;
static SSL_METHOD *method;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f
MAKE_FUNCPTR( SSL_library_init );
MAKE_FUNCPTR( SSL_load_error_strings );
MAKE_FUNCPTR( SSLv23_method );
MAKE_FUNCPTR( SSL_CTX_new );
MAKE_FUNCPTR( SSL_CTX_free );
MAKE_FUNCPTR( SSL_new );
MAKE_FUNCPTR( SSL_free );
MAKE_FUNCPTR( SSL_set_fd );
MAKE_FUNCPTR( SSL_connect );
MAKE_FUNCPTR( SSL_shutdown );
MAKE_FUNCPTR( SSL_write );
MAKE_FUNCPTR( SSL_read );
MAKE_FUNCPTR( SSL_get_verify_result );
MAKE_FUNCPTR( SSL_get_peer_certificate );
MAKE_FUNCPTR( SSL_CTX_get_timeout );
MAKE_FUNCPTR( SSL_CTX_set_timeout );
MAKE_FUNCPTR( SSL_CTX_set_default_verify_paths );
MAKE_FUNCPTR( BIO_new_fp );
MAKE_FUNCPTR( ERR_get_error );
MAKE_FUNCPTR( ERR_error_string );
#undef MAKE_FUNCPTR
#endif
/* translate a unix error code into a winsock error code */
static int sock_get_error( int err )
{
@ -132,9 +174,75 @@ static int sock_get_error( int err )
return err;
}
BOOL netconn_init( netconn_t *conn )
BOOL netconn_init( netconn_t *conn, BOOL secure )
{
conn->socket = -1;
if (!secure) return TRUE;
#if defined(SONAME_LIBSSL) && defined(SONAME_LIBCRYPTO)
if (libssl_handle) return TRUE;
if (!(libssl_handle = wine_dlopen( SONAME_LIBSSL, RTLD_NOW, NULL, 0 )))
{
ERR("Trying to use SSL but couldn't load %s. Expect trouble.\n", SONAME_LIBSSL);
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
return FALSE;
}
if (!(libcrypto_handle = wine_dlopen( SONAME_LIBCRYPTO, RTLD_NOW, NULL, 0 )))
{
ERR("Trying to use SSL but couldn't load %s. Expect trouble.\n", SONAME_LIBCRYPTO);
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
return FALSE;
}
#define LOAD_FUNCPTR(x) \
if (!(p##x = wine_dlsym( libssl_handle, #x, NULL, 0 ))) \
{ \
ERR("Failed to load symbol %s\n", #x); \
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); \
return FALSE; \
}
LOAD_FUNCPTR( SSL_library_init );
LOAD_FUNCPTR( SSL_load_error_strings );
LOAD_FUNCPTR( SSLv23_method );
LOAD_FUNCPTR( SSL_CTX_new );
LOAD_FUNCPTR( SSL_CTX_free );
LOAD_FUNCPTR( SSL_new );
LOAD_FUNCPTR( SSL_free );
LOAD_FUNCPTR( SSL_set_fd );
LOAD_FUNCPTR( SSL_connect );
LOAD_FUNCPTR( SSL_shutdown );
LOAD_FUNCPTR( SSL_write );
LOAD_FUNCPTR( SSL_read );
LOAD_FUNCPTR( SSL_get_verify_result );
LOAD_FUNCPTR( SSL_get_peer_certificate );
LOAD_FUNCPTR( SSL_CTX_get_timeout );
LOAD_FUNCPTR( SSL_CTX_set_timeout );
LOAD_FUNCPTR( SSL_CTX_set_default_verify_paths );
#undef LOAD_FUNCPTR
#define LOAD_FUNCPTR(x) \
if (!(p##x = wine_dlsym( libcrypto_handle, #x, NULL, 0 ))) \
{ \
ERR("Failed to load symbol %s\n", #x); \
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR ); \
return FALSE; \
}
LOAD_FUNCPTR( BIO_new_fp );
LOAD_FUNCPTR( ERR_get_error );
LOAD_FUNCPTR( ERR_error_string );
#undef LOAD_FUNCPTR
pSSL_library_init();
pSSL_load_error_strings();
pBIO_new_fp( stderr, BIO_NOCLOSE );
method = pSSLv23_method();
conn->ssl_ctx = pSSL_CTX_new( method );
#else
WARN("SSL support not compiled in.\n");
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
return FALSE;
#endif
return TRUE;
}
@ -158,6 +266,23 @@ BOOL netconn_close( netconn_t *conn )
{
int res;
#ifdef SONAME_LIBSSL
if (conn->secure)
{
heap_free( conn->peek_msg_mem );
conn->peek_msg_mem = NULL;
conn->peek_msg = NULL;
conn->peek_len = 0;
pSSL_shutdown( conn->ssl_conn );
pSSL_free( conn->ssl_conn );
pSSL_CTX_free( conn->ssl_ctx );
conn->ssl_conn = NULL;
conn->ssl_ctx = NULL;
conn->secure = FALSE;
}
#endif
res = close( conn->socket );
conn->socket = -1;
if (res == -1)
@ -179,9 +304,76 @@ BOOL netconn_connect( netconn_t *conn, const struct sockaddr *sockaddr, unsigned
return TRUE;
}
BOOL netconn_secure_connect( netconn_t *conn )
{
#ifdef SONAME_LIBSSL
X509 *cert;
long res;
if (!pSSL_CTX_set_default_verify_paths( conn->ssl_ctx ))
{
ERR("SSL_CTX_set_default_verify_paths failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_OUTOFMEMORY );
return FALSE;
}
if (!(conn->ssl_conn = pSSL_new( conn->ssl_ctx )))
{
ERR("SSL_new failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_OUTOFMEMORY );
goto fail;
}
if (!pSSL_set_fd( conn->ssl_conn, conn->socket ))
{
ERR("SSL_set_fd failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
goto fail;
}
if (pSSL_connect( conn->ssl_conn ) <= 0)
{
ERR("SSL_connect failed: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
goto fail;
}
if (!(cert = pSSL_get_peer_certificate( conn->ssl_conn )))
{
ERR("No certificate for server: %s\n", pERR_error_string( pERR_get_error(), 0 ));
set_last_error( ERROR_WINHTTP_SECURE_CHANNEL_ERROR );
goto fail;
}
if ((res = pSSL_get_verify_result( conn->ssl_conn )) != X509_V_OK)
{
/* FIXME: we should set an error and return, but we only print an error at the moment */
ERR("couldn't verify server certificate (%ld)\n", res);
}
TRACE("established SSL connection\n");
conn->secure = TRUE;
return TRUE;
fail:
if (conn->ssl_conn)
{
pSSL_shutdown( conn->ssl_conn );
pSSL_free( conn->ssl_conn );
conn->ssl_conn = NULL;
}
#endif
return FALSE;
}
BOOL netconn_send( netconn_t *conn, const void *msg, size_t len, int flags, int *sent )
{
if (!netconn_connected( conn )) return FALSE;
if (conn->secure)
{
#ifdef SONAME_LIBSSL
if (flags) FIXME("SSL_write doesn't support any flags (%08x)\n", flags);
*sent = pSSL_write( conn->ssl_conn, msg, len );
if (*sent < 1 && len) return FALSE;
return TRUE;
#else
return FALSE;
#endif
}
if ((*sent = send( conn->socket, msg, len, flags )) == -1)
{
set_last_error( sock_get_error( errno ) );
@ -195,6 +387,59 @@ BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd
*recvd = 0;
if (!netconn_connected( conn )) return FALSE;
if (!len) return TRUE;
if (conn->secure)
{
#ifdef SONAME_LIBSSL
if (flags & ~(MSG_PEEK | MSG_WAITALL))
FIXME("SSL_read does not support the following flags: %08x\n", flags);
/* this ugly hack is all for MSG_PEEK */
if (flags & MSG_PEEK && !conn->peek_msg)
{
if (!(conn->peek_msg = conn->peek_msg_mem = heap_alloc( len + 1 ))) return FALSE;
}
else if (flags & MSG_PEEK && conn->peek_msg)
{
if (len < conn->peek_len) FIXME("buffer isn't big enough, should we wrap?\n");
*recvd = min( len, conn->peek_len );
memcpy( buf, conn->peek_msg, *recvd );
return TRUE;
}
else if (conn->peek_msg)
{
*recvd = min( len, conn->peek_len );
memcpy( buf, conn->peek_msg, *recvd );
conn->peek_len -= *recvd;
conn->peek_msg += *recvd;
if (conn->peek_len == 0)
{
heap_free( conn->peek_msg_mem );
conn->peek_msg_mem = NULL;
conn->peek_msg = NULL;
}
/* check if we have enough data from the peek buffer */
if (!(flags & MSG_WAITALL) || (*recvd == len)) return TRUE;
}
*recvd += pSSL_read( conn->ssl_conn, (char *)buf + *recvd, len - *recvd );
if (flags & MSG_PEEK) /* must copy into buffer */
{
conn->peek_len = *recvd;
if (!*recvd)
{
heap_free( conn->peek_msg_mem );
conn->peek_msg_mem = NULL;
conn->peek_msg = NULL;
}
else memcpy( conn->peek_msg, buf, *recvd );
}
if (*recvd < 1 && len) return FALSE;
return TRUE;
#else
return FALSE;
#endif
}
if ((*recvd = recv( conn->socket, buf, len, flags )) == -1)
{
set_last_error( sock_get_error( errno ) );
@ -210,6 +455,14 @@ BOOL netconn_query_data_available( netconn_t *conn, DWORD *available )
#endif
*available = 0;
if (!netconn_connected( conn )) return FALSE;
if (conn->secure)
{
#ifdef SONAME_LIBSSL
if (conn->peek_msg) *available = conn->peek_len;
#endif
return TRUE;
}
#ifdef FIONREAD
if (!(ret = ioctl( conn->socket, FIONREAD, &unread )))
{
@ -228,6 +481,42 @@ BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen )
if (!netconn_connected( conn )) return FALSE;
if (conn->secure)
{
#ifdef SONAME_LIBSSL
long timeout;
timeout = pSSL_CTX_get_timeout( conn->ssl_ctx );
pSSL_CTX_set_timeout( conn->ssl_ctx, DEFAULT_RECEIVE_TIMEOUT );
while (recvd < *buflen)
{
int dummy;
if (!netconn_recv( conn, &buffer[recvd], 1, 0, &dummy ))
{
set_last_error( ERROR_CONNECTION_ABORTED );
break;
}
if (buffer[recvd] == '\n')
{
ret = TRUE;
break;
}
if (buffer[recvd] != '\r') recvd++;
}
pSSL_CTX_set_timeout( conn->ssl_ctx, timeout );
if (ret)
{
buffer[recvd++] = 0;
*buflen = recvd;
TRACE("received line %s\n", debugstr_a(buffer));
}
return ret;
#else
return FALSE;
#endif
}
pfd.fd = conn->socket;
pfd.events = POLLIN;
while (recvd < *buflen)
@ -257,9 +546,8 @@ BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen )
buffer[recvd++] = 0;
*buflen = recvd;
TRACE("received line %s\n", debugstr_a(buffer));
return TRUE;
}
return FALSE;
return ret;
}
DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )

View file

@ -683,6 +683,12 @@ static BOOL open_connection( request_t *request )
heap_free( addressW );
return FALSE;
}
if (request->hdr.flags & WINHTTP_FLAG_SECURE && !netconn_secure_connect( &request->netconn ))
{
netconn_close( &request->netconn );
heap_free( addressW );
return FALSE;
}
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, 0 );

View file

@ -270,7 +270,7 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o
request->connect = connect;
list_add_head( &connect->hdr.children, &request->hdr.entry );
if (!netconn_init( &request->netconn )) goto end;
if (!netconn_init( &request->netconn, request->hdr.flags & WINHTTP_FLAG_SECURE )) goto end;
if (!verb) verb = get;
if (!object) object = slash;

View file

@ -87,6 +87,9 @@ typedef struct
typedef struct
{
int socket;
BOOL secure; /* SSL active on connection? */
void *ssl_ctx;
void *ssl_conn;
char *peek_msg;
char *peek_msg_mem;
size_t peek_len;
@ -130,10 +133,11 @@ BOOL netconn_connect( netconn_t *, const struct sockaddr *, unsigned int );
BOOL netconn_connected( netconn_t * );
BOOL netconn_create( netconn_t *, int, int, int );
BOOL netconn_get_next_line( netconn_t *, char *, DWORD * );
BOOL netconn_init( netconn_t * );
BOOL netconn_init( netconn_t *, BOOL );
BOOL netconn_query_data_available( netconn_t *, DWORD * );
BOOL netconn_recv( netconn_t *, void *, size_t, int, int * );
BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_in * );
BOOL netconn_secure_connect( netconn_t * );
BOOL netconn_send( netconn_t *, const void *, size_t, int, int * );
static inline void *heap_alloc( SIZE_T size )