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

Alexandre Julliard julliard at winehq.org
Mon Dec 14 09:51:16 CST 2009


Module: wine
Branch: master
Commit: 03fe473ad79d15b41849db09a39c3c88aa773f06
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=03fe473ad79d15b41849db09a39c3c88aa773f06

Author: Juan Lang <juan.lang at gmail.com>
Date:   Sun Dec 13 16:05:35 2009 -0800

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

---

 dlls/wininet/netconnection.c |  198 ++++++++++++++++++++++++++++++++----------
 1 files changed, 153 insertions(+), 45 deletions(-)

diff --git a/dlls/wininet/netconnection.c b/dlls/wininet/netconnection.c
index e0667ec..39ef464 100644
--- a/dlls/wininet/netconnection.c
+++ b/dlls/wininet/netconnection.c
@@ -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;




More information about the wine-cvs mailing list