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

oldstable
Juan Lang 2009-12-03 11:28:40 -08:00 committed by Alexandre Julliard
parent 48a7580135
commit 82d07c4051
1 changed files with 144 additions and 30 deletions

View File

@ -212,15 +212,156 @@ static int sock_get_error( int err )
}
#ifdef SONAME_LIBSSL
static PCCERT_CONTEXT X509_to_cert_context(X509 *cert)
{
unsigned char *buffer, *p;
int len;
BOOL malloc = FALSE;
PCCERT_CONTEXT ret;
p = NULL;
if ((len = pi2d_X509( cert, &p )) < 0) return NULL;
/*
* 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)
{
if (!(buffer = heap_alloc( len ))) return NULL;
p = buffer;
len = pi2d_X509( cert, &p );
}
else
{
buffer = p;
malloc = TRUE;
}
ret = CertCreateCertificateContext( X509_ASN_ENCODING, buffer, len );
if (malloc) free( buffer );
else heap_free( 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_WINHTTP_SECURE_CERT_DATE_INVALID;
else if (chain->TrustStatus.dwErrorStatus &
CERT_TRUST_IS_UNTRUSTED_ROOT)
err = ERROR_WINHTTP_SECURE_INVALID_CA;
else if ((chain->TrustStatus.dwErrorStatus &
CERT_TRUST_IS_OFFLINE_REVOCATION) ||
(chain->TrustStatus.dwErrorStatus &
CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
err = ERROR_WINHTTP_SECURE_CERT_REV_FAILED;
else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
err = ERROR_WINHTTP_SECURE_CERT_REVOKED;
else if (chain->TrustStatus.dwErrorStatus &
CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
err = ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE;
else
err = ERROR_WINHTTP_SECURE_INVALID_CERT;
set_last_error( err );
ret = FALSE;
}
else
{
CERT_CHAIN_POLICY_PARA policyPara;
SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
CERT_CHAIN_POLICY_STATUS policyStatus;
sslExtraPolicyPara.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_WINHTTP_SECURE_CERT_CN_INVALID;
else
err = ERROR_WINHTTP_SECURE_INVALID_CERT;
set_last_error( 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 < ctx->chain->num; i++)
{
PCCERT_CONTEXT context;
cert = (X509 *)ctx->chain->data[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 (ret)
ret = netconn_verify_cert( endCert, store, server );
CertFreeCertificateContext( endCert );
CertCloseStore( store, 0 );
}
}
return ret;
}
#endif
@ -794,39 +935,12 @@ const void *netconn_get_certificate( netconn_t *conn )
{
#ifdef SONAME_LIBSSL
X509 *cert;
unsigned char *buffer, *p;
int len;
BOOL malloc = FALSE;
const CERT_CONTEXT *ret;
if (!conn->secure) return NULL;
if (!(cert = pSSL_get_peer_certificate( conn->ssl_conn ))) return NULL;
p = NULL;
if ((len = pi2d_X509( cert, &p )) < 0) return NULL;
/*
* 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)
{
if (!(buffer = heap_alloc( len ))) return NULL;
p = buffer;
len = pi2d_X509( cert, &p );
}
else
{
buffer = p;
malloc = TRUE;
}
ret = CertCreateCertificateContext( X509_ASN_ENCODING, buffer, len );
if (malloc) free( buffer );
else heap_free( buffer );
ret = X509_to_cert_context( cert );
return ret;
#else
return NULL;