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