]> granicus.if.org Git - curl/commitdiff
schannel: add support for CURLOPT_CAINFO
authorDan McNulty <dkmcnulty@gmail.com>
Fri, 10 Mar 2017 20:27:30 +0000 (14:27 -0600)
committerJay Satiro <raysatiro@yahoo.com>
Wed, 18 Apr 2018 07:59:47 +0000 (03:59 -0400)
- Move verify_certificate functionality in schannel.c into a new
  file called schannel_verify.c. Additionally, some structure defintions
  from schannel.c have been moved to schannel.h to allow them to be
  used in schannel_verify.c.

- Make verify_certificate functionality for Schannel available on
  all versions of Windows instead of just Windows CE. verify_certificate
  will be invoked on Windows CE or when the user specifies
  CURLOPT_CAINFO and CURLOPT_SSL_VERIFYPEER.

- In verify_certificate, create a custom certificate chain engine that
  exclusively trusts the certificate store backed by the CURLOPT_CAINFO
  file.

- doc updates of --cacert/CAINFO support for schannel

- Use CERT_NAME_SEARCH_ALL_NAMES_FLAG when invoking CertGetNameString
  when available. This implements a TODO in schannel.c to improve
  handling of multiple SANs in a certificate. In particular, all SANs
  will now be searched instead of just the first name.

- Update tool_operate.c to not search for the curl-ca-bundle.crt file
  when using Schannel to maintain backward compatibility. Previously,
  any curl-ca-bundle.crt file found in that search would have been
  ignored by Schannel. But, with CAINFO support, the file found by
  that search would have been used as the certificate store and
  could cause issues for any users that have curl-ca-bundle.crt in
  the search path.

- Update url.c to not set the build time CURL_CA_BUNDLE if the selected
  SSL backend is Schannel. We allow setting CA location for schannel
  only when explicitly specified by the user via CURLOPT_CAINFO /
  --cacert.

- Add new test cases 3000 and 3001. These test cases check that the first
  and last SAN, respectively, matches the connection hostname. New test
  certificates have been added for these cases. For 3000, the certificate
  prefix is Server-localhost-firstSAN and for 3001, the certificate
  prefix is Server-localhost-secondSAN.

- Remove TODO 15.2 (Add support for custom server certificate
  validation), this commit addresses it.

Closes https://github.com/curl/curl/pull/1325

37 files changed:
docs/TODO
docs/cmdline-opts/cacert.d
docs/libcurl/opts/CURLOPT_CAINFO.3
lib/Makefile.inc
lib/hostcheck.c
lib/url.c
lib/vtls/schannel.c
lib/vtls/schannel.h
lib/vtls/schannel_verify.c [new file with mode: 0644]
src/tool_doswin.c
src/tool_doswin.h
src/tool_operate.c
tests/certs/Makefile.am
tests/certs/Server-localhost-firstSAN-sv.crl [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.crt [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.csr [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.der [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.dhp [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.key [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.pem [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.prm [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.pub.der [new file with mode: 0644]
tests/certs/Server-localhost-firstSAN-sv.pub.pem [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.crl [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.crt [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.csr [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.der [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.dhp [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.key [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.pem [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.prm [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.pub.der [new file with mode: 0644]
tests/certs/Server-localhost-lastSAN-sv.pub.pem [new file with mode: 0644]
tests/data/Makefile.inc
tests/data/test3000 [new file with mode: 0644]
tests/data/test3001 [new file with mode: 0644]
winbuild/MakefileBuild.vc

index 337620924027025840fe7564c4bfa4a1f515e1b3..b6b6dfe0d8ff4148929b5883dde33c515d9470d8 100644 (file)
--- a/docs/TODO
+++ b/docs/TODO
 
  15. WinSSL/SChannel
  15.1 Add support for client certificate authentication
- 15.2 Add support for custom server certificate validation
  15.3 Add support for the --ciphers option
 
  16. SASL
@@ -823,17 +822,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
  - Getting a Certificate for Schannel
    https://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
 
-15.2 Add support for custom server certificate validation
-
- WinSSL/SChannel currently makes use of the OS-level system and user
- certificate trust store. This does not allow the application or user to
- customize the server certificate validation process using curl or libcurl.
-
- Therefore support for the existing --cacert or --capath options should be
- implemented by supplying a custom certificate to the SChannel APIs, see:
- - Getting a Certificate for Schannel
-   https://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx
-
 15.3 Add support for the --ciphers option
 
  The cipher suites used by WinSSL/SChannel are configured on an OS-level
index b2ecf9088f83b3c376e117d159c330f067c81723..073ad3a9a9c7fca5b3fb9bd023800b505b80ffac 100644 (file)
@@ -25,4 +25,9 @@ should not be set. If the option is not set, then curl will use the
 certificates in the system and user Keychain to verify the peer, which is the
 preferred method of verifying the peer's certificate chain.
 
+(Schannel/WinSSL only) This option is supported for WinSSL in Windows 7 or
+later with libcurl 7.60 or later. This option is supported for backward
+compatibility with other SSL engines; instead it is recommended to use Windows'
+store of root certificates (the default for WinSSL).
+
 If this option is used several times, the last one will be used.
index bc094ef00c3965ab9eeb87aa1d0282eec8d2000a..4e7db0448adb9ba2ba454832f545040d31a17900 100644 (file)
@@ -52,6 +52,11 @@ should not be set. If the option is not set, then curl will use the
 certificates in the system and user Keychain to verify the peer, which is the
 preferred method of verifying the peer's certificate chain.
 
+(Schannel/WinSSL only) This option is supported for WinSSL in Windows 7 or
+later with libcurl 7.60 or later. This option is supported for backward
+compatibility with other SSL engines; instead it is recommended to use Windows'
+store of root certificates (the default for WinSSL).
+
 The application does not have to keep the string around after setting this
 option.
 .SH DEFAULT
index 69f9b403d336dbfc0d51a048ec35702df0249022..61c23411dfaa3fb7b018e61bb5657b5317f9707b 100644 (file)
@@ -29,8 +29,8 @@ LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h
 
 LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c     \
   vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c               \
-  vtls/cyassl.c vtls/schannel.c vtls/darwinssl.c vtls/gskit.c           \
-  vtls/mbedtls.c
+  vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c                  \
+  vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c
 
 LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h                \
   vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h   \
index 37bcc12c1b601083fd2562876fdde3334b808ae9..c9d8112d8671a9bd959a1cdf85b181d5d8965994 100644 (file)
@@ -25,7 +25,7 @@
 #if defined(USE_OPENSSL)                                \
   || defined(USE_AXTLS)                                 \
   || defined(USE_GSKIT)                                 \
-  || (defined(USE_SCHANNEL) && defined(_WIN32_WCE))
+  || defined(USE_SCHANNEL)
 /* these backends use functions from this file */
 
 #ifdef HAVE_NETINET_IN_H
index 8c9c6914182f2d18d6e26bd1dc748687ee96a776..2491fa2da0a12cf8e281eef8f2b64523385181ea 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -488,25 +488,33 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->socks5_gssapi_nec = FALSE;
 #endif
 
-  /* This is our preferred CA cert bundle/path since install time */
+  /* Set the default CA cert bundle/path detected/specified at build time.
+   *
+   * If Schannel (WinSSL) is the selected SSL backend then these locations
+   * are ignored. We allow setting CA location for schannel only when
+   * explicitly specified by the user via CURLOPT_CAINFO / --cacert.
+   */
+  if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) {
 #if defined(CURL_CA_BUNDLE)
-  result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_ORIG], CURL_CA_BUNDLE);
-  if(result)
-    return result;
+    result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_ORIG], CURL_CA_BUNDLE);
+    if(result)
+      return result;
 
-  result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], CURL_CA_BUNDLE);
-  if(result)
-    return result;
+    result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY],
+                            CURL_CA_BUNDLE);
+    if(result)
+      return result;
 #endif
 #if defined(CURL_CA_PATH)
-  result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_ORIG], CURL_CA_PATH);
-  if(result)
-    return result;
+    result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_ORIG], CURL_CA_PATH);
+    if(result)
+      return result;
 
-  result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH);
-  if(result)
-    return result;
+    result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH);
+    if(result)
+      return result;
 #endif
+  }
 
   set->wildcard_enabled = FALSE;
   set->chunk_bgn      = ZERO_NULL;
index 4f3f0ce85fbfafc7c3e104d2290e03450327b999..5387d8713ab056183bf58bca6439425675274962 100644 (file)
 
 #ifdef USE_SCHANNEL
 
+#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+
 #ifndef USE_WINDOWS_SSPI
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
-#include <schnlsp.h>
-#include <schannel.h>
-#include "curl_sspi.h"
 #include "schannel.h"
 #include "vtls.h"
 #include "sendf.h"
@@ -61,7 +60,6 @@
 #include "x509asn1.h"
 #include "curl_printf.h"
 #include "system_win32.h"
-#include "hostcheck.h"
 
  /* The last #include file should be: */
 #include "curl_memory.h"
 #  define CALG_SHA_256 0x0000800c
 #endif
 
-/* Structs to store Schannel handles */
-struct curl_schannel_cred {
-  CredHandle cred_handle;
-  TimeStamp time_stamp;
-  int refcount;
-};
-
-struct curl_schannel_ctxt {
-  CtxtHandle ctxt_handle;
-  TimeStamp time_stamp;
-};
-
-struct ssl_backend_data {
-  struct curl_schannel_cred *cred;
-  struct curl_schannel_ctxt *ctxt;
-  SecPkgContext_StreamSizes stream_sizes;
-  size_t encdata_length, decdata_length;
-  size_t encdata_offset, decdata_offset;
-  unsigned char *encdata_buffer, *decdata_buffer;
-  /* encdata_is_incomplete: if encdata contains only a partial record that
-     can't be decrypted without another Curl_read_plain (that is, status is
-     SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
-     more bytes into encdata then set this back to false. */
-  bool encdata_is_incomplete;
-  unsigned long req_flags, ret_flags;
-  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
-  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
-  bool recv_connection_closed; /* true if connection closed, regardless how */
-  bool use_alpn; /* true if ALPN is used for this connection */
-};
-
 #define BACKEND connssl->backend
 
 static Curl_recv schannel_recv;
@@ -181,10 +148,6 @@ static Curl_send schannel_send;
 static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
                                     const char *pinnedpubkey);
 
-#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)
 {
@@ -335,6 +298,26 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
   BACKEND->use_alpn = false;
 #endif
 
+#ifdef _WIN32_WCE
+  /* certificate validation on CE doesn't seem to work right; we'll
+   * do it following a more manual process. */
+  BACKEND->use_manual_cred_validation = true;
+#else
+  if(SSL_CONN_CONFIG(CAfile)) {
+    if(Curl_verify_windows_version(6, 1, PLATFORM_WINNT,
+                                   VERSION_GREATER_THAN_EQUAL)) {
+      BACKEND->use_manual_cred_validation = true;
+    }
+    else {
+      failf(data, "schannel: this version of Windows is too old to support "
+            "certificate verification via CA bundle file.");
+      return CURLE_SSL_CACERT_BADFILE;
+    }
+  }
+  else
+    BACKEND->use_manual_cred_validation = false;
+#endif
+
   BACKEND->cred = NULL;
 
   /* check for an existing re-usable credential handle */
@@ -358,26 +341,23 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
     schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
 
     if(conn->ssl_config.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;
+      if(BACKEND->use_manual_cred_validation)
+        schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
+      else
+        schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
+
       /* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */
-      if(data->set.ssl.no_revoke)
+      if(data->set.ssl.no_revoke) {
         schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
           SCH_CRED_IGNORE_REVOCATION_OFFLINE;
-      else
-        schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
-#endif
-      if(data->set.ssl.no_revoke)
+
         infof(data, "schannel: disabled server certificate revocation "
                     "checks\n");
-      else
+      }
+      else {
+        schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
         infof(data, "schannel: checking server certificate revocation\n");
+      }
     }
     else {
       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
@@ -898,12 +878,9 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
     }
   }
 
-#ifdef _WIN32_WCE
-  /* Windows CE doesn't do any server certificate validation.
-     We have to do it manually. */
-  if(conn->ssl_config.verifypeer)
+  if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) {
     return verify_certificate(conn, sockindex);
-#endif
+  }
 
   return CURLE_OK;
 }
@@ -1868,146 +1845,6 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
   return result;
 }
 
-#ifdef _WIN32_WCE
-static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
-{
-  SECURITY_STATUS status;
-  struct Curl_easy *data = conn->data;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  CURLcode result = CURLE_OK;
-  CERT_CONTEXT *pCertContextServer = NULL;
-  const CERT_CHAIN_CONTEXT *pChainContext = NULL;
-  const char * const conn_hostname = SSL_IS_PROXY() ?
-    conn->http_proxy.host.name :
-    conn->host.name;
-
-  status = s_pSecFn->QueryContextAttributes(&BACKEND->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,
-                                (data->set.ssl.no_revoke ? 0 :
-                                 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
-                                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 = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
-      dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
-      if(dwTrustErrorMask) {
-        if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
-          failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_REVOKED");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
-          failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_PARTIAL_CHAIN");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
-          failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_UNTRUSTED_ROOT");
-        else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
-          failf(data, "schannel: CertGetCertificateChain trust error"
-                " CERT_TRUST_IS_NOT_TIME_VALID");
-        else
-          failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
-                dwTrustErrorMask);
-        result = CURLE_PEER_FAILED_VERIFICATION;
-      }
-    }
-  }
-
-  if(result == CURLE_OK) {
-    if(conn->ssl_config.verifyhost) {
-      TCHAR cert_hostname_buff[256];
-      DWORD len;
-
-      /* TODO: Fix this for certificates with multiple alternative names.
-      Right now we're only asking for the first preferred alternative name.
-      Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
-      (if WinCE supports that?) and run this section in a loop for each.
-      https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
-      curl: (51) schannel: CertGetNameString() certificate hostname
-      (.google.com) did not match connection (google.com)
-      */
-      len = CertGetNameString(pCertContextServer,
-                              CERT_NAME_DNS_TYPE,
-                              CERT_NAME_DISABLE_IE4_UTF8_FLAG,
-                              NULL,
-                              cert_hostname_buff,
-                              256);
-      if(len > 0) {
-        const char *cert_hostname;
-
-        /* Comparing the cert name and the connection hostname encoded as UTF-8
-         * is acceptable since both values are assumed to use ASCII
-         * (or some equivalent) encoding
-         */
-        cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname_buff);
-        if(!cert_hostname) {
-          result = CURLE_OUT_OF_MEMORY;
-        }
-        else{
-          int match_result;
-
-          match_result = Curl_cert_hostcheck(cert_hostname, conn->host.name);
-          if(match_result == CURL_HOST_MATCH) {
-            infof(data,
-                  "schannel: connection hostname (%s) validated "
-                  "against certificate name (%s)\n",
-                  conn->host.name,
-                  cert_hostname);
-            result = CURLE_OK;
-          }
-          else{
-            failf(data,
-                  "schannel: connection hostname (%s) "
-                  "does not match certificate name (%s)",
-                  conn->host.name,
-                  cert_hostname);
-            result = CURLE_PEER_FAILED_VERIFICATION;
-          }
-          Curl_unicodefree(cert_hostname);
-        }
-      }
-      else {
-        failf(data,
-              "schannel: CertGetNameString did not provide any "
-              "certificate name information");
-        result = CURLE_PEER_FAILED_VERIFICATION;
-      }
-    }
-  }
-
-  if(pChainContext)
-    CertFreeCertificateChain(pChainContext);
-
-  if(pCertContextServer)
-    CertFreeCertificateContext(pCertContextServer);
-
-  return result;
-}
-#endif /* _WIN32_WCE */
-
 static void Curl_schannel_checksum(const unsigned char *input,
                       size_t inputlen,
                       unsigned char *checksum,
index 932103da4737fd31f344123a805246a46fa0e0a5..4476900270dfc87a191cd653e7760f0515796363 100644 (file)
 
 #ifdef USE_SCHANNEL
 
+#include <schnlsp.h>
+#include <schannel.h>
+#include "curl_sspi.h"
+
 #include "urldata.h"
 
 extern const struct Curl_ssl Curl_ssl_schannel;
 
+CURLcode verify_certificate(struct connectdata *conn, int sockindex);
+
+/* structs to expose only in schannel.c and schannel_verify.c */
+#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+struct curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+  int refcount;
+};
+
+struct curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+};
+
+struct ssl_backend_data {
+  struct curl_schannel_cred *cred;
+  struct curl_schannel_ctxt *ctxt;
+  SecPkgContext_StreamSizes stream_sizes;
+  size_t encdata_length, decdata_length;
+  size_t encdata_offset, decdata_offset;
+  unsigned char *encdata_buffer, *decdata_buffer;
+  /* encdata_is_incomplete: if encdata contains only a partial record that
+     can't be decrypted without another Curl_read_plain (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+     more bytes into encdata then set this back to false. */
+  bool encdata_is_incomplete;
+  unsigned long req_flags, ret_flags;
+  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+  bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool use_alpn; /* true if ALPN is used for this connection */
+  bool use_manual_cred_validation; /* true if manual cred validation is used */
+};
+#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
+
 #endif /* USE_SCHANNEL */
 #endif /* HEADER_CURL_SCHANNEL_H */
diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c
new file mode 100644 (file)
index 0000000..db187dd
--- /dev/null
@@ -0,0 +1,551 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Source file for SChannel-specific certificate verification. This code should
+ * only be invoked by code in schannel.c.
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_SCHANNEL
+
+#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+
+#ifndef USE_WINDOWS_SSPI
+#  error "Can't compile SCHANNEL support without SSPI."
+#endif
+
+#include "schannel.h"
+#include "vtls.h"
+#include "sendf.h"
+#include "strerror.h"
+#include "curl_multibyte.h"
+#include "curl_printf.h"
+#include "hostcheck.h"
+#include "system_win32.h"
+
+/* The last #include file should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define BACKEND connssl->backend
+
+#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
+#define BEGIN_CERT "-----BEGIN CERTIFICATE-----\n"
+#define END_CERT "\n-----END CERTIFICATE-----"
+
+typedef struct {
+  DWORD cbSize;
+  HCERTSTORE hRestrictedRoot;
+  HCERTSTORE hRestrictedTrust;
+  HCERTSTORE hRestrictedOther;
+  DWORD cAdditionalStore;
+  HCERTSTORE *rghAdditionalStore;
+  DWORD dwFlags;
+  DWORD dwUrlRetrievalTimeout;
+  DWORD MaximumCachedCertificates;
+  DWORD CycleDetectionModulus;
+  HCERTSTORE hExclusiveRoot;
+  HCERTSTORE hExclusiveTrustedPeople;
+} CERT_CHAIN_ENGINE_CONFIG_WIN7, *PCERT_CHAIN_ENGINE_CONFIG_WIN7;
+
+
+static CURLcode add_certs_to_store(HCERTSTORE trust_store,
+                                   const char *ca_file,
+                                   struct connectdata *conn)
+{
+  CURLcode result;
+  struct Curl_easy *data = conn->data;
+  HANDLE ca_file_handle = INVALID_HANDLE_VALUE;
+  LARGE_INTEGER file_size;
+  char *ca_file_buffer = NULL;
+  char *current_ca_file_ptr = NULL;
+  const TCHAR *ca_file_tstr = NULL;
+  size_t ca_file_bufsize = 0;
+  DWORD total_bytes_read = 0;
+  bool more_certs = 0;
+  int num_certs = 0;
+  size_t END_CERT_LEN;
+
+  ca_file_tstr = Curl_convert_UTF8_to_tchar(ca_file);
+  if(!ca_file_tstr) {
+    failf(data,
+          "schannel: invalid path name for CA file '%s': %s",
+          ca_file, Curl_strerror(conn, GetLastError()));
+    result = CURLE_SSL_CACERT_BADFILE;
+    goto cleanup;
+  }
+
+  /*
+   * Read the CA file completely into memory before parsing it. This
+   * optimizes for the common case where the CA file will be relatively
+   * small ( < 1 MiB ).
+   */
+  ca_file_handle = CreateFile(ca_file_tstr,
+                              GENERIC_READ,
+                              0,
+                              NULL,
+                              OPEN_EXISTING,
+                              FILE_ATTRIBUTE_NORMAL,
+                              NULL);
+  if(ca_file_handle == INVALID_HANDLE_VALUE) {
+    failf(data,
+          "schannel: failed to open CA file '%s': %s",
+          ca_file, Curl_strerror(conn, GetLastError()));
+    result = CURLE_SSL_CACERT_BADFILE;
+    goto cleanup;
+  }
+
+  if(!GetFileSizeEx(ca_file_handle, &file_size)) {
+    failf(data,
+          "schannel: failed to determine size of CA file '%s': %s",
+          ca_file, Curl_strerror(conn, GetLastError()));
+    result = CURLE_SSL_CACERT_BADFILE;
+    goto cleanup;
+  }
+
+  if(file_size.QuadPart > MAX_CAFILE_SIZE) {
+    failf(data,
+          "schannel: CA file exceeds max size of %u bytes",
+          MAX_CAFILE_SIZE);
+    result = CURLE_OUT_OF_MEMORY;
+    goto cleanup;
+  }
+
+  ca_file_bufsize = (size_t)file_size.QuadPart;
+  ca_file_buffer = (char *)malloc(ca_file_bufsize + 1);
+  if(!ca_file_buffer) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto cleanup;
+  }
+
+  result = CURLE_OK;
+  while(total_bytes_read < ca_file_bufsize) {
+    DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read);
+    DWORD bytes_read = 0;
+
+    if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
+                 bytes_to_read, &bytes_read, NULL)) {
+
+      failf(data,
+            "schannel: failed to read from CA file '%s': %s",
+            ca_file, Curl_strerror(conn, GetLastError()));
+      result = CURLE_SSL_CACERT_BADFILE;
+      goto cleanup;
+    }
+    if(bytes_read == 0) {
+      /* Premature EOF -- adjust the bufsize to the new value */
+      ca_file_bufsize = total_bytes_read;
+    }
+    else {
+      total_bytes_read += bytes_read;
+    }
+  }
+
+  /* Null terminate the buffer */
+  ca_file_buffer[ca_file_bufsize] = '\0';
+
+  if(result != CURLE_OK) {
+    goto cleanup;
+  }
+
+  END_CERT_LEN = strlen(END_CERT);
+
+  more_certs = 1;
+  current_ca_file_ptr = ca_file_buffer;
+  while(more_certs && *current_ca_file_ptr != '\0') {
+    char *begin_cert_ptr = strstr(current_ca_file_ptr, BEGIN_CERT);
+    if(!begin_cert_ptr) {
+      more_certs = 0;
+    }
+    else {
+      char *end_cert_ptr = strstr(begin_cert_ptr, END_CERT);
+      if(!end_cert_ptr) {
+        failf(data,
+              "schannel: CA file '%s' is not correctly formatted",
+              ca_file);
+        result = CURLE_SSL_CACERT_BADFILE;
+        more_certs = 0;
+      }
+      else {
+        CERT_BLOB cert_blob;
+        CERT_CONTEXT *cert_context = NULL;
+        BOOL add_cert_result = FALSE;
+        DWORD actual_content_type = 0;
+        DWORD cert_size = (DWORD)
+          ((end_cert_ptr + END_CERT_LEN) - begin_cert_ptr);
+
+        cert_blob.pbData = (BYTE *)begin_cert_ptr;
+        cert_blob.cbData = cert_size;
+        if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
+                             &cert_blob,
+                             CERT_QUERY_CONTENT_FLAG_CERT,
+                             CERT_QUERY_FORMAT_FLAG_ALL,
+                             0,
+                             NULL,
+                             &actual_content_type,
+                             NULL,
+                             NULL,
+                             NULL,
+                             &cert_context)) {
+
+          failf(data,
+                "schannel: failed to extract certificate from CA file "
+                "'%s': %s",
+                ca_file, Curl_strerror(conn, GetLastError()));
+          result = CURLE_SSL_CACERT_BADFILE;
+          more_certs = 0;
+        }
+        else {
+          current_ca_file_ptr = begin_cert_ptr + cert_size;
+
+          /* Sanity check that the cert_context object is the right type */
+          if(CERT_QUERY_CONTENT_CERT != actual_content_type) {
+            failf(data,
+                  "schannel: unexpected content type '%d' when extracting "
+                  "certificate from CA file '%s'",
+                  actual_content_type, ca_file);
+            result = CURLE_SSL_CACERT_BADFILE;
+            more_certs = 0;
+          }
+          else {
+            add_cert_result =
+              CertAddCertificateContextToStore(trust_store,
+                                               cert_context,
+                                               CERT_STORE_ADD_ALWAYS,
+                                               NULL);
+            CertFreeCertificateContext(cert_context);
+            if(!add_cert_result) {
+              failf(data,
+                    "schannel: failed to add certificate from CA file '%s'"
+                    "to certificate store: %s",
+                    ca_file, Curl_strerror(conn, GetLastError()));
+              result = CURLE_SSL_CACERT_BADFILE;
+              more_certs = 0;
+            }
+            else {
+              num_certs++;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if(result == CURLE_OK) {
+    if(!num_certs) {
+      infof(data,
+            "schannel: did not add any certificates from CA file '%s'\n",
+            ca_file);
+    }
+    else {
+      infof(data,
+            "schannel: added %d certificate(s) from CA file '%s'\n",
+            num_certs, ca_file);
+    }
+  }
+
+cleanup:
+  if(ca_file_handle != INVALID_HANDLE_VALUE) {
+    CloseHandle(ca_file_handle);
+  }
+  Curl_safefree(ca_file_buffer);
+  Curl_unicodefree(ca_file_tstr);
+
+  return result;
+}
+
+static CURLcode verify_host(struct Curl_easy *data,
+                            CERT_CONTEXT *pCertContextServer,
+                            const char * const conn_hostname)
+{
+  CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+  TCHAR *cert_hostname_buff = NULL;
+  size_t cert_hostname_buff_index = 0;
+  DWORD len = 0;
+  DWORD actual_len = 0;
+
+  /* CertGetNameString will provide the 8-bit character string without
+   * any decoding */
+  DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG;
+
+#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
+  name_flags |= CERT_NAME_SEARCH_ALL_NAMES_FLAG;
+#endif
+
+  /* Determine the size of the string needed for the cert hostname */
+  len = CertGetNameString(pCertContextServer,
+                          CERT_NAME_DNS_TYPE,
+                          name_flags,
+                          NULL,
+                          NULL,
+                          0);
+  if(len == 0) {
+    failf(data,
+          "schannel: CertGetNameString() returned no "
+          "certificate name information");
+    result = CURLE_PEER_FAILED_VERIFICATION;
+    goto cleanup;
+  }
+
+  /* CertGetNameString guarantees that the returned name will not contain
+   * embedded null bytes. This appears to be undocumented behavior.
+   */
+  cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR));
+  actual_len = CertGetNameString(pCertContextServer,
+                                 CERT_NAME_DNS_TYPE,
+                                 name_flags,
+                                 NULL,
+                                 (LPTSTR) cert_hostname_buff,
+                                 len);
+
+  /* Sanity check */
+  if(actual_len != len) {
+    failf(data,
+          "schannel: CertGetNameString() returned certificate "
+          "name information of unexpected size");
+    result = CURLE_PEER_FAILED_VERIFICATION;
+    goto cleanup;
+  }
+
+  /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output
+   * will contain all DNS names, where each name is null-terminated
+   * and the last DNS name is double null-terminated. Due to this
+   * encoding, use the length of the buffer to iterate over all names.
+   */
+  result = CURLE_PEER_FAILED_VERIFICATION;
+  while(cert_hostname_buff_index < len &&
+        cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') &&
+        result == CURLE_PEER_FAILED_VERIFICATION) {
+
+    char *cert_hostname;
+
+    /* Comparing the cert name and the connection hostname encoded as UTF-8
+     * is acceptable since both values are assumed to use ASCII
+     * (or some equivalent) encoding
+     */
+    cert_hostname = Curl_convert_tchar_to_UTF8(
+        &cert_hostname_buff[cert_hostname_buff_index]);
+    if(!cert_hostname) {
+      result = CURLE_OUT_OF_MEMORY;
+    }
+    else {
+      int match_result;
+
+      match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname);
+      if(match_result == CURL_HOST_MATCH) {
+        infof(data,
+              "schannel: connection hostname (%s) validated "
+              "against certificate name (%s)\n",
+              conn_hostname, cert_hostname);
+        result = CURLE_OK;
+      }
+      else {
+        size_t cert_hostname_len;
+
+        infof(data,
+              "schannel: connection hostname (%s) did not match "
+              "against certificate name (%s)\n",
+              conn_hostname, cert_hostname);
+
+        cert_hostname_len = _tcslen(
+            &cert_hostname_buff[cert_hostname_buff_index]);
+
+        /* Move on to next cert name */
+        cert_hostname_buff_index += cert_hostname_len + 1;
+
+        result = CURLE_PEER_FAILED_VERIFICATION;
+      }
+      Curl_unicodefree(cert_hostname);
+    }
+  }
+
+  if(result == CURLE_PEER_FAILED_VERIFICATION) {
+    failf(data,
+          "schannel: CertGetNameString() failed to match "
+          "connection hostname (%s) against server certificate names",
+          conn_hostname);
+  }
+  else if(result != CURLE_OK)
+    failf(data, "schannel: server certificate name verification failed");
+
+cleanup:
+  Curl_unicodefree(cert_hostname_buff);
+
+  return result;
+}
+
+CURLcode verify_certificate(struct connectdata *conn, int sockindex)
+{
+  SECURITY_STATUS status;
+  struct Curl_easy *data = conn->data;
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  CURLcode result = CURLE_OK;
+  CERT_CONTEXT *pCertContextServer = NULL;
+  const CERT_CHAIN_CONTEXT *pChainContext = NULL;
+  HCERTCHAINENGINE cert_chain_engine = NULL;
+  HCERTSTORE trust_store = NULL;
+  const char * const conn_hostname = SSL_IS_PROXY() ?
+    conn->http_proxy.host.name :
+    conn->host.name;
+
+  status = s_pSecFn->QueryContextAttributes(&BACKEND->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 && SSL_CONN_CONFIG(CAfile) &&
+      BACKEND->use_manual_cred_validation) {
+    /*
+     * Create a chain engine that uses the certificates in the CA file as
+     * trusted certificates. This is only supported on Windows 7+.
+     */
+
+    if(Curl_verify_windows_version(6, 1, PLATFORM_WINNT, VERSION_LESS_THAN)) {
+      failf(data, "schannel: this version of Windows is too old to support "
+            "certificate verification via CA bundle file.");
+      result = CURLE_SSL_CACERT_BADFILE;
+    }
+    else {
+      /* Open the certificate store */
+      trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY,
+                                  0,
+                                  (HCRYPTPROV)NULL,
+                                  CERT_STORE_CREATE_NEW_FLAG,
+                                  NULL);
+      if(!trust_store) {
+        failf(data, "schannel: failed to create certificate store: %s",
+              Curl_strerror(conn, GetLastError()));
+        result = CURLE_SSL_CACERT_BADFILE;
+      }
+      else {
+        result = add_certs_to_store(trust_store, SSL_CONN_CONFIG(CAfile),
+                                    conn);
+      }
+    }
+
+    if(result == CURLE_OK) {
+      CERT_CHAIN_ENGINE_CONFIG_WIN7 engine_config;
+      BOOL create_engine_result;
+
+      memset(&engine_config, 0, sizeof(engine_config));
+      engine_config.cbSize = sizeof(engine_config);
+      engine_config.hExclusiveRoot = trust_store;
+
+      /* CertCreateCertificateChainEngine will check the expected size of the
+       * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size
+       * does not match the expected size. When this occurs, it indicates that
+       * CAINFO is not supported on the version of Windows in use.
+       */
+      create_engine_result =
+        CertCreateCertificateChainEngine(
+          (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
+      if(!create_engine_result) {
+        failf(data,
+              "schannel: failed to create certificate chain engine: %s",
+              Curl_strerror(conn, GetLastError()));
+        result = CURLE_SSL_CACERT_BADFILE;
+      }
+    }
+  }
+
+  if(result == CURLE_OK) {
+    CERT_CHAIN_PARA ChainPara;
+
+    memset(&ChainPara, 0, sizeof(ChainPara));
+    ChainPara.cbSize = sizeof(ChainPara);
+
+    if(!CertGetCertificateChain(cert_chain_engine,
+                                pCertContextServer,
+                                NULL,
+                                pCertContextServer->hCertStore,
+                                &ChainPara,
+                                (data->set.ssl.no_revoke ? 0 :
+                                 CERT_CHAIN_REVOCATION_CHECK_CHAIN),
+                                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 = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
+      dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
+      if(dwTrustErrorMask) {
+        if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
+          failf(data, "schannel: CertGetCertificateChain trust error"
+                " CERT_TRUST_IS_REVOKED");
+        else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
+          failf(data, "schannel: CertGetCertificateChain trust error"
+                " CERT_TRUST_IS_PARTIAL_CHAIN");
+        else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
+          failf(data, "schannel: CertGetCertificateChain trust error"
+                " CERT_TRUST_IS_UNTRUSTED_ROOT");
+        else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
+          failf(data, "schannel: CertGetCertificateChain trust error"
+                " CERT_TRUST_IS_NOT_TIME_VALID");
+        else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN)
+          failf(data, "schannel: CertGetCertificateChain trust error"
+                " CERT_TRUST_REVOCATION_STATUS_UNKNOWN");
+        else
+          failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
+                dwTrustErrorMask);
+        result = CURLE_PEER_FAILED_VERIFICATION;
+      }
+    }
+  }
+
+  if(result == CURLE_OK) {
+    if(SSL_CONN_CONFIG(verifyhost)) {
+      result = verify_host(conn->data, pCertContextServer, conn_hostname);
+    }
+  }
+
+  if(cert_chain_engine) {
+    CertFreeCertificateChainEngine(cert_chain_engine);
+  }
+
+  if(trust_store) {
+    CertCloseStore(trust_store, 0);
+  }
+
+  if(pChainContext)
+    CertFreeCertificateChain(pChainContext);
+
+  if(pCertContextServer)
+    CertFreeCertificateContext(pCertContextServer);
+
+  return result;
+}
+
+#endif /* USE_SCHANNEL */
index 91299986afdfaaf3baf82939f2bc533589099a80..c3a8826ff51e8c3219effe3ed8a4cd616a81e71e 100644 (file)
@@ -638,12 +638,19 @@ char **__crt0_glob_function(char *arg)
  */
 
 CURLcode FindWin32CACert(struct OperationConfig *config,
+                         curl_sslbackend backend,
                          const char *bundle_file)
 {
   CURLcode result = CURLE_OK;
 
-  /* search and set cert file only if libcurl supports SSL */
-  if(curlinfo->features & CURL_VERSION_SSL) {
+  /* Search and set cert file only if libcurl supports SSL.
+   *
+   * If Schannel (WinSSL) is the selected SSL backend then these locations
+   * are ignored. We allow setting CA location for schannel only when
+   * explicitly specified by the user via CURLOPT_CAINFO / --cacert.
+   */
+  if((curlinfo->features & CURL_VERSION_SSL) &&
+     backend != CURLSSLBACKEND_SCHANNEL) {
 
     DWORD res_len;
     char buf[PATH_MAX];
index f649ef0235186a194ea9a5c02928b473eae37eab..289281f4f6181aaeebcf820a786b2344b26636a3 100644 (file)
@@ -58,6 +58,7 @@ char **__crt0_glob_function(char *arg);
 #ifdef WIN32
 
 CURLcode FindWin32CACert(struct OperationConfig *config,
+                         curl_sslbackend backend,
                          const char *bundle_file);
 
 #endif /* WIN32 */
index 8eac65d6a3e823c512738d9ebe355b3412f29919..932dda6e09255add7e56803db03bcdc02de941a3 100644 (file)
@@ -228,52 +228,76 @@ static CURLcode operate_do(struct GlobalConfig *global,
   if(!config->cacert &&
      !config->capath &&
      !config->insecure_ok) {
-    char *env;
-    env = curlx_getenv("CURL_CA_BUNDLE");
-    if(env) {
-      config->cacert = strdup(env);
-      if(!config->cacert) {
-        curl_free(env);
-        helpf(global->errors, "out of memory\n");
-        result = CURLE_OUT_OF_MEMORY;
-        goto quit_curl;
-      }
+    struct curl_tlssessioninfo *tls_backend_info = NULL;
+
+    /* With the addition of CAINFO support for Schannel, this search could find
+     * a certificate bundle that was previously ignored. To maintain backward
+     * compatibility, only perform this search if not using Schannel.
+     */
+    result = curl_easy_getinfo(config->easy,
+                               CURLINFO_TLS_SSL_PTR,
+                               &tls_backend_info);
+    if(result) {
+      goto quit_curl;
     }
-    else {
-      env = curlx_getenv("SSL_CERT_DIR");
+
+    /* Set the CA cert locations specified in the environment. For Windows if
+     * no environment-specified filename is found then check for CA bundle
+     * default filename curl-ca-bundle.crt in the user's PATH.
+     *
+     * If Schannel (WinSSL) is the selected SSL backend then these locations
+     * are ignored. We allow setting CA location for schannel only when
+     * explicitly specified by the user via CURLOPT_CAINFO / --cacert.
+     */
+    if(tls_backend_info->backend != CURLSSLBACKEND_SCHANNEL) {
+      char *env;
+      env = curlx_getenv("CURL_CA_BUNDLE");
       if(env) {
-        config->capath = strdup(env);
-        if(!config->capath) {
+        config->cacert = strdup(env);
+        if(!config->cacert) {
           curl_free(env);
           helpf(global->errors, "out of memory\n");
           result = CURLE_OUT_OF_MEMORY;
           goto quit_curl;
         }
-        capath_from_env = true;
       }
       else {
-        env = curlx_getenv("SSL_CERT_FILE");
+        env = curlx_getenv("SSL_CERT_DIR");
         if(env) {
-          config->cacert = strdup(env);
-          if(!config->cacert) {
+          config->capath = strdup(env);
+          if(!config->capath) {
             curl_free(env);
             helpf(global->errors, "out of memory\n");
             result = CURLE_OUT_OF_MEMORY;
             goto quit_curl;
           }
+          capath_from_env = true;
+        }
+        else {
+          env = curlx_getenv("SSL_CERT_FILE");
+          if(env) {
+            config->cacert = strdup(env);
+            if(!config->cacert) {
+              curl_free(env);
+              helpf(global->errors, "out of memory\n");
+              result = CURLE_OUT_OF_MEMORY;
+              goto quit_curl;
+            }
+          }
         }
       }
-    }
 
-    if(env)
-      curl_free(env);
+      if(env)
+        curl_free(env);
 #ifdef WIN32
-    else {
-      result = FindWin32CACert(config, "curl-ca-bundle.crt");
-      if(result)
-        goto quit_curl;
-    }
+      else {
+        result = FindWin32CACert(config, tls_backend_info->backend,
+                                 "curl-ca-bundle.crt");
+        if(result)
+          goto quit_curl;
+      }
 #endif
+    }
   }
 
   if(config->postfields) {
index 7e7f1fa3d7c0adba2605fe1c36e93be738bbafd8..6a1c22aa6930c2bef22c7bb809b37446514fecf8 100644 (file)
@@ -62,7 +62,25 @@ GENERATEDCERTS = \
   Server-localhost0h-sv.key \
   Server-localhost0h-sv.pem \
   Server-localhost0h-sv.pub.der \
-  Server-localhost0h-sv.pub.pem
+  Server-localhost0h-sv.pub.pem \
+  Server-localhost-firstSAN-sv.crl \
+  Server-localhost-firstSAN-sv.crt \
+  Server-localhost-firstSAN-sv.csr \
+  Server-localhost-firstSAN-sv.der \
+  Server-localhost-firstSAN-sv.dhp \
+  Server-localhost-firstSAN-sv.key \
+  Server-localhost-firstSAN-sv.pem \
+  Server-localhost-firstSAN-sv.pub.der \
+  Server-localhost-firstSAN-sv.pub.pem \
+  Server-localhost-lastSAN-sv.crl \
+  Server-localhost-lastSAN-sv.crt \
+  Server-localhost-lastSAN-sv.csr \
+  Server-localhost-lastSAN-sv.der \
+  Server-localhost-lastSAN-sv.dhp \
+  Server-localhost-lastSAN-sv.key \
+  Server-localhost-lastSAN-sv.pem \
+  Server-localhost-lastSAN-sv.pub.der \
+  Server-localhost-lastSAN-sv.pub.pem
 
 SRPFILES = \
   srp-verifier-conf \
@@ -76,7 +94,8 @@ clean-certs:
        cd $(srcdir); rm -f $(GENERATEDCERTS)
 
 build-certs: $(srcdir)/EdelCurlRoot-ca.cacert $(srcdir)/Server-localhost-sv.pem \
-       $(srcdir)/Server-localhost.nn-sv.pem $(srcdir)/Server-localhost0h-sv.pem
+       $(srcdir)/Server-localhost.nn-sv.pem $(srcdir)/Server-localhost0h-sv.pem \
+       $(srcdir)/Server-localhost-firstSAN-sv.pem $(srcdir)/Server-localhost-lastSAN-sv.pem
 
 $(srcdir)/EdelCurlRoot-ca.cacert:
        cd $(srcdir); scripts/genroot.sh EdelCurlRoot
@@ -89,3 +108,9 @@ $(srcdir)/Server-localhost.nn-sv.pem: $(srcdir)/EdelCurlRoot-ca.cacert
 
 $(srcdir)/Server-localhost0h-sv.pem: $(srcdir)/EdelCurlRoot-ca.cacert
        cd $(srcdir); scripts/genserv.sh Server-localhost0h EdelCurlRoot
+
+$(srcdir)/Server-localhost-firstSAN-sv.pem: $(srcdir)/EdelCurlRoot-ca.cacert
+       cd $(srcdir); scripts/genserv.sh Server-localhost-firstSAN EdelCurlRoot
+
+$(srcdir)/Server-localhost-lastSAN-sv.pem: $(srcdir)/EdelCurlRoot-ca.cacert
+       cd $(srcdir); scripts/genserv.sh Server-localhost-firstSAN EdelCurlRoot
diff --git a/tests/certs/Server-localhost-firstSAN-sv.crl b/tests/certs/Server-localhost-firstSAN-sv.crl
new file mode 100644 (file)
index 0000000..af0be0d
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN X509 CRL-----
+MIIB9TCB3gIBATANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJOTjExMC8GA1UE
+CgwoRWRlbCBDdXJsIEFyY3RpYyBJbGx1ZGl1bSBSZXNlYXJjaCBDbG91ZDEmMCQG
+A1UEAwwdTm9ydGhlcm4gTm93aGVyZSBUcnVzdCBBbmNob3IXDTE2MDgzMDE4MzIx
+NVoXDTE2MDkyOTE4MzIxNVowMjAXAgYNZJ8o86IXDTE2MDgzMDE4MzAxNVowFwIG
+DWSfO0UqFw0xNjA4MzAxODMyMTVaoA4wDDAKBgNVHRQEAwIBATANBgkqhkiG9w0B
+AQUFAAOCAQEAOUlXQDrOURgODds6feyO+87oPHkgTveOiTm8CtSVHObxwkPkHTIg
+pivd7iXccgEc8CstcGF9Pk5KLVJrXXxEKgGr69NZNGtHa8xXlYSIh+Vre0Pni3Px
+sUAMcsnvGt+cYw/5s/2Wy9u5UVzfJwdxjkxMMp9X648AqeSop229541zGV47M4ox
+h0wh2Mj/w/CFUKw0ijVgVWff5DhKXVaLPCXdh7hhgXcsYUZ4W3G/iOL/jd+Ji88o
+OmZvoP+MOco6or13rz178bGB1mS626z7EU/HNgP8sn25TyQwwopr9uW6H7VvRMaI
+6uwWvihKgoGCRVSVwYEfX+oOLadfJqdHdg==
+-----END X509 CRL-----
diff --git a/tests/certs/Server-localhost-firstSAN-sv.crt b/tests/certs/Server-localhost-firstSAN-sv.crt
new file mode 100644 (file)
index 0000000..5e37ef0
--- /dev/null
@@ -0,0 +1,80 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 14725819352362 (0xd649f3b452a)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = Northern Nowhere Trust Anchor
+        Validity
+            Not Before: Aug 30 18:32:15 2016 GMT
+            Not After : Nov 16 18:32:15 2024 GMT
+        Subject:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = localhost.nn
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c5:87:2e:fb:f5:88:8a:39:4c:62:88:9f:fb:4a:
+                    02:1c:27:92:9d:0b:65:a2:70:1f:d1:b7:de:c8:1d:
+                    87:28:4b:9c:4b:cc:f6:f6:7c:83:1f:2d:76:be:41:
+                    29:5e:31:fa:23:0c:2d:7d:cb:38:c2:8b:54:8f:fc:
+                    6a:50:6d:c7:d7:af:72:fb:3b:a1:a7:4d:c4:1b:d2:
+                    0d:75:7c:92:62:97:48:c4:e8:12:c0:00:33:66:0e:
+                    28:17:0f:5c:36:d6:50:70:ec:c8:9f:a2:ae:b9:eb:
+                    eb:19:05:f0:53:83:42:2a:ae:40:1f:fa:fb:7a:b7:
+                    86:4c:ab:6b:28:0b:2f:7f:81
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                DNS:localhost, DNS:localhost1, DNS:localhost2
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment, Key Agreement
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Key Identifier: 
+                2C:4D:DD:54:88:59:3F:A4:34:9C:E3:56:FF:95:0F:E2:CE:51:20:95
+            X509v3 Authority Key Identifier: 
+                keyid:12:CA:BA:4B:46:04:A7:75:8A:2C:E8:0E:54:94:BC:12:65:A6:7B:CE
+
+            X509v3 Basic Constraints: 
+                CA:FALSE
+    Signature Algorithm: sha1WithRSAEncryption
+         77:cd:d2:17:91:a6:4b:70:de:79:6a:20:82:a3:56:a3:d0:6a:
+         ba:f7:7d:6f:00:69:d2:06:06:0b:da:cd:49:9d:36:fd:d0:cc:
+         bd:8a:dc:e1:d6:89:c9:23:02:8a:19:2d:14:ca:c6:06:87:66:
+         c7:f4:32:37:95:0d:f1:a7:1c:a1:fe:43:4f:3b:03:03:e2:1a:
+         c6:fc:91:d5:0d:a0:7e:82:60:14:31:2f:6d:b8:f4:57:98:8d:
+         04:74:a3:82:28:6d:1c:b4:de:1a:70:bd:fe:73:ac:b7:96:ec:
+         7c:9b:6d:64:c6:f8:67:39:c7:ea:f4:aa:48:26:b8:14:85:f0:
+         00:ab:8f:bd:1a:95:e2:a7:63:92:35:1e:37:04:c2:70:2c:1c:
+         56:95:b1:83:70:8c:99:88:1c:8a:6f:7a:a2:0d:84:dd:4f:0e:
+         3e:8b:fb:31:cf:ae:ee:b0:e4:f6:c1:8d:d1:98:a9:8d:17:1f:
+         5d:5a:79:e8:7c:97:ab:40:bc:aa:7e:c4:0b:19:30:ad:18:aa:
+         9c:9b:eb:3f:35:d3:86:9c:3a:cc:e6:9a:2c:47:d1:bb:36:6e:
+         f2:c5:d4:e3:0c:5b:c6:eb:30:e6:0d:3a:4b:3a:a3:6b:62:93:
+         8b:6a:59:1f:48:66:2e:d9:70:14:b6:aa:4f:d1:3b:38:5e:e6:
+         79:7f:b7:00
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIGDWSfO0UqMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT
+Ak5OMTEwLwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNo
+IENsb3VkMSYwJAYDVQQDDB1Ob3J0aGVybiBOb3doZXJlIFRydXN0IEFuY2hvcjAe
+Fw0xNjA4MzAxODMyMTVaFw0yNDExMTYxODMyMTVaMFcxCzAJBgNVBAYTAk5OMTEw
+LwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNoIENsb3Vk
+MRUwEwYDVQQDDAxsb2NhbGhvc3Qubm4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAMWHLvv1iIo5TGKIn/tKAhwnkp0LZaJwH9G33sgdhyhLnEvM9vZ8gx8tdr5B
+KV4x+iMMLX3LOMKLVI/8alBtx9evcvs7oadNxBvSDXV8kmKXSMToEsAAM2YOKBcP
+XDbWUHDsyJ+irrnr6xkF8FODQiquQB/6+3q3hkyraygLL3+BAgMBAAGjgZ4wgZsw
+LAYDVR0RBCUwI4IJbG9jYWxob3N0ggpsb2NhbGhvc3Qxggpsb2NhbGhvc3QyMAsG
+A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQULE3dVIhZ
+P6Q0nONW/5UP4s5RIJUwHwYDVR0jBBgwFoAUEsq6S0YEp3WKLOgOVJS8EmWme84w
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAd83SF5GmS3DeeWoggqNWo9Bq
+uvd9bwBp0gYGC9rNSZ02/dDMvYrc4daJySMCihktFMrGBodmx/QyN5UN8accof5D
+TzsDA+IaxvyR1Q2gfoJgFDEvbbj0V5iNBHSjgihtHLTeGnC9/nOst5bsfJttZMb4
+ZznH6vSqSCa4FIXwAKuPvRqV4qdjkjUeNwTCcCwcVpWxg3CMmYgcim96og2E3U8O
+Pov7Mc+u7rDk9sGN0ZipjRcfXVp56HyXq0C8qn7ECxkwrRiqnJvrPzXThpw6zOaa
+LEfRuzZu8sXU4wxbxusw5g06Szqja2KTi2pZH0hmLtlwFLaqT9E7OF7meX+3AA==
+-----END CERTIFICATE-----
diff --git a/tests/certs/Server-localhost-firstSAN-sv.csr b/tests/certs/Server-localhost-firstSAN-sv.csr
new file mode 100644 (file)
index 0000000..729034e
--- /dev/null
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBlzCCAQACAQAwVzELMAkGA1UEBhMCTk4xMTAvBgNVBAoMKEVkZWwgQ3VybCBB
+cmN0aWMgSWxsdWRpdW0gUmVzZWFyY2ggQ2xvdWQxFTATBgNVBAMMDGxvY2FsaG9z
+dC5ubjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxYcu+/WIijlMYoif+0oC
+HCeSnQtlonAf0bfeyB2HKEucS8z29nyDHy12vkEpXjH6Iwwtfcs4wotUj/xqUG3H
+169y+zuhp03EG9INdXySYpdIxOgSwAAzZg4oFw9cNtZQcOzIn6KuuevrGQXwU4NC
+Kq5AH/r7ereGTKtrKAsvf4ECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBADlhAYRy
+2heMP/fGllGXW/uAEm2q6ubWhqgd3/5d+06LjTIs57qT/nwbr79e1PxholGWM+Zb
+/NGKZ4geZK0mJT/gnaJCyUsrjdp2KIEw9zmBoZyypJd/5Fhe1tzX0xwG40+3np9K
+orpp7wcILLeMho8+I4mNEzOJHidxy/9uia5U
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/certs/Server-localhost-firstSAN-sv.der b/tests/certs/Server-localhost-firstSAN-sv.der
new file mode 100644 (file)
index 0000000..c878bfb
Binary files /dev/null and b/tests/certs/Server-localhost-firstSAN-sv.der differ
diff --git a/tests/certs/Server-localhost-firstSAN-sv.dhp b/tests/certs/Server-localhost-firstSAN-sv.dhp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/certs/Server-localhost-firstSAN-sv.key b/tests/certs/Server-localhost-firstSAN-sv.key
new file mode 100644 (file)
index 0000000..4903266
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDFhy779YiKOUxiiJ/7SgIcJ5KdC2WicB/Rt97IHYcoS5xLzPb2
+fIMfLXa+QSleMfojDC19yzjCi1SP/GpQbcfXr3L7O6GnTcQb0g11fJJil0jE6BLA
+ADNmDigXD1w21lBw7Mifoq656+sZBfBTg0IqrkAf+vt6t4ZMq2soCy9/gQIDAQAB
+AoGAUjKXErJyR1LgvoAsUt3RUvYExOVhPd963kKtqojfHZ2ZRNHeU2QtDGRW7YUg
+OdqCRONkatyOmiZw4hogA6graJKiqKLvM/F4qRoLsxH9T/cSOIl9QjZVkUd1HV/Z
+iJluibPTewVyfoYzkq48GN+QIi//msYKBjI5Q2Yybn4WrgECQQDk/mDp4sAvuLXL
+NxaQKuDZA5TxU2u8GTItFqOoHneVFSJLE4O3kr7wh47O817mnljZfskZwVXBYx6R
+VbXsy8ZJAkEA3NLRFh8cR03CN+eYPi33JrUVRSrn8eAB5MNOaOdO4mT0pTAzfVfe
+g6rMDnK2n7WZzwf6YmvRVyppW2/kQjyPeQJAXoa3ILTuWoSn3owN71MT3+E/oWKr
+LUlFUiFvSx3QhSTlNBKJI8UatpVumPUTbqVczeMtRkltidfNrXaxE1+GqQJAW9WU
+vMVtZj3xUnyPNPS6vy85zE0ertmBEBklJ71icgaYM4aLM0pysIE8YZnVVzAX6iCg
+QYQjSEPMEwnCfMVgyQJAcWnk6HPLbJmUt+ZGGAcqzfycR6jMKFnm4st2Ld6JuDT2
+h2lb40Uma9gO+aXLIf+K9prCxb+7nR1M3qLwV4krkQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/certs/Server-localhost-firstSAN-sv.pem b/tests/certs/Server-localhost-firstSAN-sv.pem
new file mode 100644 (file)
index 0000000..2b5ada5
--- /dev/null
@@ -0,0 +1,120 @@
+extensions = x509v3
+[ x509v3 ]
+subjectAltName = DNS:localhost,DNS:localhost1,DNS:localhost2
+keyUsage       = keyEncipherment,digitalSignature,keyAgreement
+extendedKeyUsage = serverAuth
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid
+basicConstraints = CA:false
+[ req ]
+default_bits                    = 1024
+distinguished_name              = req_DN
+default_md                     = sha256
+string_mask                    = utf8only
+[ req_DN ]
+countryName                     = "Country Name is Northern Nowhere"
+countryName_value            = NN
+organizationName              = "Organization Name"
+organizationName_value     = Edel Curl Arctic Illudium Research Cloud
+commonName                      = "Common Name"
+commonName_value              = localhost.nn
+
+[something]
+# The key
+# the certificate
+# some dhparam
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDFhy779YiKOUxiiJ/7SgIcJ5KdC2WicB/Rt97IHYcoS5xLzPb2
+fIMfLXa+QSleMfojDC19yzjCi1SP/GpQbcfXr3L7O6GnTcQb0g11fJJil0jE6BLA
+ADNmDigXD1w21lBw7Mifoq656+sZBfBTg0IqrkAf+vt6t4ZMq2soCy9/gQIDAQAB
+AoGAUjKXErJyR1LgvoAsUt3RUvYExOVhPd963kKtqojfHZ2ZRNHeU2QtDGRW7YUg
+OdqCRONkatyOmiZw4hogA6graJKiqKLvM/F4qRoLsxH9T/cSOIl9QjZVkUd1HV/Z
+iJluibPTewVyfoYzkq48GN+QIi//msYKBjI5Q2Yybn4WrgECQQDk/mDp4sAvuLXL
+NxaQKuDZA5TxU2u8GTItFqOoHneVFSJLE4O3kr7wh47O817mnljZfskZwVXBYx6R
+VbXsy8ZJAkEA3NLRFh8cR03CN+eYPi33JrUVRSrn8eAB5MNOaOdO4mT0pTAzfVfe
+g6rMDnK2n7WZzwf6YmvRVyppW2/kQjyPeQJAXoa3ILTuWoSn3owN71MT3+E/oWKr
+LUlFUiFvSx3QhSTlNBKJI8UatpVumPUTbqVczeMtRkltidfNrXaxE1+GqQJAW9WU
+vMVtZj3xUnyPNPS6vy85zE0ertmBEBklJ71icgaYM4aLM0pysIE8YZnVVzAX6iCg
+QYQjSEPMEwnCfMVgyQJAcWnk6HPLbJmUt+ZGGAcqzfycR6jMKFnm4st2Ld6JuDT2
+h2lb40Uma9gO+aXLIf+K9prCxb+7nR1M3qLwV4krkQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 14725819352362 (0xd649f3b452a)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = Northern Nowhere Trust Anchor
+        Validity
+            Not Before: Aug 30 18:32:15 2016 GMT
+            Not After : Nov 16 18:32:15 2024 GMT
+        Subject:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = localhost.nn
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c5:87:2e:fb:f5:88:8a:39:4c:62:88:9f:fb:4a:
+                    02:1c:27:92:9d:0b:65:a2:70:1f:d1:b7:de:c8:1d:
+                    87:28:4b:9c:4b:cc:f6:f6:7c:83:1f:2d:76:be:41:
+                    29:5e:31:fa:23:0c:2d:7d:cb:38:c2:8b:54:8f:fc:
+                    6a:50:6d:c7:d7:af:72:fb:3b:a1:a7:4d:c4:1b:d2:
+                    0d:75:7c:92:62:97:48:c4:e8:12:c0:00:33:66:0e:
+                    28:17:0f:5c:36:d6:50:70:ec:c8:9f:a2:ae:b9:eb:
+                    eb:19:05:f0:53:83:42:2a:ae:40:1f:fa:fb:7a:b7:
+                    86:4c:ab:6b:28:0b:2f:7f:81
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                DNS:localhost, DNS:localhost1, DNS:localhost2
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment, Key Agreement
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Key Identifier: 
+                2C:4D:DD:54:88:59:3F:A4:34:9C:E3:56:FF:95:0F:E2:CE:51:20:95
+            X509v3 Authority Key Identifier: 
+                keyid:12:CA:BA:4B:46:04:A7:75:8A:2C:E8:0E:54:94:BC:12:65:A6:7B:CE
+
+            X509v3 Basic Constraints: 
+                CA:FALSE
+    Signature Algorithm: sha1WithRSAEncryption
+         77:cd:d2:17:91:a6:4b:70:de:79:6a:20:82:a3:56:a3:d0:6a:
+         ba:f7:7d:6f:00:69:d2:06:06:0b:da:cd:49:9d:36:fd:d0:cc:
+         bd:8a:dc:e1:d6:89:c9:23:02:8a:19:2d:14:ca:c6:06:87:66:
+         c7:f4:32:37:95:0d:f1:a7:1c:a1:fe:43:4f:3b:03:03:e2:1a:
+         c6:fc:91:d5:0d:a0:7e:82:60:14:31:2f:6d:b8:f4:57:98:8d:
+         04:74:a3:82:28:6d:1c:b4:de:1a:70:bd:fe:73:ac:b7:96:ec:
+         7c:9b:6d:64:c6:f8:67:39:c7:ea:f4:aa:48:26:b8:14:85:f0:
+         00:ab:8f:bd:1a:95:e2:a7:63:92:35:1e:37:04:c2:70:2c:1c:
+         56:95:b1:83:70:8c:99:88:1c:8a:6f:7a:a2:0d:84:dd:4f:0e:
+         3e:8b:fb:31:cf:ae:ee:b0:e4:f6:c1:8d:d1:98:a9:8d:17:1f:
+         5d:5a:79:e8:7c:97:ab:40:bc:aa:7e:c4:0b:19:30:ad:18:aa:
+         9c:9b:eb:3f:35:d3:86:9c:3a:cc:e6:9a:2c:47:d1:bb:36:6e:
+         f2:c5:d4:e3:0c:5b:c6:eb:30:e6:0d:3a:4b:3a:a3:6b:62:93:
+         8b:6a:59:1f:48:66:2e:d9:70:14:b6:aa:4f:d1:3b:38:5e:e6:
+         79:7f:b7:00
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIGDWSfO0UqMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT
+Ak5OMTEwLwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNo
+IENsb3VkMSYwJAYDVQQDDB1Ob3J0aGVybiBOb3doZXJlIFRydXN0IEFuY2hvcjAe
+Fw0xNjA4MzAxODMyMTVaFw0yNDExMTYxODMyMTVaMFcxCzAJBgNVBAYTAk5OMTEw
+LwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNoIENsb3Vk
+MRUwEwYDVQQDDAxsb2NhbGhvc3Qubm4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAMWHLvv1iIo5TGKIn/tKAhwnkp0LZaJwH9G33sgdhyhLnEvM9vZ8gx8tdr5B
+KV4x+iMMLX3LOMKLVI/8alBtx9evcvs7oadNxBvSDXV8kmKXSMToEsAAM2YOKBcP
+XDbWUHDsyJ+irrnr6xkF8FODQiquQB/6+3q3hkyraygLL3+BAgMBAAGjgZ4wgZsw
+LAYDVR0RBCUwI4IJbG9jYWxob3N0ggpsb2NhbGhvc3Qxggpsb2NhbGhvc3QyMAsG
+A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQULE3dVIhZ
+P6Q0nONW/5UP4s5RIJUwHwYDVR0jBBgwFoAUEsq6S0YEp3WKLOgOVJS8EmWme84w
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAd83SF5GmS3DeeWoggqNWo9Bq
+uvd9bwBp0gYGC9rNSZ02/dDMvYrc4daJySMCihktFMrGBodmx/QyN5UN8accof5D
+TzsDA+IaxvyR1Q2gfoJgFDEvbbj0V5iNBHSjgihtHLTeGnC9/nOst5bsfJttZMb4
+ZznH6vSqSCa4FIXwAKuPvRqV4qdjkjUeNwTCcCwcVpWxg3CMmYgcim96og2E3U8O
+Pov7Mc+u7rDk9sGN0ZipjRcfXVp56HyXq0C8qn7ECxkwrRiqnJvrPzXThpw6zOaa
+LEfRuzZu8sXU4wxbxusw5g06Szqja2KTi2pZH0hmLtlwFLaqT9E7OF7meX+3AA==
+-----END CERTIFICATE-----
diff --git a/tests/certs/Server-localhost-firstSAN-sv.prm b/tests/certs/Server-localhost-firstSAN-sv.prm
new file mode 100644 (file)
index 0000000..f299a3c
--- /dev/null
@@ -0,0 +1,25 @@
+extensions = x509v3
+[ x509v3 ]
+subjectAltName = DNS:localhost,DNS:localhost1,DNS:localhost2
+keyUsage       = keyEncipherment,digitalSignature,keyAgreement
+extendedKeyUsage = serverAuth
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid
+basicConstraints = CA:false
+[ req ]
+default_bits                    = 1024
+distinguished_name              = req_DN
+default_md                     = sha256
+string_mask                    = utf8only
+[ req_DN ]
+countryName                     = "Country Name is Northern Nowhere"
+countryName_value            = NN
+organizationName              = "Organization Name"
+organizationName_value     = Edel Curl Arctic Illudium Research Cloud
+commonName                      = "Common Name"
+commonName_value              = localhost.nn
+
+[something]
+# The key
+# the certificate
+# some dhparam
diff --git a/tests/certs/Server-localhost-firstSAN-sv.pub.der b/tests/certs/Server-localhost-firstSAN-sv.pub.der
new file mode 100644 (file)
index 0000000..fb1b486
Binary files /dev/null and b/tests/certs/Server-localhost-firstSAN-sv.pub.der differ
diff --git a/tests/certs/Server-localhost-firstSAN-sv.pub.pem b/tests/certs/Server-localhost-firstSAN-sv.pub.pem
new file mode 100644 (file)
index 0000000..ef14594
--- /dev/null
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFhy779YiKOUxiiJ/7SgIcJ5Kd
+C2WicB/Rt97IHYcoS5xLzPb2fIMfLXa+QSleMfojDC19yzjCi1SP/GpQbcfXr3L7
+O6GnTcQb0g11fJJil0jE6BLAADNmDigXD1w21lBw7Mifoq656+sZBfBTg0IqrkAf
++vt6t4ZMq2soCy9/gQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.crl b/tests/certs/Server-localhost-lastSAN-sv.crl
new file mode 100644 (file)
index 0000000..486bf92
--- /dev/null
@@ -0,0 +1,14 @@
+-----BEGIN X509 CRL-----
+MIICDjCB9wIBATANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJOTjExMC8GA1UE
+CgwoRWRlbCBDdXJsIEFyY3RpYyBJbGx1ZGl1bSBSZXNlYXJjaCBDbG91ZDEmMCQG
+A1UEAwwdTm9ydGhlcm4gTm93aGVyZSBUcnVzdCBBbmNob3IXDTE2MDgzMDE4MzI1
+N1oXDTE2MDkyOTE4MzI1N1owSzAXAgYNZJ8o86IXDTE2MDgzMDE4MzAxNVowFwIG
+DWSfO0UqFw0xNjA4MzAxODMyMTVaMBcCBg1kn0GuixcNMTYwODMwMTgzMjU3WqAO
+MAwwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQEFBQADggEBAE0v3zGBeKKuODAUugh7
+l6bJi7Cs0CDhuBJ8wCpwL5XRGwWhYChJtEXWFUKLBhMarIPYKEv3f3rd8gtFII/8
+wmnxoTL6eXZNL+FpHkZJ+blsHi73G7xzpB6kdFHIxI4tixwiUCe85u6WIRWkIEBs
+kyPPAgbnosF37umQfEaBrweVy+EztrYw8jgd0oDBybQp25p7Noa0N4uDoDInuOgP
+A7Q1Zf0ndWjrlEQtjMAUdA6blGKlb8BjMPtX50mbXuXctLeICns8TVUSQSiKU+oR
+1QTgbkl+AfdaFlfNAum4a42bCLyeBQ/O31NydZbCE8o2q9PqPepAkL9dXhMLiK/a
+tjA=
+-----END X509 CRL-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.crt b/tests/certs/Server-localhost-lastSAN-sv.crt
new file mode 100644 (file)
index 0000000..a6d8ae9
--- /dev/null
@@ -0,0 +1,80 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 14725819772555 (0xd649f41ae8b)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = Northern Nowhere Trust Anchor
+        Validity
+            Not Before: Aug 30 18:32:57 2016 GMT
+            Not After : Nov 16 18:32:57 2024 GMT
+        Subject:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = localhost.nn
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a3:2a:75:d7:bf:75:41:40:be:42:b8:b9:00:28:
+                    f1:45:29:55:bc:36:ca:a6:b7:86:93:97:25:84:aa:
+                    c9:80:ac:41:d9:28:fb:b0:68:4b:5b:ee:bd:94:83:
+                    da:2b:f6:cc:cc:11:df:fb:48:e6:e9:d5:97:41:7f:
+                    9a:0d:b7:87:96:12:22:41:2a:7f:95:8a:14:d6:6c:
+                    4b:34:df:18:29:01:0d:b2:3c:4d:c8:c4:5e:87:fa:
+                    9f:aa:ee:a4:73:e9:bb:74:57:85:24:2a:51:e4:43:
+                    5c:4b:97:51:52:b9:82:6e:9c:ce:ae:0f:91:45:25:
+                    f9:b4:24:66:8e:47:1f:d7:d5
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                DNS:localhost1, DNS:localhost2, DNS:localhost
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment, Key Agreement
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Key Identifier: 
+                2C:CF:E3:6E:08:F9:CE:9B:98:3B:B3:17:7F:0C:9D:E4:5B:1B:76:8A
+            X509v3 Authority Key Identifier: 
+                keyid:12:CA:BA:4B:46:04:A7:75:8A:2C:E8:0E:54:94:BC:12:65:A6:7B:CE
+
+            X509v3 Basic Constraints: 
+                CA:FALSE
+    Signature Algorithm: sha1WithRSAEncryption
+         2e:3d:c1:a2:a7:e4:70:f8:a8:13:86:c3:af:22:1f:e9:e1:62:
+         f4:cf:16:66:a8:3b:70:f6:12:30:be:fe:8e:44:1b:71:b5:c1:
+         e0:4b:66:c4:5d:d4:d7:7d:49:43:4a:6d:22:1b:ce:3d:e3:14:
+         14:b3:6d:3a:93:39:0c:9b:2c:83:35:1d:7e:7c:29:29:3c:51:
+         6b:27:c3:5b:2d:f2:61:18:f8:c7:90:be:3b:68:3f:08:9b:ac:
+         68:01:d2:0c:ec:aa:5d:9e:78:b7:8b:84:04:01:b2:08:ef:df:
+         0c:f2:29:99:fe:61:d1:65:80:aa:ef:df:8e:28:55:a6:f9:88:
+         0c:01:bb:fc:1c:9e:9c:08:8d:c5:34:24:91:c1:ac:71:22:e1:
+         12:78:e0:45:d5:e2:39:c4:3c:16:09:80:d0:5b:bc:49:0a:4c:
+         a3:5b:e1:36:40:ed:26:6d:8d:a0:d3:4a:3c:86:93:2f:d4:0a:
+         3c:72:08:62:d7:66:d0:b3:05:c2:0f:1d:af:3c:65:67:f2:6c:
+         76:a5:9c:37:ac:c4:ac:96:b7:e4:c0:ef:a4:5b:28:1e:16:09:
+         15:f6:7b:bb:5d:a2:94:9a:df:52:7b:ae:c9:39:f4:18:9e:84:
+         57:6c:d3:6d:ae:35:38:8f:8f:9b:0d:df:77:69:ae:25:ec:ce:
+         d0:2b:bd:8d
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIGDWSfQa6LMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT
+Ak5OMTEwLwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNo
+IENsb3VkMSYwJAYDVQQDDB1Ob3J0aGVybiBOb3doZXJlIFRydXN0IEFuY2hvcjAe
+Fw0xNjA4MzAxODMyNTdaFw0yNDExMTYxODMyNTdaMFcxCzAJBgNVBAYTAk5OMTEw
+LwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNoIENsb3Vk
+MRUwEwYDVQQDDAxsb2NhbGhvc3Qubm4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAKMqdde/dUFAvkK4uQAo8UUpVbw2yqa3hpOXJYSqyYCsQdko+7BoS1vuvZSD
+2iv2zMwR3/tI5unVl0F/mg23h5YSIkEqf5WKFNZsSzTfGCkBDbI8TcjEXof6n6ru
+pHPpu3RXhSQqUeRDXEuXUVK5gm6czq4PkUUl+bQkZo5HH9fVAgMBAAGjgZ4wgZsw
+LAYDVR0RBCUwI4IKbG9jYWxob3N0MYIKbG9jYWxob3N0MoIJbG9jYWxob3N0MAsG
+A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQULM/jbgj5
+zpuYO7MXfwyd5FsbdoowHwYDVR0jBBgwFoAUEsq6S0YEp3WKLOgOVJS8EmWme84w
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEALj3BoqfkcPioE4bDryIf6eFi
+9M8WZqg7cPYSML7+jkQbcbXB4EtmxF3U131JQ0ptIhvOPeMUFLNtOpM5DJssgzUd
+fnwpKTxRayfDWy3yYRj4x5C+O2g/CJusaAHSDOyqXZ54t4uEBAGyCO/fDPIpmf5h
+0WWAqu/fjihVpvmIDAG7/ByenAiNxTQkkcGscSLhEnjgRdXiOcQ8FgmA0Fu8SQpM
+o1vhNkDtJm2NoNNKPIaTL9QKPHIIYtdm0LMFwg8drzxlZ/JsdqWcN6zErJa35MDv
+pFsoHhYJFfZ7u12ilJrfUnuuyTn0GJ6EV2zTba41OI+Pmw3fd2muJezO0Cu9jQ==
+-----END CERTIFICATE-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.csr b/tests/certs/Server-localhost-lastSAN-sv.csr
new file mode 100644 (file)
index 0000000..bf63555
--- /dev/null
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBlzCCAQACAQAwVzELMAkGA1UEBhMCTk4xMTAvBgNVBAoMKEVkZWwgQ3VybCBB
+cmN0aWMgSWxsdWRpdW0gUmVzZWFyY2ggQ2xvdWQxFTATBgNVBAMMDGxvY2FsaG9z
+dC5ubjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAoyp11791QUC+Qri5ACjx
+RSlVvDbKpreGk5clhKrJgKxB2Sj7sGhLW+69lIPaK/bMzBHf+0jm6dWXQX+aDbeH
+lhIiQSp/lYoU1mxLNN8YKQENsjxNyMReh/qfqu6kc+m7dFeFJCpR5ENcS5dRUrmC
+bpzOrg+RRSX5tCRmjkcf19UCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4GBACoIAlf0
+hSnZJOBBt7FS7iXlNRyKZAD881jhHFux+Gxq3gtbJsP57c+ALZ3MswjjUXW0Iq11
+IZLeZQGCAHYp4/GuTHbaq0qo1LjgpTqgQfwEB3BqNGs6yJiST+3risgawFbfqEDY
+LCm7rs/yyaOdMjwdwrUMciSv5KtlXZ1VThyt
+-----END CERTIFICATE REQUEST-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.der b/tests/certs/Server-localhost-lastSAN-sv.der
new file mode 100644 (file)
index 0000000..5ffa9ce
Binary files /dev/null and b/tests/certs/Server-localhost-lastSAN-sv.der differ
diff --git a/tests/certs/Server-localhost-lastSAN-sv.dhp b/tests/certs/Server-localhost-lastSAN-sv.dhp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/certs/Server-localhost-lastSAN-sv.key b/tests/certs/Server-localhost-lastSAN-sv.key
new file mode 100644 (file)
index 0000000..824ee6f
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCjKnXXv3VBQL5CuLkAKPFFKVW8Nsqmt4aTlyWEqsmArEHZKPuw
+aEtb7r2Ug9or9szMEd/7SObp1ZdBf5oNt4eWEiJBKn+VihTWbEs03xgpAQ2yPE3I
+xF6H+p+q7qRz6bt0V4UkKlHkQ1xLl1FSuYJunM6uD5FFJfm0JGaORx/X1QIDAQAB
+AoGAaC2QGDSSNRuVXxx6YnPBuJrvtsB1G4VKU6nJtq8lARb65CCassOkegow2UZm
+YnOtxw4SqGqfpOVPMe66+c8Yrd+6zimC7VorxmfhNxqOO34bxzztKKk8Q7c+odl3
++c4aVnFBk2hzuOW4PuJoFfFNQZWmh/XJdKK85X+bkryS/oECQQDTdzwYyDxvrPaw
+ZeR5oDleopk5W5QwmBAq4ehtie1oZfhzlNZzPOjnI9I71MRYdCwkesKHL2k6q7cT
+jA4sSmx5AkEAxYc6+o8l0/HE8HzypWe/ZfozaY3ccIFzmvcwQorbCvAxDtZ1DbFy
+VWLOgM/6gwDIUDF6ckaInaVmiVJl60Y3PQJAZFBOuO7cBJoHWDytuqiwLl1x1EzG
+KpsoKD+MU9I3RewBhUrYxEfjsCpFA8716YQKoK9/ckOiZouoyGQLISWY+QJAG5id
+AMxm+Ilafk62h61K7DBcZm7PUViEki3erC1CFPEhqXUEvXkBBDTdrNlholPFqI6B
+EN4R0BR/ksfUPV598QJAF8jl/8gz8pmAWmqw8tKbWdQeDgisyTHeYlPMxq4fUbLH
+mJk05csSX9CTg4eO7NRRwPxODKmPCd88sZZSOuTQmQ==
+-----END RSA PRIVATE KEY-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.pem b/tests/certs/Server-localhost-lastSAN-sv.pem
new file mode 100644 (file)
index 0000000..b563e0a
--- /dev/null
@@ -0,0 +1,120 @@
+extensions = x509v3
+[ x509v3 ]
+subjectAltName = DNS:localhost1,DNS:localhost2,DNS:localhost
+keyUsage       = keyEncipherment,digitalSignature,keyAgreement
+extendedKeyUsage = serverAuth
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid
+basicConstraints = CA:false
+[ req ]
+default_bits                    = 1024
+distinguished_name              = req_DN
+default_md                     = sha256
+string_mask                    = utf8only
+[ req_DN ]
+countryName                     = "Country Name is Northern Nowhere"
+countryName_value            = NN
+organizationName              = "Organization Name"
+organizationName_value     = Edel Curl Arctic Illudium Research Cloud
+commonName                      = "Common Name"
+commonName_value              = localhost.nn
+
+[something]
+# The key
+# the certificate
+# some dhparam
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCjKnXXv3VBQL5CuLkAKPFFKVW8Nsqmt4aTlyWEqsmArEHZKPuw
+aEtb7r2Ug9or9szMEd/7SObp1ZdBf5oNt4eWEiJBKn+VihTWbEs03xgpAQ2yPE3I
+xF6H+p+q7qRz6bt0V4UkKlHkQ1xLl1FSuYJunM6uD5FFJfm0JGaORx/X1QIDAQAB
+AoGAaC2QGDSSNRuVXxx6YnPBuJrvtsB1G4VKU6nJtq8lARb65CCassOkegow2UZm
+YnOtxw4SqGqfpOVPMe66+c8Yrd+6zimC7VorxmfhNxqOO34bxzztKKk8Q7c+odl3
++c4aVnFBk2hzuOW4PuJoFfFNQZWmh/XJdKK85X+bkryS/oECQQDTdzwYyDxvrPaw
+ZeR5oDleopk5W5QwmBAq4ehtie1oZfhzlNZzPOjnI9I71MRYdCwkesKHL2k6q7cT
+jA4sSmx5AkEAxYc6+o8l0/HE8HzypWe/ZfozaY3ccIFzmvcwQorbCvAxDtZ1DbFy
+VWLOgM/6gwDIUDF6ckaInaVmiVJl60Y3PQJAZFBOuO7cBJoHWDytuqiwLl1x1EzG
+KpsoKD+MU9I3RewBhUrYxEfjsCpFA8716YQKoK9/ckOiZouoyGQLISWY+QJAG5id
+AMxm+Ilafk62h61K7DBcZm7PUViEki3erC1CFPEhqXUEvXkBBDTdrNlholPFqI6B
+EN4R0BR/ksfUPV598QJAF8jl/8gz8pmAWmqw8tKbWdQeDgisyTHeYlPMxq4fUbLH
+mJk05csSX9CTg4eO7NRRwPxODKmPCd88sZZSOuTQmQ==
+-----END RSA PRIVATE KEY-----
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 14725819772555 (0xd649f41ae8b)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = Northern Nowhere Trust Anchor
+        Validity
+            Not Before: Aug 30 18:32:57 2016 GMT
+            Not After : Nov 16 18:32:57 2024 GMT
+        Subject:
+            countryName               = NN
+            organizationName          = Edel Curl Arctic Illudium Research Cloud
+            commonName                = localhost.nn
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a3:2a:75:d7:bf:75:41:40:be:42:b8:b9:00:28:
+                    f1:45:29:55:bc:36:ca:a6:b7:86:93:97:25:84:aa:
+                    c9:80:ac:41:d9:28:fb:b0:68:4b:5b:ee:bd:94:83:
+                    da:2b:f6:cc:cc:11:df:fb:48:e6:e9:d5:97:41:7f:
+                    9a:0d:b7:87:96:12:22:41:2a:7f:95:8a:14:d6:6c:
+                    4b:34:df:18:29:01:0d:b2:3c:4d:c8:c4:5e:87:fa:
+                    9f:aa:ee:a4:73:e9:bb:74:57:85:24:2a:51:e4:43:
+                    5c:4b:97:51:52:b9:82:6e:9c:ce:ae:0f:91:45:25:
+                    f9:b4:24:66:8e:47:1f:d7:d5
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Alternative Name: 
+                DNS:localhost1, DNS:localhost2, DNS:localhost
+            X509v3 Key Usage: 
+                Digital Signature, Key Encipherment, Key Agreement
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+            X509v3 Subject Key Identifier: 
+                2C:CF:E3:6E:08:F9:CE:9B:98:3B:B3:17:7F:0C:9D:E4:5B:1B:76:8A
+            X509v3 Authority Key Identifier: 
+                keyid:12:CA:BA:4B:46:04:A7:75:8A:2C:E8:0E:54:94:BC:12:65:A6:7B:CE
+
+            X509v3 Basic Constraints: 
+                CA:FALSE
+    Signature Algorithm: sha1WithRSAEncryption
+         2e:3d:c1:a2:a7:e4:70:f8:a8:13:86:c3:af:22:1f:e9:e1:62:
+         f4:cf:16:66:a8:3b:70:f6:12:30:be:fe:8e:44:1b:71:b5:c1:
+         e0:4b:66:c4:5d:d4:d7:7d:49:43:4a:6d:22:1b:ce:3d:e3:14:
+         14:b3:6d:3a:93:39:0c:9b:2c:83:35:1d:7e:7c:29:29:3c:51:
+         6b:27:c3:5b:2d:f2:61:18:f8:c7:90:be:3b:68:3f:08:9b:ac:
+         68:01:d2:0c:ec:aa:5d:9e:78:b7:8b:84:04:01:b2:08:ef:df:
+         0c:f2:29:99:fe:61:d1:65:80:aa:ef:df:8e:28:55:a6:f9:88:
+         0c:01:bb:fc:1c:9e:9c:08:8d:c5:34:24:91:c1:ac:71:22:e1:
+         12:78:e0:45:d5:e2:39:c4:3c:16:09:80:d0:5b:bc:49:0a:4c:
+         a3:5b:e1:36:40:ed:26:6d:8d:a0:d3:4a:3c:86:93:2f:d4:0a:
+         3c:72:08:62:d7:66:d0:b3:05:c2:0f:1d:af:3c:65:67:f2:6c:
+         76:a5:9c:37:ac:c4:ac:96:b7:e4:c0:ef:a4:5b:28:1e:16:09:
+         15:f6:7b:bb:5d:a2:94:9a:df:52:7b:ae:c9:39:f4:18:9e:84:
+         57:6c:d3:6d:ae:35:38:8f:8f:9b:0d:df:77:69:ae:25:ec:ce:
+         d0:2b:bd:8d
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIGDWSfQa6LMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYT
+Ak5OMTEwLwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNo
+IENsb3VkMSYwJAYDVQQDDB1Ob3J0aGVybiBOb3doZXJlIFRydXN0IEFuY2hvcjAe
+Fw0xNjA4MzAxODMyNTdaFw0yNDExMTYxODMyNTdaMFcxCzAJBgNVBAYTAk5OMTEw
+LwYDVQQKDChFZGVsIEN1cmwgQXJjdGljIElsbHVkaXVtIFJlc2VhcmNoIENsb3Vk
+MRUwEwYDVQQDDAxsb2NhbGhvc3Qubm4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+AoGBAKMqdde/dUFAvkK4uQAo8UUpVbw2yqa3hpOXJYSqyYCsQdko+7BoS1vuvZSD
+2iv2zMwR3/tI5unVl0F/mg23h5YSIkEqf5WKFNZsSzTfGCkBDbI8TcjEXof6n6ru
+pHPpu3RXhSQqUeRDXEuXUVK5gm6czq4PkUUl+bQkZo5HH9fVAgMBAAGjgZ4wgZsw
+LAYDVR0RBCUwI4IKbG9jYWxob3N0MYIKbG9jYWxob3N0MoIJbG9jYWxob3N0MAsG
+A1UdDwQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAdBgNVHQ4EFgQULM/jbgj5
+zpuYO7MXfwyd5FsbdoowHwYDVR0jBBgwFoAUEsq6S0YEp3WKLOgOVJS8EmWme84w
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEALj3BoqfkcPioE4bDryIf6eFi
+9M8WZqg7cPYSML7+jkQbcbXB4EtmxF3U131JQ0ptIhvOPeMUFLNtOpM5DJssgzUd
+fnwpKTxRayfDWy3yYRj4x5C+O2g/CJusaAHSDOyqXZ54t4uEBAGyCO/fDPIpmf5h
+0WWAqu/fjihVpvmIDAG7/ByenAiNxTQkkcGscSLhEnjgRdXiOcQ8FgmA0Fu8SQpM
+o1vhNkDtJm2NoNNKPIaTL9QKPHIIYtdm0LMFwg8drzxlZ/JsdqWcN6zErJa35MDv
+pFsoHhYJFfZ7u12ilJrfUnuuyTn0GJ6EV2zTba41OI+Pmw3fd2muJezO0Cu9jQ==
+-----END CERTIFICATE-----
diff --git a/tests/certs/Server-localhost-lastSAN-sv.prm b/tests/certs/Server-localhost-lastSAN-sv.prm
new file mode 100644 (file)
index 0000000..faefe68
--- /dev/null
@@ -0,0 +1,25 @@
+extensions = x509v3
+[ x509v3 ]
+subjectAltName = DNS:localhost1,DNS:localhost2,DNS:localhost
+keyUsage       = keyEncipherment,digitalSignature,keyAgreement
+extendedKeyUsage = serverAuth
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid
+basicConstraints = CA:false
+[ req ]
+default_bits                    = 1024
+distinguished_name              = req_DN
+default_md                     = sha256
+string_mask                    = utf8only
+[ req_DN ]
+countryName                     = "Country Name is Northern Nowhere"
+countryName_value            = NN
+organizationName              = "Organization Name"
+organizationName_value     = Edel Curl Arctic Illudium Research Cloud
+commonName                      = "Common Name"
+commonName_value              = localhost.nn
+
+[something]
+# The key
+# the certificate
+# some dhparam
diff --git a/tests/certs/Server-localhost-lastSAN-sv.pub.der b/tests/certs/Server-localhost-lastSAN-sv.pub.der
new file mode 100644 (file)
index 0000000..06fe6d0
Binary files /dev/null and b/tests/certs/Server-localhost-lastSAN-sv.pub.der differ
diff --git a/tests/certs/Server-localhost-lastSAN-sv.pub.pem b/tests/certs/Server-localhost-lastSAN-sv.pub.pem
new file mode 100644 (file)
index 0000000..a8e2dd4
--- /dev/null
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCjKnXXv3VBQL5CuLkAKPFFKVW8
+Nsqmt4aTlyWEqsmArEHZKPuwaEtb7r2Ug9or9szMEd/7SObp1ZdBf5oNt4eWEiJB
+Kn+VihTWbEs03xgpAQ2yPE3IxF6H+p+q7qRz6bt0V4UkKlHkQ1xLl1FSuYJunM6u
+D5FFJfm0JGaORx/X1QIDAQAB
+-----END PUBLIC KEY-----
index 2f57213183acea63e751a854ada43c71b3ba5f34..fbda6008f31d222ef39d242c07ba7dfae02301fd 100644 (file)
@@ -195,4 +195,6 @@ test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 \
 test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \
 test2064 test2065 test2066 test2067 test2068 test2069 \
 \
-test2070 test2071 test2072 test2073
+test2070 test2071 test2072 test2073 \
+\
+test3000 test3001
diff --git a/tests/data/test3000 b/tests/data/test3000
new file mode 100644 (file)
index 0000000..e62e531
--- /dev/null
@@ -0,0 +1,57 @@
+<testcase>
+<info>
+<keywords>
+HTTPS
+HTTP GET
+PEM certificate
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 7
+
+MooMoo
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+SSL
+</features>
+<server>
+https Server-localhost-firstSAN-sv.pem
+</server>
+<name>
+HTTPS GET to localhost, first subject alt name matches, CN does not match
+</name>
+<command>
+--cacert %SRCDIR/certs/EdelCurlRoot-ca.crt https://localhost:%HTTPSPORT/3000
+</command>
+# Ensure that we're running on localhost because we're checking the host name
+<precheck>
+perl -e "print 'Test requires default test server host' if ( '%HOSTIP' ne '127.0.0.1' );"
+</precheck>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /3000 HTTP/1.1\r
+Host: localhost:%HTTPSPORT\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test3001 b/tests/data/test3001
new file mode 100644 (file)
index 0000000..706c3f7
--- /dev/null
@@ -0,0 +1,57 @@
+<testcase>
+<info>
+<keywords>
+HTTPS
+HTTP GET
+PEM certificate
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 7
+
+MooMoo
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<features>
+SSL
+</features>
+<server>
+https Server-localhost-lastSAN-sv.pem
+</server>
+<name>
+HTTPS GET to localhost, last subject alt name matches, CN does not match
+</name>
+<command>
+--cacert %SRCDIR/certs/EdelCurlRoot-ca.crt https://localhost:%HTTPSPORT/3001
+</command>
+# Ensure that we're running on localhost because we're checking the host name
+<precheck>
+perl -e "print 'Test requires default test server host' if ( '%HOSTIP' ne '127.0.0.1' );"
+</precheck>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /3001 HTTP/1.1\r
+Host: localhost:%HTTPSPORT\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
+</testcase>
index 5f994cb30353c478bba4fc99ab233e967abdab1c..bf79db3760b22e02dbc188021f67d1eeddfa9612 100644 (file)
@@ -82,7 +82,7 @@ LFLAGS_PDB = /incremental:no /opt:ref,icf /DEBUG
 \r
 CFLAGS_LIBCURL_STATIC  = /DCURL_STATICLIB\r
 \r
-WIN_LIBS    = ws2_32.lib wldap32.lib advapi32.lib\r
+WIN_LIBS    = ws2_32.lib wldap32.lib advapi32.lib crypt32.lib\r
 \r
 BASE_NAME              = libcurl\r
 BASE_NAME_DEBUG        = $(BASE_NAME)_debug\r