[CMAKE]
[reactos.git] / dll / win32 / wininet / netconnection.c
index 5aa647a..6e569a3 100644 (file)
@@ -60,6 +60,7 @@
 #endif
 #ifdef HAVE_OPENSSL_SSL_H
 # include <openssl/ssl.h>
+# include <openssl/opensslv.h>
 #undef FAR
 #undef DSA
 #endif
@@ -113,10 +114,15 @@ static CRITICAL_SECTION init_ssl_cs = { &init_ssl_cs_debug, -1, 0, 0, 0, 0 };
 static void *OpenSSL_ssl_handle;
 static void *OpenSSL_crypto_handle;
 
+#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER> 0x1000000)
+static const SSL_METHOD *meth;
+#else
 static SSL_METHOD *meth;
+#endif
 static SSL_CTX *ctx;
 static int hostname_idx;
 static int error_idx;
+static int conn_idx;
 
 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
 
@@ -139,12 +145,13 @@ MAKE_FUNCPTR(SSL_get_ex_new_index);
 MAKE_FUNCPTR(SSL_get_ex_data);
 MAKE_FUNCPTR(SSL_set_ex_data);
 MAKE_FUNCPTR(SSL_get_ex_data_X509_STORE_CTX_idx);
-MAKE_FUNCPTR(SSL_get_verify_result);
 MAKE_FUNCPTR(SSL_get_peer_certificate);
 MAKE_FUNCPTR(SSL_CTX_get_timeout);
 MAKE_FUNCPTR(SSL_CTX_set_timeout);
 MAKE_FUNCPTR(SSL_CTX_set_default_verify_paths);
 MAKE_FUNCPTR(SSL_CTX_set_verify);
+MAKE_FUNCPTR(SSL_get_current_cipher);
+MAKE_FUNCPTR(SSL_CIPHER_get_bits);
 MAKE_FUNCPTR(X509_STORE_CTX_get_ex_data);
 
 /* OpenSSL's libcrypto functions that we use */
@@ -214,7 +221,7 @@ static PCCERT_CONTEXT X509_to_cert_context(X509 *cert)
 }
 
 static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
-    WCHAR *server)
+    WCHAR *server, DWORD security_flags)
 {
     BOOL ret;
     CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
@@ -231,38 +238,58 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
     {
         if (chain->TrustStatus.dwErrorStatus)
         {
+            static const DWORD supportedErrors =
+                CERT_TRUST_IS_NOT_TIME_VALID |
+                CERT_TRUST_IS_UNTRUSTED_ROOT |
+                CERT_TRUST_IS_OFFLINE_REVOCATION |
+                CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
+                CERT_TRUST_IS_REVOKED |
+                CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+
             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)
+                     CERT_TRUST_IS_UNTRUSTED_ROOT &&
+                     !(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
                 err = ERROR_INTERNET_INVALID_CA;
-            else if ((chain->TrustStatus.dwErrorStatus &
+            else if (!(security_flags & SECURITY_FLAG_IGNORE_REVOCATION) &&
+                     ((chain->TrustStatus.dwErrorStatus &
                       CERT_TRUST_IS_OFFLINE_REVOCATION) ||
-                     (chain->TrustStatus.dwErrorStatus &
-                      CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
+                      (chain->TrustStatus.dwErrorStatus &
+                       CERT_TRUST_REVOCATION_STATUS_UNKNOWN)))
                 err = ERROR_INTERNET_SEC_CERT_NO_REV;
-            else if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)
+            else if (!(security_flags & SECURITY_FLAG_IGNORE_REVOCATION) &&
+                     (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)
+            else if (!(security_flags & SECURITY_FLAG_IGNORE_WRONG_USAGE) &&
+                     (chain->TrustStatus.dwErrorStatus &
+                      CERT_TRUST_IS_NOT_VALID_FOR_USAGE))
                 err = ERROR_INTERNET_SEC_INVALID_CERT;
-            else
+            else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors)
                 err = ERROR_INTERNET_SEC_INVALID_CERT;
         }
-        else
+        if (!err)
         {
             CERT_CHAIN_POLICY_PARA policyPara;
             SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
             CERT_CHAIN_POLICY_STATUS policyStatus;
+            CERT_CHAIN_CONTEXT chainCopy;
 
+            /* Clear chain->TrustStatus.dwErrorStatus so
+             * CertVerifyCertificateChainPolicy will verify additional checks
+             * rather than stopping with an existing, ignored error.
+             */
+            memcpy(&chainCopy, chain, sizeof(chainCopy));
+            chainCopy.TrustStatus.dwErrorStatus = 0;
             sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
             sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
             sslExtraPolicyPara.pwszServerName = server;
+            sslExtraPolicyPara.fdwChecks = security_flags;
             policyPara.cbSize = sizeof(policyPara);
             policyPara.dwFlags = 0;
             policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
             ret = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
-                chain, &policyPara, &policyStatus);
+                &chainCopy, &policyPara, &policyStatus);
             /* Any error in the policy status indicates that the
              * policy couldn't be verified.
              */
@@ -285,55 +312,52 @@ static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
     SSL *ssl;
     WCHAR *server;
     BOOL ret = FALSE;
+    HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
+        CERT_STORE_CREATE_NEW_FLAG, NULL);
+    WININET_NETCONNECTION *conn;
 
     ssl = pX509_STORE_CTX_get_ex_data(ctx,
         pSSL_get_ex_data_X509_STORE_CTX_idx());
     server = pSSL_get_ex_data(ssl, hostname_idx);
-    if (preverify_ok)
+    conn = pSSL_get_ex_data(ssl, conn_idx);
+    if (store)
     {
-        HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
-            CERT_STORE_CREATE_NEW_FLAG, NULL);
+        X509 *cert;
+        int i;
+        PCCERT_CONTEXT endCert = NULL;
 
-        if (store)
+        ret = TRUE;
+        for (i = 0; ret && i < psk_num((struct stack_st *)ctx->chain); i++)
         {
-            X509 *cert;
-            int i;
-            PCCERT_CONTEXT endCert = NULL;
+            PCCERT_CONTEXT context;
 
-            ret = TRUE;
-            for (i = 0; ret && i < psk_num((struct stack_st *)ctx->chain); i++)
+            cert = (X509 *)psk_value((struct stack_st *)ctx->chain, i);
+            if ((context = X509_to_cert_context(cert)))
             {
-                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 (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)
-            {
-                DWORD_PTR err = netconn_verify_cert(endCert, store, server);
+        }
+        if (!endCert) ret = FALSE;
+        if (ret)
+        {
+            DWORD_PTR err = netconn_verify_cert(endCert, store, server,
+                                                conn->security_flags);
 
-                if (err)
-                {
-                    pSSL_set_ex_data(ssl, error_idx, (void *)err);
-                    ret = FALSE;
-                }
+            if (err)
+            {
+                pSSL_set_ex_data(ssl, error_idx, (void *)err);
+                ret = FALSE;
             }
-            CertFreeCertificateContext(endCert);
-            CertCloseStore(store, 0);
         }
-    } else
-        pSSL_set_ex_data(ssl, error_idx, (void *)ERROR_INTERNET_SEC_CERT_ERRORS);
-
+        CertFreeCertificateContext(endCert);
+        CertCloseStore(store, 0);
+    }
     return ret;
 }
 
@@ -400,12 +424,13 @@ DWORD NETCON_init(WININET_NETCONNECTION *connection, BOOL useSSL)
        DYNSSL(SSL_get_ex_data);
        DYNSSL(SSL_set_ex_data);
        DYNSSL(SSL_get_ex_data_X509_STORE_CTX_idx);
-       DYNSSL(SSL_get_verify_result);
        DYNSSL(SSL_get_peer_certificate);
        DYNSSL(SSL_CTX_get_timeout);
        DYNSSL(SSL_CTX_set_timeout);
        DYNSSL(SSL_CTX_set_default_verify_paths);
        DYNSSL(SSL_CTX_set_verify);
+        DYNSSL(SSL_get_current_cipher);
+        DYNSSL(SSL_CIPHER_get_bits);
        DYNSSL(X509_STORE_CTX_get_ex_data);
 #undef DYNSSL
 
@@ -460,6 +485,15 @@ DWORD NETCON_init(WININET_NETCONNECTION *connection, BOOL useSSL)
             LeaveCriticalSection(&init_ssl_cs);
             return ERROR_OUTOFMEMORY;
         }
+        conn_idx = pSSL_get_ex_new_index(0, (void *)"netconn index",
+                NULL, NULL, NULL);
+        if (conn_idx == -1)
+        {
+            ERR("SSL_get_ex_new_index failed; %s\n",
+                pERR_error_string(pERR_get_error(), 0));
+            LeaveCriticalSection(&init_ssl_cs);
+            return ERROR_OUTOFMEMORY;
+        }
         pSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, netconn_secure_verify);
 
         pCRYPTO_set_id_callback(ssl_thread_id);
@@ -513,6 +547,7 @@ BOOL NETCON_connected(WININET_NETCONNECTION *connection)
         return TRUE;
 }
 
+#if 0
 /* translate a unix error code into a winsock one */
 int sock_get_error( int err )
 {
@@ -579,6 +614,7 @@ int sock_get_error( int err )
 #endif
     return err;
 }
+#endif
 
 /******************************************************************************
  * NETCON_create
@@ -635,10 +671,8 @@ DWORD NETCON_close(WININET_NETCONNECTION *connection)
 DWORD NETCON_secure_connect(WININET_NETCONNECTION *connection, LPWSTR hostname)
 {
     DWORD res = ERROR_NOT_SUPPORTED;
-#ifdef SONAME_LIBSSL
-    long verify_res;
-    X509 *cert;
 
+#ifdef SONAME_LIBSSL
     /* can't connect if we are already connected */
     if (connection->useSSL)
     {
@@ -663,29 +697,27 @@ DWORD NETCON_secure_connect(WININET_NETCONNECTION *connection, LPWSTR hostname)
         goto fail;
     }
 
-    if (pSSL_connect(connection->ssl_s) <= 0)
+    if (!pSSL_set_ex_data(connection->ssl_s, hostname_idx, hostname))
     {
-        res = (DWORD_PTR)pSSL_get_ex_data(connection->ssl_s, error_idx);
-        if (!res)
-            res = ERROR_INTERNET_SECURITY_CHANNEL_ERROR;
-        ERR("SSL_connect failed: %d\n", res);
+        ERR("SSL_set_ex_data failed: %s\n",
+            pERR_error_string(pERR_get_error(), 0));
+        res = ERROR_INTERNET_SECURITY_CHANNEL_ERROR;
         goto fail;
     }
-    pSSL_set_ex_data(connection->ssl_s, hostname_idx, hostname);
-    cert = pSSL_get_peer_certificate(connection->ssl_s);
-    if (!cert)
+    if (!pSSL_set_ex_data(connection->ssl_s, conn_idx, connection))
     {
-        ERR("no certificate for server %s\n", debugstr_w(hostname));
-        /* FIXME: is this the best error? */
-        res = ERROR_INTERNET_INVALID_CA;
+        ERR("SSL_set_ex_data failed: %s\n",
+            pERR_error_string(pERR_get_error(), 0));
+        res = ERROR_INTERNET_SECURITY_CHANNEL_ERROR;
         goto fail;
     }
-    verify_res = pSSL_get_verify_result(connection->ssl_s);
-    if (verify_res != X509_V_OK)
+    if (pSSL_connect(connection->ssl_s) <= 0)
     {
-        ERR("couldn't verify the security of the connection, %ld\n", verify_res);
-        /* FIXME: we should set an error and return, but we only warn at
-         * the moment */
+        res = (DWORD_PTR)pSSL_get_ex_data(connection->ssl_s, error_idx);
+        if (!res)
+            res = ERROR_INTERNET_SECURITY_CHANNEL_ERROR;
+        ERR("SSL_connect failed: %d\n", res);
+        goto fail;
     }
 
     connection->useSSL = TRUE;
@@ -770,6 +802,8 @@ DWORD NETCON_recv(WININET_NETCONNECTION *connection, void *buf, size_t len, int
     if (!connection->useSSL)
     {
        *recvd = recv(connection->socketFD, buf, len, flags);
+        if(!*recvd)
+            NETCON_close(connection);
        return *recvd == -1 ? sock_get_error(errno) :  ERROR_SUCCESS;
     }
     else
@@ -779,8 +813,10 @@ DWORD NETCON_recv(WININET_NETCONNECTION *connection, void *buf, size_t len, int
 
         /* Check if EOF was received */
         if(!*recvd && (pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_ZERO_RETURN
-                    || pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_SYSCALL))
+                    || pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_SYSCALL)) {
+            NETCON_close(connection);
             return ERROR_SUCCESS;
+        }
 
         return *recvd > 0 ? ERROR_SUCCESS : ERROR_INTERNET_CONNECTION_ABORTED;
 #else
@@ -838,6 +874,24 @@ LPCVOID NETCON_GetCert(WININET_NETCONNECTION *connection)
 #endif
 }
 
+int NETCON_GetCipherStrength(WININET_NETCONNECTION *connection)
+{
+#ifdef SONAME_LIBSSL
+    SSL_CIPHER *cipher;
+    int bits = 0;
+
+    if (!connection->useSSL)
+        return 0;
+    cipher = pSSL_get_current_cipher(connection->ssl_s);
+    if (!cipher)
+        return 0;
+    pSSL_CIPHER_get_bits(cipher, &bits);
+    return bits;
+#else
+    return 0;
+#endif
+}
+
 DWORD NETCON_set_timeout(WININET_NETCONNECTION *connection, BOOL send, int value)
 {
     int result;