|
@@ -77,21 +77,156 @@ static int is_cr_or_lf(char c)
|
|
|
return c == '\r' || c == '\n';
|
|
|
}
|
|
|
|
|
|
-static CURLcode add_certs_to_store(HCERTSTORE trust_store,
|
|
|
- const char *ca_file,
|
|
|
- struct Curl_easy *data)
|
|
|
+/* Search the substring needle,needlelen into string haystack,haystacklen
|
|
|
+ * Strings don't need to be terminated by a '\0'.
|
|
|
+ * Similar of OSX/Linux memmem (not available on Visual Studio).
|
|
|
+ * Return position of beginning of first occurence or NULL if not found
|
|
|
+ */
|
|
|
+static const char *c_memmem(const void *haystack, size_t haystacklen,
|
|
|
+ const void *needle, size_t needlelen)
|
|
|
+{
|
|
|
+ const char *p;
|
|
|
+ char first;
|
|
|
+ const char *str_limit = (const char *)haystack + haystacklen;
|
|
|
+ if(!needlelen || needlelen > haystacklen)
|
|
|
+ return NULL;
|
|
|
+ first = *(const char *)needle;
|
|
|
+ for(p = (const char *)haystack; p <= (str_limit - needlelen); p++)
|
|
|
+ if(((*p) == first) && (memcmp(p, needle, needlelen) == 0))
|
|
|
+ return p;
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
|
|
|
+ const char *ca_buffer,
|
|
|
+ size_t ca_buffer_size,
|
|
|
+ const char *ca_file_text,
|
|
|
+ struct Curl_easy *data)
|
|
|
+{
|
|
|
+ const size_t begin_cert_len = strlen(BEGIN_CERT);
|
|
|
+ const size_t end_cert_len = strlen(END_CERT);
|
|
|
+ CURLcode result = CURLE_OK;
|
|
|
+ int num_certs = 0;
|
|
|
+ bool more_certs = 1;
|
|
|
+ const char *current_ca_file_ptr = ca_buffer;
|
|
|
+ const char *ca_buffer_limit = ca_buffer + ca_buffer_size;
|
|
|
+
|
|
|
+ while(more_certs && (current_ca_file_ptr<ca_buffer_limit)) {
|
|
|
+ const char *begin_cert_ptr = c_memmem(current_ca_file_ptr,
|
|
|
+ ca_buffer_limit-current_ca_file_ptr,
|
|
|
+ BEGIN_CERT,
|
|
|
+ begin_cert_len);
|
|
|
+ if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) {
|
|
|
+ more_certs = 0;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ const char *end_cert_ptr = c_memmem(begin_cert_ptr,
|
|
|
+ ca_buffer_limit-begin_cert_ptr,
|
|
|
+ END_CERT,
|
|
|
+ end_cert_len);
|
|
|
+ if(!end_cert_ptr) {
|
|
|
+ failf(data,
|
|
|
+ "schannel: CA file '%s' is not correctly formatted",
|
|
|
+ ca_file_text);
|
|
|
+ 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,
|
|
|
+ (const void **)&cert_context)) {
|
|
|
+ char buffer[STRERROR_LEN];
|
|
|
+ failf(data,
|
|
|
+ "schannel: failed to extract certificate from CA file "
|
|
|
+ "'%s': %s",
|
|
|
+ ca_file_text,
|
|
|
+ Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
|
|
|
+ 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_text);
|
|
|
+ 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) {
|
|
|
+ char buffer[STRERROR_LEN];
|
|
|
+ failf(data,
|
|
|
+ "schannel: failed to add certificate from CA file '%s' "
|
|
|
+ "to certificate store: %s",
|
|
|
+ ca_file_text,
|
|
|
+ Curl_winapi_strerror(GetLastError(), buffer,
|
|
|
+ sizeof(buffer)));
|
|
|
+ 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_text);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ infof(data,
|
|
|
+ "schannel: added %d certificate(s) from CA file '%s'\n",
|
|
|
+ num_certs, ca_file_text);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
|
|
|
+ const char *ca_file,
|
|
|
+ struct Curl_easy *data)
|
|
|
{
|
|
|
CURLcode result;
|
|
|
HANDLE ca_file_handle = INVALID_HANDLE_VALUE;
|
|
|
LARGE_INTEGER file_size;
|
|
|
char *ca_file_buffer = NULL;
|
|
|
- char *current_ca_file_ptr = NULL;
|
|
|
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 = curlx_convert_UTF8_to_tchar((char *)ca_file);
|
|
|
if(!ca_file_tstr) {
|
|
@@ -181,106 +316,10 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
|
|
|
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 || !is_cr_or_lf(begin_cert_ptr[strlen(BEGIN_CERT)])) {
|
|
|
- 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,
|
|
|
- (const void **)&cert_context)) {
|
|
|
- char buffer[STRERROR_LEN];
|
|
|
- failf(data,
|
|
|
- "schannel: failed to extract certificate from CA file "
|
|
|
- "'%s': %s",
|
|
|
- ca_file,
|
|
|
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
|
|
|
- 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) {
|
|
|
- char buffer[STRERROR_LEN];
|
|
|
- failf(data,
|
|
|
- "schannel: failed to add certificate from CA file '%s' "
|
|
|
- "to certificate store: %s",
|
|
|
- ca_file,
|
|
|
- Curl_winapi_strerror(GetLastError(), buffer,
|
|
|
- sizeof(buffer)));
|
|
|
- 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);
|
|
|
- }
|
|
|
- }
|
|
|
+ result = add_certs_data_to_store(trust_store,
|
|
|
+ ca_file_buffer, ca_file_bufsize,
|
|
|
+ ca_file,
|
|
|
+ data);
|
|
|
|
|
|
cleanup:
|
|
|
if(ca_file_handle != INVALID_HANDLE_VALUE) {
|
|
@@ -550,7 +589,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
|
|
|
result = CURLE_PEER_FAILED_VERIFICATION;
|
|
|
}
|
|
|
|
|
|
- if(result == CURLE_OK && SSL_CONN_CONFIG(CAfile) &&
|
|
|
+ if(result == CURLE_OK &&
|
|
|
+ (SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) &&
|
|
|
BACKEND->use_manual_cred_validation) {
|
|
|
/*
|
|
|
* Create a chain engine that uses the certificates in the CA file as
|
|
@@ -576,8 +616,19 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
|
|
}
|
|
|
else {
|
|
|
- result = add_certs_to_store(trust_store, SSL_CONN_CONFIG(CAfile),
|
|
|
- data);
|
|
|
+ const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
|
|
|
+ if(ca_info_blob) {
|
|
|
+ result = add_certs_data_to_store(trust_store,
|
|
|
+ (const char *)ca_info_blob->data,
|
|
|
+ ca_info_blob->len,
|
|
|
+ "(memory blob)",
|
|
|
+ data);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ result = add_certs_file_to_store(trust_store,
|
|
|
+ SSL_CONN_CONFIG(CAfile),
|
|
|
+ data);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|