Loading docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 +5 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms Loading Loading @@ -105,6 +105,8 @@ PEM/DER support: 7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+ 7.58.1: SChannel/WinSSL sha256 support: 7.44.0: OpenSSL, GnuTLS, NSS and wolfSSL/CyaSSL Loading @@ -115,6 +117,8 @@ sha256 support: 7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+ 7.58.1: SChannel/WinSSL Windows XP SP3+ Other SSL backends not supported. .SH RETURN VALUE Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or Loading lib/vtls/schannel.c +140 −3 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms Loading Loading @@ -129,6 +129,10 @@ * #define failf(x, y, ...) printf(y, __VA_ARGS__) */ #ifndef CALG_SHA_256 # define CALG_SHA_256 0x0000800c #endif /* Structs to store Schannel handles */ struct curl_schannel_cred { CredHandle cred_handle; Loading Loading @@ -165,6 +169,9 @@ struct ssl_backend_data { static Curl_recv schannel_recv; static Curl_send schannel_send; static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, const char *pinnedpubkey); #ifdef _WIN32_WCE static CURLcode verify_certificate(struct connectdata *conn, int sockindex); #endif Loading Loading @@ -542,6 +549,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) bool doread; char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const char *pubkey_ptr; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; Loading Loading @@ -761,6 +769,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) infof(data, "schannel: SSL/TLS handshake complete\n"); } pubkey_ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; if(pubkey_ptr) { result = pkp_pin_peer_pubkey(conn, sockindex, pubkey_ptr); if(result) { failf(data, "SSL: public key does not match pinned public key!"); return result; } } #ifdef _WIN32_WCE /* Windows CE doesn't do any server certificate validation. We have to do it manually. */ Loading Loading @@ -1669,6 +1688,68 @@ static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM, return CURLE_OK; } static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, const char *pinnedpubkey) { SECURITY_STATUS status; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; CERT_CONTEXT *pCertContextServer = NULL; const char *x509_der; int x509_der_len; curl_X509certificate x509_parsed; curl_asn1Element *pubkey; /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) return CURLE_OK; do { status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); if((status != SEC_E_OK) || (pCertContextServer == NULL)) { failf(data, "schannel: Failed to read remote certificate context: %s", Curl_sspi_strerror(conn, status)); break; /* failed */ } if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && (pCertContextServer->cbCertEncoded > 0))) break; x509_der = pCertContextServer->pbCertEncoded; x509_der_len = pCertContextServer->cbCertEncoded; memset(&x509_parsed, 0, sizeof x509_parsed); if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) break; pubkey = &x509_parsed.subjectPublicKeyInfo; if(!pubkey->header || pubkey->end <= pubkey->header) { failf(data, "SSL: failed retrieving public key from server certificate"); break; } result = Curl_pin_peer_pubkey(data, pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); if(result) { failf(data, "SSL: public key does not match pinned public key!"); } } while(0); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); return result; } #ifdef _WIN32_WCE static CURLcode verify_certificate(struct connectdata *conn, int sockindex) { Loading Loading @@ -1809,6 +1890,62 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex) } #endif /* _WIN32_WCE */ static void Curl_schannel_checksum(const unsigned char *input, size_t inputlen, unsigned char *checksum, size_t checksumlen, const unsigned char *pszProvider, const unsigned int algId) { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; size_t cbHashSize = 0, dwCount = sizeof(size_t); /* since this can fail in multiple ways, zero memory first so we never * return old data */ memset(checksum, 0, checksumlen); if(!CryptAcquireContext(&hProv, NULL, NULL, pszProvider, CRYPT_VERIFYCONTEXT)) return; /* failed */ do { if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) break; /* failed */ if(!CryptHashData(hHash, (const BYTE*) input, inputlen, 0)) break; /* failed */ /* get hash size */ if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, &dwCount, 0)) break; /* failed */ /* check hash size */ if(checksumlen < cbHashSize) break; /* failed */ if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &checksumlen, 0)) break; /* failed */ } while(0); if(hHash) CryptDestroyHash(hHash); if(hProv) CryptReleaseContext(hProv, 0); } void Curl_schannel_sha256sum(unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256len) { Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, PROV_RSA_AES, CALG_SHA_256); } static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { Loading @@ -1821,7 +1958,7 @@ const struct Curl_ssl Curl_ssl_schannel = { 0, /* have_ca_path */ 1, /* have_certinfo */ 0, /* have_pinnedpubkey */ 1, /* have_pinnedpubkey */ 0, /* have_ssl_ctx */ 0, /* support_https_proxy */ Loading @@ -1846,7 +1983,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_engines_list, /* engines_list */ Curl_none_false_start, /* false_start */ Curl_none_md5sum, /* md5sum */ NULL /* sha256sum */ Curl_schannel_sha256sum /* sha256sum */ }; #endif /* USE_SCHANNEL */ tests/runtests.pl +1 −0 Original line number Diff line number Diff line Loading @@ -2772,6 +2772,7 @@ sub checksystem { } if ($libcurl =~ /winssl/i) { $has_winssl=1; $has_sslpinning=1; $ssllib="WinSSL"; } elsif ($libcurl =~ /openssl/i) { Loading Loading
docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3 +5 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms Loading Loading @@ -105,6 +105,8 @@ PEM/DER support: 7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+ 7.58.1: SChannel/WinSSL sha256 support: 7.44.0: OpenSSL, GnuTLS, NSS and wolfSSL/CyaSSL Loading @@ -115,6 +117,8 @@ sha256 support: 7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+ 7.58.1: SChannel/WinSSL Windows XP SP3+ Other SSL backends not supported. .SH RETURN VALUE Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or Loading
lib/vtls/schannel.c +140 −3 Original line number Diff line number Diff line Loading @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> * Copyright (C) 2012 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2012 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms Loading Loading @@ -129,6 +129,10 @@ * #define failf(x, y, ...) printf(y, __VA_ARGS__) */ #ifndef CALG_SHA_256 # define CALG_SHA_256 0x0000800c #endif /* Structs to store Schannel handles */ struct curl_schannel_cred { CredHandle cred_handle; Loading Loading @@ -165,6 +169,9 @@ struct ssl_backend_data { static Curl_recv schannel_recv; static Curl_send schannel_send; static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, const char *pinnedpubkey); #ifdef _WIN32_WCE static CURLcode verify_certificate(struct connectdata *conn, int sockindex); #endif Loading Loading @@ -542,6 +549,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) bool doread; char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const char *pubkey_ptr; doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; Loading Loading @@ -761,6 +769,17 @@ schannel_connect_step2(struct connectdata *conn, int sockindex) infof(data, "schannel: SSL/TLS handshake complete\n"); } pubkey_ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; if(pubkey_ptr) { result = pkp_pin_peer_pubkey(conn, sockindex, pubkey_ptr); if(result) { failf(data, "SSL: public key does not match pinned public key!"); return result; } } #ifdef _WIN32_WCE /* Windows CE doesn't do any server certificate validation. We have to do it manually. */ Loading Loading @@ -1669,6 +1688,68 @@ static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM, return CURLE_OK; } static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, const char *pinnedpubkey) { SECURITY_STATUS status; struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; CERT_CONTEXT *pCertContextServer = NULL; const char *x509_der; int x509_der_len; curl_X509certificate x509_parsed; curl_asn1Element *pubkey; /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) return CURLE_OK; do { status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &pCertContextServer); if((status != SEC_E_OK) || (pCertContextServer == NULL)) { failf(data, "schannel: Failed to read remote certificate context: %s", Curl_sspi_strerror(conn, status)); break; /* failed */ } if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && (pCertContextServer->cbCertEncoded > 0))) break; x509_der = pCertContextServer->pbCertEncoded; x509_der_len = pCertContextServer->cbCertEncoded; memset(&x509_parsed, 0, sizeof x509_parsed); if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) break; pubkey = &x509_parsed.subjectPublicKeyInfo; if(!pubkey->header || pubkey->end <= pubkey->header) { failf(data, "SSL: failed retrieving public key from server certificate"); break; } result = Curl_pin_peer_pubkey(data, pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); if(result) { failf(data, "SSL: public key does not match pinned public key!"); } } while(0); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); return result; } #ifdef _WIN32_WCE static CURLcode verify_certificate(struct connectdata *conn, int sockindex) { Loading Loading @@ -1809,6 +1890,62 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex) } #endif /* _WIN32_WCE */ static void Curl_schannel_checksum(const unsigned char *input, size_t inputlen, unsigned char *checksum, size_t checksumlen, const unsigned char *pszProvider, const unsigned int algId) { HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; size_t cbHashSize = 0, dwCount = sizeof(size_t); /* since this can fail in multiple ways, zero memory first so we never * return old data */ memset(checksum, 0, checksumlen); if(!CryptAcquireContext(&hProv, NULL, NULL, pszProvider, CRYPT_VERIFYCONTEXT)) return; /* failed */ do { if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) break; /* failed */ if(!CryptHashData(hHash, (const BYTE*) input, inputlen, 0)) break; /* failed */ /* get hash size */ if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, &dwCount, 0)) break; /* failed */ /* check hash size */ if(checksumlen < cbHashSize) break; /* failed */ if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &checksumlen, 0)) break; /* failed */ } while(0); if(hHash) CryptDestroyHash(hHash); if(hProv) CryptReleaseContext(hProv, 0); } void Curl_schannel_sha256sum(unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256len) { Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, PROV_RSA_AES, CALG_SHA_256); } static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { Loading @@ -1821,7 +1958,7 @@ const struct Curl_ssl Curl_ssl_schannel = { 0, /* have_ca_path */ 1, /* have_certinfo */ 0, /* have_pinnedpubkey */ 1, /* have_pinnedpubkey */ 0, /* have_ssl_ctx */ 0, /* support_https_proxy */ Loading @@ -1846,7 +1983,7 @@ const struct Curl_ssl Curl_ssl_schannel = { Curl_none_engines_list, /* engines_list */ Curl_none_false_start, /* false_start */ Curl_none_md5sum, /* md5sum */ NULL /* sha256sum */ Curl_schannel_sha256sum /* sha256sum */ }; #endif /* USE_SCHANNEL */
tests/runtests.pl +1 −0 Original line number Diff line number Diff line Loading @@ -2772,6 +2772,7 @@ sub checksystem { } if ($libcurl =~ /winssl/i) { $has_winssl=1; $has_sslpinning=1; $ssllib="WinSSL"; } elsif ($libcurl =~ /openssl/i) { Loading