summaryrefslogtreecommitdiff
path: root/lib/vtls/schannel.c
diff options
context:
space:
mode:
authorGilles Vollant <info@winimage.com>2020-05-15 10:47:46 +0200
committerDaniel Stenberg <daniel@haxx.se>2020-05-15 13:03:59 +0200
commitcac5374298b3e79405bbdabe38941227c73a4c96 (patch)
treeb3548eba0d3ea4538765897a7ef01154805788e5 /lib/vtls/schannel.c
parent8df455479f8801bbebad8839fc96abbffa711603 (diff)
downloadcurl-cac5374298b3e79405bbdabe38941227c73a4c96.tar.gz
setopt: support certificate options in memory with struct curl_blob
This change introduces a generic way to provide binary data in setopt options, called BLOBs. This change introduces these new setopts: CURLOPT_ISSUERCERT_BLOB, CURLOPT_PROXY_SSLCERT_BLOB, CURLOPT_PROXY_SSLKEY_BLOB, CURLOPT_SSLCERT_BLOB and CURLOPT_SSLKEY_BLOB. Reviewed-by: Daniel Stenberg Closes #5357
Diffstat (limited to 'lib/vtls/schannel.c')
-rw-r--r--lib/vtls/schannel.c143
1 files changed, 86 insertions, 57 deletions
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 1305205dc..f1499786f 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -39,6 +39,7 @@
#include "schannel.h"
#include "vtls.h"
+#include "strcase.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
#include "strerror.h"
@@ -583,94 +584,122 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
#ifdef HAS_CLIENT_CERT_PATH
/* client certificate */
- if(data->set.ssl.cert) {
- DWORD cert_store_name;
+ if(data->set.ssl.cert || data->set.ssl.cert_blob) {
+ DWORD cert_store_name = 0;
TCHAR *cert_store_path = NULL;
- TCHAR *cert_thumbprint_str;
+ TCHAR *cert_thumbprint_str = NULL;
CRYPT_HASH_BLOB cert_thumbprint;
BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
- HCERTSTORE cert_store;
+ HCERTSTORE cert_store = NULL;
FILE *fInCert = NULL;
+ void *certdata = NULL;
+ size_t certsize = 0;
+ bool blob = data->set.ssl.cert_blob != NULL;
+ TCHAR *cert_path = NULL;
+ if(blob) {
+ certdata = data->set.ssl.cert_blob->data;
+ certsize = data->set.ssl.cert_blob->len;
+ }
+ else {
+ cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert);
+ if(!cert_path)
+ return CURLE_OUT_OF_MEMORY;
- TCHAR *cert_path = curlx_convert_UTF8_to_tchar(data->set.ssl.cert);
- if(!cert_path)
- return CURLE_OUT_OF_MEMORY;
+ result = get_cert_location(cert_path, &cert_store_name,
+ &cert_store_path, &cert_thumbprint_str);
+
+ if(result && (data->set.ssl.cert[0]!='\0'))
+ fInCert = fopen(data->set.ssl.cert, "rb");
- result = get_cert_location(cert_path, &cert_store_name,
- &cert_store_path, &cert_thumbprint_str);
- if((result != CURLE_OK) && (data->set.ssl.cert[0]!='\0'))
- fInCert = fopen(data->set.ssl.cert, "rb");
+ if(result && !fInCert) {
+ failf(data, "schannel: Failed to get certificate location"
+ " or file for %s",
+ data->set.ssl.cert);
+ curlx_unicodefree(cert_path);
+ return result;
+ }
+ }
- if((result != CURLE_OK) && (fInCert == NULL)) {
- failf(data, "schannel: Failed to get certificate location"
- " or file for %s",
- data->set.ssl.cert);
+ if((fInCert || blob) && (data->set.ssl.cert_type) &&
+ (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
+ failf(data, "schannel: certificate format compatibility error "
+ " for %s",
+ blob ? "(memory blob)" : data->set.ssl.cert);
curlx_unicodefree(cert_path);
- return result;
+ return CURLE_SSL_CERTPROBLEM;
}
- if(fInCert) {
+ if(fInCert || blob) {
/* Reading a .P12 or .pfx file, like the example at bottom of
- https://social.msdn.microsoft.com/Forums/windowsdesktop/
- en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
+ https://social.msdn.microsoft.com/Forums/windowsdesktop/
+ en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
*/
- void *certdata = NULL;
- long filesize = 0;
CRYPT_DATA_BLOB datablob;
WCHAR* pszPassword;
size_t pwd_len = 0;
int str_w_len = 0;
- int continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
- if(continue_reading)
- filesize = ftell(fInCert);
- if(filesize < 0)
- continue_reading = 0;
- if(continue_reading)
- continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
- if(continue_reading)
- certdata = malloc(((size_t)filesize) + 1);
- if((certdata == NULL) ||
- ((int) fread(certdata, (size_t)filesize, 1, fInCert) != 1))
- continue_reading = 0;
- fclose(fInCert);
+ const char *cert_showfilename_error = blob ?
+ "(memory blob)" : data->set.ssl.cert;
curlx_unicodefree(cert_path);
-
- if(!continue_reading) {
- failf(data, "schannel: Failed to read cert file %s",
+ if(fInCert) {
+ long cert_tell = 0;
+ bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
+ if(continue_reading)
+ cert_tell = ftell(fInCert);
+ if(cert_tell < 0)
+ continue_reading = FALSE;
+ else
+ certsize = (size_t)cert_tell;
+ if(continue_reading)
+ continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
+ if(continue_reading)
+ certdata = malloc(certsize + 1);
+ if((!certdata) ||
+ ((int) fread(certdata, certsize, 1, fInCert) != 1))
+ continue_reading = FALSE;
+ fclose(fInCert);
+ if(!continue_reading) {
+ failf(data, "schannel: Failed to read cert file %s",
data->set.ssl.cert);
- free(certdata);
- return CURLE_SSL_CERTPROBLEM;
+ free(certdata);
+ return CURLE_SSL_CERTPROBLEM;
+ }
}
/* Convert key-pair data to the in-memory certificate store */
datablob.pbData = (BYTE*)certdata;
- datablob.cbData = (DWORD)filesize;
+ datablob.cbData = (DWORD)certsize;
if(data->set.ssl.key_passwd != NULL)
pwd_len = strlen(data->set.ssl.key_passwd);
pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
- if(pwd_len > 0)
- str_w_len =
- MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
- data->set.ssl.key_passwd, (int)pwd_len,
- pszPassword, (int)(pwd_len + 1));
-
- if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
- pszPassword[str_w_len] = 0;
- else
- pszPassword[0] = 0;
-
- cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
- free(pszPassword);
- free(certdata);
+ if(pszPassword) {
+ if(pwd_len > 0)
+ str_w_len = MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ data->set.ssl.key_passwd, (int)pwd_len,
+ pszPassword, (int)(pwd_len + 1));
+
+ if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
+ pszPassword[str_w_len] = 0;
+ else
+ pszPassword[0] = 0;
+
+ cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+ free(pszPassword);
+ }
+ if(!blob)
+ free(certdata);
if(cert_store == NULL) {
DWORD errorcode = GetLastError();
if(errorcode == ERROR_INVALID_PASSWORD)
failf(data, "schannel: Failed to import cert file %s, "
- "password is bad", data->set.ssl.cert);
+ "password is bad",
+ cert_showfilename_error);
else
failf(data, "schannel: Failed to import cert file %s, "
- "last error is 0x%x", data->set.ssl.cert, errorcode);
+ "last error is 0x%x",
+ cert_showfilename_error, errorcode);
return CURLE_SSL_CERTPROBLEM;
}
@@ -681,7 +710,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
if(client_certs[0] == NULL) {
failf(data, "schannel: Failed to get certificate from file %s"
", last error is 0x%x",
- data->set.ssl.cert, GetLastError());
+ cert_showfilename_error, GetLastError());
CertCloseStore(cert_store, 0);
return CURLE_SSL_CERTPROBLEM;
}