wininet: Check the certificate in an https connection using the crypto api.

oldstable
Juan Lang 2009-12-13 16:05:35 -08:00 committed by Alexandre Julliard
parent 6217326a09
commit 03fe473ad7
1 changed files with 153 additions and 45 deletions

View File

@ -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;