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