From 82d07c405168df59db17d298bc3b8476f13540e9 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Thu, 3 Dec 2009 11:28:40 -0800 Subject: [PATCH] winhttp: Check the certificate in an https connection using the crypto api. --- dlls/winhttp/net.c | 174 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index a86a5a62772..3ed3b1b5e01 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; -- 2.11.4.GIT