From cac5374298b3e79405bbdabe38941227c73a4c96 Mon Sep 17 00:00:00 2001 From: Gilles Vollant Date: Fri, 15 May 2020 10:47:46 +0200 Subject: 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 --- lib/vtls/schannel.c | 143 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 57 deletions(-) (limited to 'lib/vtls/schannel.c') 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; } -- cgit v1.2.3