aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Salisbury <mark.salisbury@hp.com>2012-06-19 04:15:02 +0200
committerYang Tse <yangsita@gmail.com>2012-06-19 04:39:45 +0200
commit1e4c57fa6471c0010f66d82a83f190a07fa37309 (patch)
tree9aaf2c71055c4f4b0e1aa0084ff21f9225550104
parent29dd7192e6115ebd592cba89602908160802e904 (diff)
schannel SSL: certificate validation on WinCE
curl_schannel.c - auto certificate validation doesn't seem to work right on CE. I added a method to perform the certificate validation which uses CertGetCertificateChain and manually handles the result.
-rw-r--r--lib/curl_schannel.c127
1 files changed, 127 insertions, 0 deletions
diff --git a/lib/curl_schannel.c b/lib/curl_schannel.c
index 5e2a0b3e3..3ea6b4d66 100644
--- a/lib/curl_schannel.c
+++ b/lib/curl_schannel.c
@@ -6,6 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
* Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
@@ -86,6 +87,10 @@
static Curl_recv schannel_recv;
static Curl_send schannel_send;
+#ifdef _WIN32_WCE
+static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
+#endif
+
static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
void *BufDataPtr, unsigned long BufByteSize)
{
@@ -133,8 +138,16 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
if(data->set.ssl.verifypeer) {
+#ifdef _WIN32_WCE
+ /* certificate validation on CE doesn't seem to work right; we'll
+ do it following a more manual process. */
+ schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+#else
schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
SCH_CRED_REVOCATION_CHECK_CHAIN;
+#endif
infof(data, "schannel: checking server certificate revocation\n");
}
else {
@@ -426,6 +439,13 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
infof(data, "schannel: handshake complete\n");
}
+#ifdef _WIN32_WCE
+ /* Windows CE doesn't do any server certificate validation.
+ We have to do it manually. */
+ if(data->set.ssl.verifypeer)
+ return verify_certificate(conn, sockindex);
+#endif
+
return CURLE_OK;
}
@@ -990,4 +1010,111 @@ size_t Curl_schannel_version(char *buffer, size_t size)
return size;
}
+#ifdef _WIN32_WCE
+static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
+{
+ SECURITY_STATUS status;
+ struct SessionHandle *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ CURLcode result = CURLE_OK;
+ CERT_CONTEXT *pCertContextServer = NULL;
+ CCERT_CHAIN_CONTEXT *pChainContext = NULL;
+
+ status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ failf(data, "schannel: Failed to read remote certificate context: %s",
+ Curl_sspi_strerror(conn, status));
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ if(result == CURLE_OK) {
+ CERT_CHAIN_PARA ChainPara;
+ memset(&ChainPara, 0, sizeof(ChainPara));
+ ChainPara.cbSize = sizeof(ChainPara);
+
+ if(!CertGetCertificateChain(NULL,
+ pCertContextServer,
+ NULL,
+ pCertContextServer->hCertStore,
+ &ChainPara,
+ 0,
+ NULL,
+ &pChainContext)) {
+ failf(data, "schannel: CertGetCertificateChain failed: %s",
+ Curl_sspi_strerror(conn, GetLastError()));
+ pChainContext = NULL;
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ if(result == CURLE_OK) {
+ CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
+ DWORD dwTrustErrorMask = ~(CERT_TRUST_IS_NOT_TIME_NESTED|
+ CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
+ dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
+ if(dwTrustErrorMask) {
+ if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_PARTIAL_CHAIN");
+ if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_UNTRUSTED_ROOT");
+ if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
+ failf(data, "schannel: CertGetCertificateChain trust error"
+ " CERT_TRUST_IS_NOT_TIME_VALID");
+ failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+ dwTrustErrorMask);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ }
+
+ if(result == CURLE_OK) {
+ if(data->set.ssl.verifyhost == 1) {
+ infof(data, "warning: ignoring unsupported value (1) ssl.verifyhost\n");
+ }
+ else if(data->set.ssl.verifyhost == 2) {
+ WCHAR cert_hostname[128];
+ WCHAR *hostname = Curl_convert_UTF8_to_wchar(conn->host.name);
+ DWORD len;
+
+ len = CertGetNameStringW(pCertContextServer,
+ CERT_NAME_DNS_TYPE,
+ 0,
+ NULL,
+ cert_hostname,
+ 128);
+ if(len > 0 && cert_hostname[0] == '*') {
+ /* this is a wildcard cert. try matching the last len - 1 chars */
+ int hostname_len = strlen(conn->host.name);
+ if(wcsicmp(cert_hostname + 1, hostname + hostname_len - len + 2) != 0)
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(len == 0 || wcsicmp(hostname, cert_hostname) != 0) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ if(result == CURLE_PEER_FAILED_VERIFICATION) {
+ const char *_cert_hostname;
+ _cert_hostname = Curl_convert_wchar_to_UTF8(cert_hostname);
+ failf(data, "schannel: CertGetNameString() certificate hostname "
+ "(%s) did not match connection (%s)",
+ _cert_hostname, conn->host.name);
+ free(_cert_hostname);
+ }
+ free(hostname);
+ }
+ }
+
+ if(pChainContext)
+ CertFreeCertificateChain(pChainContext);
+
+ if(pCertContextServer)
+ CertFreeCertificateContext(pCertContextServer);
+
+ return result;
+}
+#endif /* _WIN32_WCE */
+
#endif /* USE_SCHANNEL */