Juan Lang : winhttp: Check the certificate in an https connection using the crypto api.
Alexandre Julliard
julliard at winehq.org
Fri Dec 4 09:11:26 CST 2009
Module: wine
Branch: master
Commit: 82d07c405168df59db17d298bc3b8476f13540e9
URL: http://source.winehq.org/git/wine.git/?a=commit;h=82d07c405168df59db17d298bc3b8476f13540e9
Author: Juan Lang <juan.lang at gmail.com>
Date: Thu Dec 3 11:28:40 2009 -0800
winhttp: Check the certificate in an https connection using the crypto api.
---
dlls/winhttp/net.c | 174 +++++++++++++++++++++++++++++++++++++++++++---------
1 files changed, 144 insertions(+), 30 deletions(-)
diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c
index a86a5a6..3ed3b1b 100644
--- a/dlls/winhttp/net.c
+++ b/dlls/winhttp/net.c
@@ -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;
More information about the wine-cvs
mailing list