gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnurl] 19/282: schannel: Make CURLOPT_CAINFO work better on Windows 7


From: gnunet
Subject: [gnurl] 19/282: schannel: Make CURLOPT_CAINFO work better on Windows 7
Date: Wed, 01 Apr 2020 14:28:04 +0200

This is an automated email from the git hooks/post-receive script.

ng0 pushed a commit to branch master
in repository gnurl.

commit 29e40a6d8a70630dd8eaa15beded205792342d08
Author: Faizur Rahman <address@hidden>
AuthorDate: Wed Dec 25 18:49:48 2019 -0500

    schannel: Make CURLOPT_CAINFO work better on Windows 7
    
    - Support hostname verification via alternative names (SAN) in the
      peer certificate when CURLOPT_CAINFO is used in Windows 7 and earlier.
    
    CERT_NAME_SEARCH_ALL_NAMES_FLAG doesn't exist before Windows 8. As a
    result CertGetNameString doesn't quite work on those versions of
    Windows. This change provides an alternative solution for
    CertGetNameString by iterating through CERT_ALT_NAME_INFO for earlier
    versions of Windows.
    
    Prior to this change many certificates failed the hostname validation
    when CURLOPT_CAINFO was used in Windows 7 and earlier. Most certificates
    now represent multiple hostnames and rely on the alternative names field
    exclusively to represent their hostnames.
    
    Reported-by: Jeroen Ooms
    
    Fixes https://github.com/curl/curl/issues/3711
    Closes https://github.com/curl/curl/pull/4761
---
 lib/vtls/schannel_verify.c | 150 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 130 insertions(+), 20 deletions(-)

diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c
index 3a668adc7..d7b72c210 100644
--- a/lib/vtls/schannel_verify.c
+++ b/lib/vtls/schannel_verify.c
@@ -293,6 +293,133 @@ cleanup:
   return result;
 }
 
+/*
+ * Returns the number of characters necessary to populate all the host_names.
+ * If host_names is not NULL, populate it with all the host names. Each string
+ * in the host_names is null-terminated and the last string is double
+ * null-terminated. If no DNS names are found, a single null-terminated empty
+ * string is returned.
+ */
+static DWORD cert_get_name_string(struct Curl_easy *data,
+                                  CERT_CONTEXT *cert_context,
+                                  LPTSTR host_names,
+                                  DWORD length)
+{
+  DWORD actual_length = 0;
+  BOOL compute_content = FALSE;
+  CERT_INFO *cert_info = NULL;
+  CERT_EXTENSION *extension = NULL;
+  CRYPT_DECODE_PARA decode_para = {0, 0, 0};
+  CERT_ALT_NAME_INFO *alt_name_info = NULL;
+  DWORD alt_name_info_size = 0;
+  BOOL ret_val = FALSE;
+  char *current_pos = NULL;
+  DWORD i;
+
+  /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */
+  if(Curl_verify_windows_version(6, 2, PLATFORM_WINNT,
+                                 VERSION_GREATER_THAN_EQUAL)) {
+#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
+    /* CertGetNameString will provide the 8-bit character string without
+     * any decoding */
+    DWORD name_flags =
+      CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG;
+    actual_length = CertGetNameString(cert_context,
+                                      CERT_NAME_DNS_TYPE,
+                                      name_flags,
+                                      NULL,
+                                      host_names,
+                                      length);
+    return actual_length;
+#endif
+  }
+
+  compute_content = host_names != NULL && length != 0;
+
+  /* Initialize default return values. */
+  actual_length = 1;
+  if(compute_content) {
+    *host_names = '\0';
+  }
+
+  if(!cert_context) {
+    failf(data, "schannel: Null certificate context.");
+    return actual_length;
+  }
+
+  cert_info = cert_context->pCertInfo;
+  if(!cert_info) {
+    failf(data, "schannel: Null certificate info.");
+    return actual_length;
+  }
+
+  extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
+                                cert_info->cExtension,
+                                cert_info->rgExtension);
+  if(!extension) {
+    failf(data, "schannel: CertFindExtension() returned no extension.");
+    return actual_length;
+  }
+
+  decode_para.cbSize  =  sizeof(CRYPT_DECODE_PARA);
+
+  ret_val =
+    CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+                        szOID_SUBJECT_ALT_NAME2,
+                        extension->Value.pbData,
+                        extension->Value.cbData,
+                        CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG,
+                        &decode_para,
+                        &alt_name_info,
+                        &alt_name_info_size);
+  if(!ret_val) {
+    failf(data,
+          "schannel: CryptDecodeObjectEx() returned no alternate name "
+          "information.");
+    return actual_length;
+  }
+
+  current_pos = host_names;
+
+  /* Iterate over the alternate names and populate host_names. */
+  for(i = 0; i < alt_name_info->cAltEntry; i++) {
+    const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i];
+    wchar_t *dns_w = NULL;
+    size_t current_length = 0;
+
+    if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) {
+      continue;
+    }
+    if(entry->pwszDNSName == NULL) {
+      infof(data, "schannel: Empty DNS name.");
+      continue;
+    }
+    current_length = wcslen(entry->pwszDNSName) + 1;
+    if(!compute_content) {
+      actual_length += (DWORD)current_length;
+      continue;
+    }
+    /* Sanity check to prevent buffer overrun. */
+    if((actual_length + current_length) > length) {
+      failf(data, "schannel: Not enough memory to list all host names.");
+      break;
+    }
+    dns_w = entry->pwszDNSName;
+    /* pwszDNSName is in ia5 string format and hence doesn't contain any
+     * non-ascii characters. */
+    while(*dns_w != '\0') {
+      *current_pos++ = (char)(*dns_w++);
+    }
+    *current_pos++ = '\0';
+    actual_length += (DWORD)current_length;
+  }
+  if(compute_content) {
+    /* Last string has double null-terminator. */
+    *current_pos = '\0';
+  }
+  return actual_length;
+}
+
 static CURLcode verify_host(struct Curl_easy *data,
                             CERT_CONTEXT *pCertContextServer,
                             const char * const conn_hostname)
@@ -303,21 +430,8 @@ static CURLcode verify_host(struct Curl_easy *data,
   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);
+  len = cert_get_name_string(data, pCertContextServer, NULL, 0);
   if(len == 0) {
     failf(data,
           "schannel: CertGetNameString() returned no "
@@ -334,12 +448,8 @@ static CURLcode verify_host(struct Curl_easy *data,
     result = CURLE_OUT_OF_MEMORY;
     goto cleanup;
   }
-  actual_len = CertGetNameString(pCertContextServer,
-                                 CERT_NAME_DNS_TYPE,
-                                 name_flags,
-                                 NULL,
-                                 (LPTSTR) cert_hostname_buff,
-                                 len);
+  actual_len = cert_get_name_string(
+    data, pCertContextServer, (LPTSTR)cert_hostname_buff, len);
 
   /* Sanity check */
   if(actual_len != len) {

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]