Loading docs/libcurl/opts/CURLOPT_SSLCERT.3 +3 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,9 @@ you wish to authenticate with as it is named in the security database. If you want to use a file from the current directory, please precede it with "./" prefix, in order to avoid confusion with a nickname. With WinSSL, this can be expression like "CurrentUser\\MY\\<thumbprint>" to refer to a certificate in the system certificates store. When using a client certificate, you most likely also need to provide a private key with \fICURLOPT_SSLKEY(3)\fP. Loading lib/vtls/schannel.c +120 −1 Original line number Diff line number Diff line Loading @@ -92,6 +92,12 @@ #endif #endif #ifdef UNICODE #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W #else #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A #endif #ifndef SP_PROT_SSL2_CLIENT #define SP_PROT_SSL2_CLIENT 0x00000008 #endif Loading Loading @@ -124,6 +130,9 @@ #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 #define CERT_THUMBPRINT_STR_LEN 40 #define CERT_THUMBPRINT_DATA_LEN 20 /* Uncomment to force verbose output * #define infof(x, y, ...) printf(y, __VA_ARGS__) * #define failf(x, y, ...) printf(y, __VA_ARGS__) Loading Loading @@ -227,6 +236,56 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) return CURLE_OK; } static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, TCHAR **thumbprint) { TCHAR *sep; size_t store_name_len; sep = _tcschr(path, TEXT('\\')); if(sep == NULL) return CURLE_SSL_CONNECT_ERROR; store_name_len = sep - path; if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER; else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_SERVICES; else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_USERS; else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; else if(_tcsnccmp(path, TEXT("LocalMachineEnterprise"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; else return CURLE_SSL_CONNECT_ERROR; *store_path = sep + 1; sep = _tcschr(*store_path, TEXT('\\')); if(sep == NULL) return CURLE_SSL_CONNECT_ERROR; *sep = 0; *thumbprint = sep + 1; if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) return CURLE_SSL_CONNECT_ERROR; return CURLE_OK; } static CURLcode schannel_connect_step1(struct connectdata *conn, int sockindex) { Loading @@ -241,6 +300,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) unsigned char alpn_buffer[128]; #endif SCHANNEL_CRED schannel_cred; PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; struct curl_schannel_cred *old_cred = NULL; struct in_addr addr; Loading Loading @@ -361,11 +421,67 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } /* client certificate */ if(data->set.ssl.cert) { DWORD cert_store_name; TCHAR *cert_store_path; TCHAR *cert_thumbprint_str; CRYPT_HASH_BLOB cert_thumbprint; BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; HCERTSTORE cert_store; TCHAR *cert_path = Curl_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 != CURLE_OK) { Curl_unicodefree(cert_path); return result; } cert_store = CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, NULL, cert_store_name, cert_store_path); if(!cert_store) { Curl_unicodefree(cert_path); return CURLE_SSL_CONNECT_ERROR; } cert_thumbprint.pbData = cert_thumbprint_data; cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, CRYPT_STRING_HEXRAW, cert_thumbprint_data, &cert_thumbprint.cbData, NULL, NULL)) { Curl_unicodefree(cert_path); return CURLE_SSL_CONNECT_ERROR; } client_certs[0] = CertFindCertificateInStore( cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH, &cert_thumbprint, NULL); Curl_unicodefree(cert_path); if(client_certs[0]) { schannel_cred.cCreds = 1; schannel_cred.paCred = client_certs; } CertCloseStore(cert_store, 0); } /* allocate memory for the re-usable credential handle */ BACKEND->cred = (struct curl_schannel_cred *) calloc(1, sizeof(struct curl_schannel_cred)); if(!BACKEND->cred) { failf(data, "schannel: unable to allocate memory"); if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); return CURLE_OUT_OF_MEMORY; } BACKEND->cred->refcount = 1; Loading @@ -379,6 +495,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) &BACKEND->cred->cred_handle, &BACKEND->cred->time_stamp); if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); if(sspi_status != SEC_E_OK) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) failf(data, "schannel: SNI or certificate check failed: %s", Loading Loading
docs/libcurl/opts/CURLOPT_SSLCERT.3 +3 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,9 @@ you wish to authenticate with as it is named in the security database. If you want to use a file from the current directory, please precede it with "./" prefix, in order to avoid confusion with a nickname. With WinSSL, this can be expression like "CurrentUser\\MY\\<thumbprint>" to refer to a certificate in the system certificates store. When using a client certificate, you most likely also need to provide a private key with \fICURLOPT_SSLKEY(3)\fP. Loading
lib/vtls/schannel.c +120 −1 Original line number Diff line number Diff line Loading @@ -92,6 +92,12 @@ #endif #endif #ifdef UNICODE #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W #else #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A #endif #ifndef SP_PROT_SSL2_CLIENT #define SP_PROT_SSL2_CLIENT 0x00000008 #endif Loading Loading @@ -124,6 +130,9 @@ #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 #define CERT_THUMBPRINT_STR_LEN 40 #define CERT_THUMBPRINT_DATA_LEN 20 /* Uncomment to force verbose output * #define infof(x, y, ...) printf(y, __VA_ARGS__) * #define failf(x, y, ...) printf(y, __VA_ARGS__) Loading Loading @@ -227,6 +236,56 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) return CURLE_OK; } static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, TCHAR **thumbprint) { TCHAR *sep; size_t store_name_len; sep = _tcschr(path, TEXT('\\')); if(sep == NULL) return CURLE_SSL_CONNECT_ERROR; store_name_len = sep - path; if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER; else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_SERVICES; else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_USERS; else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; else if(_tcsnccmp(path, TEXT("LocalMachineEnterprise"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; else return CURLE_SSL_CONNECT_ERROR; *store_path = sep + 1; sep = _tcschr(*store_path, TEXT('\\')); if(sep == NULL) return CURLE_SSL_CONNECT_ERROR; *sep = 0; *thumbprint = sep + 1; if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) return CURLE_SSL_CONNECT_ERROR; return CURLE_OK; } static CURLcode schannel_connect_step1(struct connectdata *conn, int sockindex) { Loading @@ -241,6 +300,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) unsigned char alpn_buffer[128]; #endif SCHANNEL_CRED schannel_cred; PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; struct curl_schannel_cred *old_cred = NULL; struct in_addr addr; Loading Loading @@ -361,11 +421,67 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } /* client certificate */ if(data->set.ssl.cert) { DWORD cert_store_name; TCHAR *cert_store_path; TCHAR *cert_thumbprint_str; CRYPT_HASH_BLOB cert_thumbprint; BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; HCERTSTORE cert_store; TCHAR *cert_path = Curl_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 != CURLE_OK) { Curl_unicodefree(cert_path); return result; } cert_store = CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, NULL, cert_store_name, cert_store_path); if(!cert_store) { Curl_unicodefree(cert_path); return CURLE_SSL_CONNECT_ERROR; } cert_thumbprint.pbData = cert_thumbprint_data; cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, CRYPT_STRING_HEXRAW, cert_thumbprint_data, &cert_thumbprint.cbData, NULL, NULL)) { Curl_unicodefree(cert_path); return CURLE_SSL_CONNECT_ERROR; } client_certs[0] = CertFindCertificateInStore( cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_HASH, &cert_thumbprint, NULL); Curl_unicodefree(cert_path); if(client_certs[0]) { schannel_cred.cCreds = 1; schannel_cred.paCred = client_certs; } CertCloseStore(cert_store, 0); } /* allocate memory for the re-usable credential handle */ BACKEND->cred = (struct curl_schannel_cred *) calloc(1, sizeof(struct curl_schannel_cred)); if(!BACKEND->cred) { failf(data, "schannel: unable to allocate memory"); if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); return CURLE_OUT_OF_MEMORY; } BACKEND->cred->refcount = 1; Loading @@ -379,6 +495,9 @@ schannel_connect_step1(struct connectdata *conn, int sockindex) &BACKEND->cred->cred_handle, &BACKEND->cred->time_stamp); if(client_certs[0]) CertFreeCertificateContext(client_certs[0]); if(sspi_status != SEC_E_OK) { if(sspi_status == SEC_E_WRONG_PRINCIPAL) failf(data, "schannel: SNI or certificate check failed: %s", Loading