mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-11 03:17:16 +00:00
wininet: Check the certificate in an https connection using the crypto api.
This commit is contained in:
parent
6217326a09
commit
03fe473ad7
|
@ -23,6 +23,8 @@
|
|||
#include "config.h"
|
||||
#include "wine/port.h"
|
||||
|
||||
#define NONAMELESSUNION
|
||||
|
||||
#if defined(__MINGW32__) || defined (_MSC_VER)
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
@ -152,6 +154,8 @@ MAKE_FUNCPTR(ERR_free_strings);
|
|||
MAKE_FUNCPTR(ERR_get_error);
|
||||
MAKE_FUNCPTR(ERR_error_string);
|
||||
MAKE_FUNCPTR(i2d_X509);
|
||||
MAKE_FUNCPTR(sk_num);
|
||||
MAKE_FUNCPTR(sk_value);
|
||||
#undef MAKE_FUNCPTR
|
||||
|
||||
static CRITICAL_SECTION *ssl_locks;
|
||||
|
@ -169,16 +173,159 @@ static void ssl_lock_callback(int mode, int type, const char *file, int line)
|
|||
LeaveCriticalSection(&ssl_locks[type]);
|
||||
}
|
||||
|
||||
static PCCERT_CONTEXT X509_to_cert_context(X509 *cert)
|
||||
{
|
||||
unsigned char* buffer,*p;
|
||||
INT len;
|
||||
BOOL malloced = FALSE;
|
||||
PCCERT_CONTEXT ret;
|
||||
|
||||
p = NULL;
|
||||
len = pi2d_X509(cert,&p);
|
||||
/*
|
||||
* SSL 0.9.7 and above malloc the buffer if it is null.
|
||||
* however earlier version do not and so we would need to alloc the buffer.
|
||||
*
|
||||
* see the i2d_X509 man page for more details.
|
||||
*/
|
||||
if (!p)
|
||||
{
|
||||
buffer = HeapAlloc(GetProcessHeap(),0,len);
|
||||
p = buffer;
|
||||
len = pi2d_X509(cert,&p);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = p;
|
||||
malloced = TRUE;
|
||||
}
|
||||
|
||||
ret = CertCreateCertificateContext(X509_ASN_ENCODING,buffer,len);
|
||||
|
||||
if (malloced)
|
||||
free(buffer);
|
||||
else
|
||||
HeapFree(GetProcessHeap(),0,buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
|
||||
WCHAR *server)
|
||||
{
|
||||
BOOL ret;
|
||||
CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
|
||||
PCCERT_CHAIN_CONTEXT chain;
|
||||
char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
|
||||
char *server_auth[] = { oid_server_auth };
|
||||
DWORD err;
|
||||
|
||||
TRACE("verifying %s\n", debugstr_w(server));
|
||||
chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
|
||||
chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
|
||||
if ((ret = CertGetCertificateChain(NULL, cert, NULL, store, &chainPara, 0,
|
||||
NULL, &chain)))
|
||||
{
|
||||
if (chain->TrustStatus.dwErrorStatus)
|
||||
{
|
||||
if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
|
||||
err = ERROR_INTERNET_SEC_CERT_DATE_INVALID;
|
||||
else if (chain->TrustStatus.dwErrorStatus &
|
||||
CERT_TRUST_IS_UNTRUSTED_ROOT)
|
||||
err = ERROR_INTERNET_INVALID_CA;
|
||||
else if ((chain->TrustStatus.dwErrorStatus &
|
||||
CERT_TRUST_IS_OFFLINE_REVOCATION) ||
|
||||
(chain->TrustStatus.dwErrorStatus &
|
||||
CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
|
||||
err = ERROR_INTERNET_SEC_CERT_NO_REV;
|
||||
else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
|
||||
err = ERROR_INTERNET_SEC_CERT_REVOKED;
|
||||
else if (chain->TrustStatus.dwErrorStatus &
|
||||
CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
|
||||
err = ERROR_INTERNET_SEC_INVALID_CERT;
|
||||
else
|
||||
err = ERROR_INTERNET_SEC_INVALID_CERT;
|
||||
INTERNET_SetLastError(err);
|
||||
ret = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
CERT_CHAIN_POLICY_PARA policyPara;
|
||||
SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
|
||||
CERT_CHAIN_POLICY_STATUS policyStatus;
|
||||
|
||||
sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
|
||||
sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
|
||||
sslExtraPolicyPara.pwszServerName = server;
|
||||
policyPara.cbSize = sizeof(policyPara);
|
||||
policyPara.dwFlags = 0;
|
||||
policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
|
||||
ret = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
|
||||
chain, &policyPara, &policyStatus);
|
||||
/* Any error in the policy status indicates that the
|
||||
* policy couldn't be verified.
|
||||
*/
|
||||
if (ret && policyStatus.dwError)
|
||||
{
|
||||
if (policyStatus.dwError == CERT_E_CN_NO_MATCH)
|
||||
err = ERROR_INTERNET_SEC_CERT_CN_INVALID;
|
||||
else
|
||||
err = ERROR_INTERNET_SEC_INVALID_CERT;
|
||||
INTERNET_SetLastError(err);
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
CertFreeCertificateChain(chain);
|
||||
}
|
||||
TRACE("returning %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
SSL *ssl;
|
||||
WCHAR *server;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
ssl = pX509_STORE_CTX_get_ex_data(ctx,
|
||||
pSSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
server = pSSL_get_ex_data(ssl, hostname_idx);
|
||||
FIXME("verify %s\n", debugstr_w(server));
|
||||
return preverify_ok;
|
||||
if (preverify_ok)
|
||||
{
|
||||
HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
|
||||
CERT_STORE_CREATE_NEW_FLAG, NULL);
|
||||
|
||||
if (store)
|
||||
{
|
||||
X509 *cert;
|
||||
int i;
|
||||
PCCERT_CONTEXT endCert = NULL;
|
||||
|
||||
ret = TRUE;
|
||||
for (i = 0; ret && i < psk_num((struct stack_st *)ctx->chain); i++)
|
||||
{
|
||||
PCCERT_CONTEXT context;
|
||||
|
||||
cert = (X509 *)psk_value((struct stack_st *)ctx->chain, i);
|
||||
if ((context = X509_to_cert_context(cert)))
|
||||
{
|
||||
if (i == 0)
|
||||
ret = CertAddCertificateContextToStore(store, context,
|
||||
CERT_STORE_ADD_ALWAYS, &endCert);
|
||||
else
|
||||
ret = CertAddCertificateContextToStore(store, context,
|
||||
CERT_STORE_ADD_ALWAYS, NULL);
|
||||
CertFreeCertificateContext(context);
|
||||
}
|
||||
}
|
||||
if (!endCert) ret = FALSE;
|
||||
if (ret)
|
||||
ret = netconn_verify_cert(endCert, store, server);
|
||||
CertFreeCertificateContext(endCert);
|
||||
CertCloseStore(store, 0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -268,6 +415,8 @@ DWORD NETCON_init(WININET_NETCONNECTION *connection, BOOL useSSL)
|
|||
DYNCRYPTO(ERR_get_error);
|
||||
DYNCRYPTO(ERR_error_string);
|
||||
DYNCRYPTO(i2d_X509);
|
||||
DYNCRYPTO(sk_num);
|
||||
DYNCRYPTO(sk_value);
|
||||
#undef DYNCRYPTO
|
||||
|
||||
pSSL_library_init();
|
||||
|
@ -459,13 +608,7 @@ DWORD NETCON_close(WININET_NETCONNECTION *connection)
|
|||
return sock_get_error(errno);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
#ifdef SONAME_LIBSSL
|
||||
static BOOL check_hostname(X509 *cert, LPCWSTR hostname)
|
||||
{
|
||||
/* FIXME: implement */
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* NETCON_secure_connect
|
||||
* Initiates a secure connection over an existing plaintext connection.
|
||||
|
@ -525,12 +668,6 @@ DWORD NETCON_secure_connect(WININET_NETCONNECTION *connection, LPWSTR hostname)
|
|||
* the moment */
|
||||
}
|
||||
|
||||
if (!check_hostname(cert, hostname))
|
||||
{
|
||||
res = ERROR_INTERNET_SEC_CERT_CN_INVALID;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
connection->useSSL = TRUE;
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
|
@ -662,42 +799,13 @@ LPCVOID NETCON_GetCert(WININET_NETCONNECTION *connection)
|
|||
{
|
||||
#ifdef SONAME_LIBSSL
|
||||
X509* cert;
|
||||
unsigned char* buffer,*p;
|
||||
INT len;
|
||||
BOOL malloced = FALSE;
|
||||
LPCVOID r = NULL;
|
||||
|
||||
if (!connection->useSSL)
|
||||
return NULL;
|
||||
|
||||
cert = pSSL_get_peer_certificate(connection->ssl_s);
|
||||
p = NULL;
|
||||
len = pi2d_X509(cert,&p);
|
||||
/*
|
||||
* SSL 0.9.7 and above malloc the buffer if it is null.
|
||||
* however earlier version do not and so we would need to alloc the buffer.
|
||||
*
|
||||
* see the i2d_X509 man page for more details.
|
||||
*/
|
||||
if (!p)
|
||||
{
|
||||
buffer = HeapAlloc(GetProcessHeap(),0,len);
|
||||
p = buffer;
|
||||
len = pi2d_X509(cert,&p);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = p;
|
||||
malloced = TRUE;
|
||||
}
|
||||
|
||||
r = CertCreateCertificateContext(X509_ASN_ENCODING,buffer,len);
|
||||
|
||||
if (malloced)
|
||||
free(buffer);
|
||||
else
|
||||
HeapFree(GetProcessHeap(),0,buffer);
|
||||
|
||||
r = X509_to_cert_context(cert);
|
||||
return r;
|
||||
#else
|
||||
return NULL;
|
||||
|
|
Loading…
Reference in a new issue