Commit 868575a4 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

wininet: Store certificate error information in security flags.

parent f8f2273b
...@@ -4641,7 +4641,9 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) ...@@ -4641,7 +4641,9 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
request->server->addr_str, request->server->addr_str,
strlen(request->server->addr_str)+1); strlen(request->server->addr_str)+1);
res = create_netconn(is_https, request->server, request->security_flags, request->connect_timeout, &netconn); res = create_netconn(is_https, request->server, request->security_flags,
(request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0,
request->connect_timeout, &netconn);
if(res != ERROR_SUCCESS) { if(res != ERROR_SUCCESS) {
ERR("create_netconn failed: %u\n", res); ERR("create_netconn failed: %u\n", res);
return res; return res;
...@@ -4664,20 +4666,6 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) ...@@ -4664,20 +4666,6 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
res = HTTP_SecureProxyConnect(request); res = HTTP_SecureProxyConnect(request);
if(res == ERROR_SUCCESS) if(res == ERROR_SUCCESS)
res = NETCON_secure_connect(request->netconn); res = NETCON_secure_connect(request->netconn);
if(res != ERROR_SUCCESS)
{
WARN("Couldn't connect securely to host\n");
if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
|| res == ERROR_INTERNET_INVALID_CA
|| res == ERROR_INTERNET_SEC_CERT_NO_REV
|| res == ERROR_INTERNET_SEC_CERT_REV_FAILED
|| res == ERROR_INTERNET_SEC_CERT_REVOKED
|| res == ERROR_INTERNET_SEC_INVALID_CERT
|| res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
res = ERROR_INTERNET_SEC_CERT_ERRORS;
}
} }
if(res != ERROR_SUCCESS) { if(res != ERROR_SUCCESS) {
......
...@@ -86,6 +86,7 @@ typedef struct ...@@ -86,6 +86,7 @@ typedef struct
void *ssl_s; void *ssl_s;
server_t *server; server_t *server;
DWORD security_flags; DWORD security_flags;
BOOL mask_errors;
BOOL keep_alive; BOOL keep_alive;
DWORD64 keep_until; DWORD64 keep_until;
...@@ -532,7 +533,7 @@ VOID INTERNET_SendCallback(object_header_t *hdr, DWORD_PTR dwContext, ...@@ -532,7 +533,7 @@ VOID INTERNET_SendCallback(object_header_t *hdr, DWORD_PTR dwContext,
DWORD dwStatusInfoLength) DECLSPEC_HIDDEN; DWORD dwStatusInfoLength) DECLSPEC_HIDDEN;
BOOL INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto, WCHAR *foundProxy, DWORD *foundProxyLen) DECLSPEC_HIDDEN; BOOL INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto, WCHAR *foundProxy, DWORD *foundProxyLen) DECLSPEC_HIDDEN;
DWORD create_netconn(BOOL, server_t *, DWORD, DWORD, netconn_t **) DECLSPEC_HIDDEN; DWORD create_netconn(BOOL,server_t*,DWORD,BOOL,DWORD,netconn_t**) DECLSPEC_HIDDEN;
void free_netconn(netconn_t*) DECLSPEC_HIDDEN; void free_netconn(netconn_t*) DECLSPEC_HIDDEN;
void NETCON_unload(void) DECLSPEC_HIDDEN; void NETCON_unload(void) DECLSPEC_HIDDEN;
DWORD NETCON_secure_connect(netconn_t *connection) DECLSPEC_HIDDEN; DWORD NETCON_secure_connect(netconn_t *connection) DECLSPEC_HIDDEN;
...@@ -559,4 +560,12 @@ typedef struct ...@@ -559,4 +560,12 @@ typedef struct
const char* name; const char* name;
} wininet_flag_info; } wininet_flag_info;
/* Undocumented security flags */
#define _SECURITY_FLAG_CERT_INVALID_CA 0x00800000
#define _SECURITY_FLAG_CERT_INVALID_CN 0x02000000
#define _SECURITY_ERROR_FLAGS_MASK \
(_SECURITY_FLAG_CERT_INVALID_CA \
|_SECURITY_FLAG_CERT_INVALID_CN)
#endif /* _WINE_INTERNET_H_ */ #endif /* _WINE_INTERNET_H_ */
...@@ -214,26 +214,15 @@ static PCCERT_CONTEXT X509_to_cert_context(X509 *cert) ...@@ -214,26 +214,15 @@ static PCCERT_CONTEXT X509_to_cert_context(X509 *cert)
return ret; return ret;
} }
static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store, static DWORD netconn_verify_cert(netconn_t *conn, PCCERT_CONTEXT cert, HCERTSTORE store)
WCHAR *server, DWORD security_flags)
{ {
BOOL ret; BOOL ret;
CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } }; CERT_CHAIN_PARA chainPara = { sizeof(chainPara), { 0 } };
PCCERT_CHAIN_CONTEXT chain; PCCERT_CHAIN_CONTEXT chain;
char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH; char oid_server_auth[] = szOID_PKIX_KP_SERVER_AUTH;
char *server_auth[] = { oid_server_auth }; char *server_auth[] = { oid_server_auth };
DWORD err = ERROR_SUCCESS, chainFlags = 0; DWORD err = ERROR_SUCCESS, chainFlags = 0, errors;
TRACE("verifying %s\n", debugstr_w(server));
chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
if (!(security_flags & SECURITY_FLAG_IGNORE_REVOCATION))
chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
if ((ret = CertGetCertificateChain(NULL, cert, NULL, store, &chainPara,
chainFlags, NULL, &chain)))
{
if (chain->TrustStatus.dwErrorStatus)
{
static const DWORD supportedErrors = static const DWORD supportedErrors =
CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_VALID |
CERT_TRUST_IS_UNTRUSTED_ROOT | CERT_TRUST_IS_UNTRUSTED_ROOT |
...@@ -243,31 +232,62 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store, ...@@ -243,31 +232,62 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
CERT_TRUST_IS_REVOKED | CERT_TRUST_IS_REVOKED |
CERT_TRUST_IS_NOT_VALID_FOR_USAGE; CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
if (chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID && TRACE("verifying %s\n", debugstr_w(conn->server->name));
!(security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
chainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = server_auth;
if (!(conn->security_flags & SECURITY_FLAG_IGNORE_REVOCATION))
chainFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
if (!(ret = CertGetCertificateChain(NULL, cert, NULL, store, &chainPara, chainFlags, NULL, &chain))) {
TRACE("failed\n");
return GetLastError();
}
errors = chain->TrustStatus.dwErrorStatus;
if (chain->TrustStatus.dwErrorStatus & ~supportedErrors) {
WARN("CERT_TRUST_IS_NOT_TIME_VALID, unknown error flags\n");
err = ERROR_INTERNET_SEC_INVALID_CERT;
errors &= supportedErrors;
}
if(errors & CERT_TRUST_IS_NOT_TIME_VALID) {
WARN("CERT_TRUST_IS_NOT_TIME_VALID, unknown error flags\n");
if(!(conn->security_flags & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
err = ERROR_INTERNET_SEC_CERT_DATE_INVALID; err = ERROR_INTERNET_SEC_CERT_DATE_INVALID;
else if (chain->TrustStatus.dwErrorStatus & errors &= ~CERT_TRUST_IS_NOT_TIME_VALID;
(CERT_TRUST_IS_UNTRUSTED_ROOT | CERT_TRUST_IS_PARTIAL_CHAIN) && }
!(security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
if(errors & (CERT_TRUST_IS_UNTRUSTED_ROOT | CERT_TRUST_IS_PARTIAL_CHAIN)) {
conn->security_flags |= _SECURITY_FLAG_CERT_INVALID_CA;
if(!(conn->security_flags & SECURITY_FLAG_IGNORE_UNKNOWN_CA))
err = ERROR_INTERNET_INVALID_CA; err = ERROR_INTERNET_INVALID_CA;
else if (!(security_flags & SECURITY_FLAG_IGNORE_REVOCATION) && errors &= ~(CERT_TRUST_IS_UNTRUSTED_ROOT | CERT_TRUST_IS_PARTIAL_CHAIN);
((chain->TrustStatus.dwErrorStatus & }
CERT_TRUST_IS_OFFLINE_REVOCATION) ||
(chain->TrustStatus.dwErrorStatus & if(errors & (CERT_TRUST_IS_OFFLINE_REVOCATION | CERT_TRUST_REVOCATION_STATUS_UNKNOWN)) {
CERT_TRUST_REVOCATION_STATUS_UNKNOWN))) WARN("TRUST_IS_OFFLINE_REVOCATION | CERT_TRUST_REVOCATION_STATUS_UNKNOWN, unknown error flags\n");
if(!(conn->security_flags & SECURITY_FLAG_IGNORE_REVOCATION))
err = ERROR_INTERNET_SEC_CERT_NO_REV; err = ERROR_INTERNET_SEC_CERT_NO_REV;
else if (!(security_flags & SECURITY_FLAG_IGNORE_REVOCATION) && errors &= ~(CERT_TRUST_IS_OFFLINE_REVOCATION | CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
(chain->TrustStatus.dwErrorStatus & CERT_TRUST_IS_REVOKED)) }
if(errors & CERT_TRUST_IS_REVOKED) {
WARN("TRUST_IS_OFFLINE_REVOCATION | CERT_TRUST_REVOCATION_STATUS_UNKNOWN, unknown error flags\n");
if(!(conn->security_flags & SECURITY_FLAG_IGNORE_REVOCATION))
err = ERROR_INTERNET_SEC_CERT_REVOKED; err = ERROR_INTERNET_SEC_CERT_REVOKED;
else if (!(security_flags & SECURITY_FLAG_IGNORE_WRONG_USAGE) && errors &= ~CERT_TRUST_IS_REVOKED;
(chain->TrustStatus.dwErrorStatus & }
CERT_TRUST_IS_NOT_VALID_FOR_USAGE))
err = ERROR_INTERNET_SEC_INVALID_CERT; if(errors & CERT_TRUST_IS_NOT_VALID_FOR_USAGE) {
else if (chain->TrustStatus.dwErrorStatus & ~supportedErrors) WARN("CERT_TRUST_IS_NOT_VALID_FOR_USAGE, unknown error flags\n");
if(!(conn->security_flags & SECURITY_FLAG_IGNORE_WRONG_USAGE))
err = ERROR_INTERNET_SEC_INVALID_CERT; err = ERROR_INTERNET_SEC_INVALID_CERT;
errors &= ~CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
} }
if (!err)
{ if(!err || conn->mask_errors) {
CERT_CHAIN_POLICY_PARA policyPara; CERT_CHAIN_POLICY_PARA policyPara;
SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara; SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslExtraPolicyPara;
CERT_CHAIN_POLICY_STATUS policyStatus; CERT_CHAIN_POLICY_STATUS policyStatus;
...@@ -281,8 +301,8 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store, ...@@ -281,8 +301,8 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
chainCopy.TrustStatus.dwErrorStatus = 0; chainCopy.TrustStatus.dwErrorStatus = 0;
sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara); sslExtraPolicyPara.u.cbSize = sizeof(sslExtraPolicyPara);
sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER; sslExtraPolicyPara.dwAuthType = AUTHTYPE_SERVER;
sslExtraPolicyPara.pwszServerName = server; sslExtraPolicyPara.pwszServerName = conn->server->name;
sslExtraPolicyPara.fdwChecks = security_flags; sslExtraPolicyPara.fdwChecks = conn->security_flags;
policyPara.cbSize = sizeof(policyPara); policyPara.cbSize = sizeof(policyPara);
policyPara.dwFlags = 0; policyPara.dwFlags = 0;
policyPara.pvExtraPolicyPara = &sslExtraPolicyPara; policyPara.pvExtraPolicyPara = &sslExtraPolicyPara;
...@@ -291,18 +311,30 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store, ...@@ -291,18 +311,30 @@ static DWORD netconn_verify_cert(PCCERT_CONTEXT cert, HCERTSTORE store,
/* Any error in the policy status indicates that the /* Any error in the policy status indicates that the
* policy couldn't be verified. * policy couldn't be verified.
*/ */
if (ret && policyStatus.dwError) if(ret) {
{ if(policyStatus.dwError == CERT_E_CN_NO_MATCH) {
if (policyStatus.dwError == CERT_E_CN_NO_MATCH) conn->security_flags |= _SECURITY_FLAG_CERT_INVALID_CN;
err = ERROR_INTERNET_SEC_CERT_CN_INVALID; err = ERROR_INTERNET_SEC_CERT_CN_INVALID;
else }else if(policyStatus.dwError) {
WARN("unknown error flags for policy status %x\n", policyStatus.dwError);
err = ERROR_INTERNET_SEC_INVALID_CERT; err = ERROR_INTERNET_SEC_INVALID_CERT;
} }
}else {
err = GetLastError();
} }
CertFreeCertificateChain(chain);
} }
TRACE("returning %08x\n", err);
CertFreeCertificateChain(chain);
if(err) {
WARN("failed %u\n", err);
conn->server->security_flags |= conn->security_flags & _SECURITY_ERROR_FLAGS_MASK;
if(conn->mask_errors)
return err == ERROR_INTERNET_INVALID_CA ? ERROR_INTERNET_SEC_CERT_REV_FAILED : ERROR_INTERNET_SEC_CERT_ERRORS;
return err; return err;
}
return ERROR_SUCCESS;
} }
static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx) static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
...@@ -339,8 +371,7 @@ static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx) ...@@ -339,8 +371,7 @@ static int netconn_secure_verify(int preverify_ok, X509_STORE_CTX *ctx)
if (!endCert) ret = FALSE; if (!endCert) ret = FALSE;
if (ret) if (ret)
{ {
DWORD_PTR err = netconn_verify_cert(endCert, store, conn->server->name, DWORD_PTR err = netconn_verify_cert(conn, endCert, store);
conn->security_flags);
if (err) if (err)
{ {
...@@ -488,7 +519,7 @@ static DWORD init_openssl(void) ...@@ -488,7 +519,7 @@ static DWORD init_openssl(void)
#endif #endif
} }
DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, DWORD timeout, netconn_t **ret) DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, BOOL mask_errors, DWORD timeout, netconn_t **ret)
{ {
netconn_t *netconn; netconn_t *netconn;
int result, flag; int result, flag;
...@@ -512,6 +543,7 @@ DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, DWORD ...@@ -512,6 +543,7 @@ DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, DWORD
netconn->useSSL = useSSL; netconn->useSSL = useSSL;
netconn->socketFD = -1; netconn->socketFD = -1;
netconn->security_flags = security_flags | server->security_flags; netconn->security_flags = security_flags | server->security_flags;
netconn->mask_errors = mask_errors;
list_init(&netconn->pool_entry); list_init(&netconn->pool_entry);
assert(server->addr_len); assert(server->addr_len);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment