diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/curl_schannel.c | 127 | 
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 */ | 
