mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
HTTPS Proxy: Implement CURLOPT_PROXY_PINNEDPUBLICKEY
This commit is contained in:
parent
1232dbb8bd
commit
4f8b17743d
99
docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.3
Normal file
99
docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.3
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
.\" **************************************************************************
|
||||||
|
.\" * _ _ ____ _
|
||||||
|
.\" * Project ___| | | | _ \| |
|
||||||
|
.\" * / __| | | | |_) | |
|
||||||
|
.\" * | (__| |_| | _ <| |___
|
||||||
|
.\" * \___|\___/|_| \_\_____|
|
||||||
|
.\" *
|
||||||
|
.\" * Copyright (C) 1998 - 2016, 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
|
||||||
|
.\" * are also available at https://curl.haxx.se/docs/copyright.html.
|
||||||
|
.\" *
|
||||||
|
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||||
|
.\" * copies of the Software, and permit persons to whom the Software is
|
||||||
|
.\" * furnished to do so, under the terms of the COPYING file.
|
||||||
|
.\" *
|
||||||
|
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
.\" * KIND, either express or implied.
|
||||||
|
.\" *
|
||||||
|
.\" **************************************************************************
|
||||||
|
.\"
|
||||||
|
.TH CURLOPT_PROXY_PINNEDPUBLICKEY 3 "24 Nov 2016" "libcurl 7.52.0" "curl_easy_setopt options"
|
||||||
|
.SH NAME
|
||||||
|
CURLOPT_PROXY_PINNEDPUBLICKEY \- set pinned public key for https proxy
|
||||||
|
.SH SYNOPSIS
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROXY_PINNEDPUBLICKEY, char *pinnedpubkey);
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Pass a pointer to a zero terminated string as parameter. The string can be the
|
||||||
|
file name of your pinned public key. The file format expected is "PEM" or "DER".
|
||||||
|
The string can also be any number of base64 encoded sha256 hashes preceded by
|
||||||
|
"sha256//" and separated by ";"
|
||||||
|
|
||||||
|
When negotiating a TLS or SSL connection, the https proxy sends a certificate
|
||||||
|
indicating its identity. A public key is extracted from this certificate and
|
||||||
|
if it does not exactly match the public key provided to this option, curl will
|
||||||
|
abort the connection before sending or receiving any data.
|
||||||
|
|
||||||
|
On mismatch, \fICURLE_SSL_PINNEDPUBKEYNOTMATCH\fP is returned.
|
||||||
|
.SH DEFAULT
|
||||||
|
NULL
|
||||||
|
.SH PROTOCOLS
|
||||||
|
All TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.
|
||||||
|
.SH EXAMPLE
|
||||||
|
.nf
|
||||||
|
TODO
|
||||||
|
.fi
|
||||||
|
.SH PUBLIC KEY EXTRACTION
|
||||||
|
If you do not have the https proxy server's public key file you can extract it
|
||||||
|
from the https proxy server's certificate.
|
||||||
|
.nf
|
||||||
|
# retrieve the server's certificate if you don't already have it
|
||||||
|
#
|
||||||
|
# be sure to examine the certificate to see if it is what you expected
|
||||||
|
#
|
||||||
|
# Windows-specific:
|
||||||
|
# - Use NUL instead of /dev/null.
|
||||||
|
# - OpenSSL may wait for input instead of disconnecting. Hit enter.
|
||||||
|
# - If you don't have sed, then just copy the certificate into a file:
|
||||||
|
# Lines from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----.
|
||||||
|
#
|
||||||
|
openssl s_client -servername www.example.com -connect www.example.com:443 < /dev/null | sed -n "/-----BEGIN/,/-----END/p" > www.example.com.pem
|
||||||
|
|
||||||
|
# extract public key in pem format from certificate
|
||||||
|
openssl x509 -in www.example.com.pem -pubkey -noout > www.example.com.pubkey.pem
|
||||||
|
|
||||||
|
# convert public key from pem to der
|
||||||
|
openssl asn1parse -noout -inform pem -in www.example.com.pubkey.pem -out www.example.com.pubkey.der
|
||||||
|
|
||||||
|
# sha256 hash and base64 encode der to string for use
|
||||||
|
openssl dgst -sha256 -binary www.example.com.pubkey.der | openssl base64
|
||||||
|
.fi
|
||||||
|
The public key in PEM format contains a header, base64 data and a
|
||||||
|
footer:
|
||||||
|
.nf
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
[BASE 64 DATA]
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
.fi
|
||||||
|
.SH AVAILABILITY
|
||||||
|
PEM/DER support:
|
||||||
|
|
||||||
|
7.52.0: GSKit, GnuTLS, NSS, OpenSSL, PolarSSL, mbedtls, wolfSSL/CyaSSL
|
||||||
|
|
||||||
|
sha256 support:
|
||||||
|
|
||||||
|
7.52.0: GnuTLS, NSS, OpenSSL, PolarSSL, mbedtls, wolfSSL/CyaSSL
|
||||||
|
|
||||||
|
Other SSL backends not supported.
|
||||||
|
.SH RETURN VALUE
|
||||||
|
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
|
||||||
|
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR CURLOPT_PROXY_SSL_VERIFYPEER "(3), "
|
||||||
|
.BR CURLOPT_PROXY_SSL_VERIFYHOST "(3), "
|
||||||
|
.BR CURLOPT_PROXY_CAINFO "(3), "
|
||||||
|
.BR CURLOPT_PROXY_CAPATH "(3), "
|
@ -488,6 +488,7 @@ CURLOPT_PROXY_SSL_VERIFYPEER 7.52.0
|
|||||||
CURLOPT_PROXY_TLSAUTH_PASSWORD 7.52.0
|
CURLOPT_PROXY_TLSAUTH_PASSWORD 7.52.0
|
||||||
CURLOPT_PROXY_TLSAUTH_TYPE 7.52.0
|
CURLOPT_PROXY_TLSAUTH_TYPE 7.52.0
|
||||||
CURLOPT_PROXY_TLSAUTH_USERNAME 7.52.0
|
CURLOPT_PROXY_TLSAUTH_USERNAME 7.52.0
|
||||||
|
CURLOPT_PROXY_PINNEDPUBLICKEY 7.52.0
|
||||||
CURLOPT_PROXY_TRANSFER_MODE 7.18.0
|
CURLOPT_PROXY_TRANSFER_MODE 7.18.0
|
||||||
CURLOPT_PUT 7.1
|
CURLOPT_PUT 7.1
|
||||||
CURLOPT_QUOTE 7.1
|
CURLOPT_QUOTE 7.1
|
||||||
|
@ -1770,6 +1770,10 @@ typedef enum {
|
|||||||
CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */
|
CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */
|
||||||
CINIT(SOCKS_PROXYTYPE, LONG, 263),
|
CINIT(SOCKS_PROXYTYPE, LONG, 263),
|
||||||
|
|
||||||
|
/* The public key in DER form used to validate the proxy public key
|
||||||
|
this option is used only if PROXY_SSL_VERIFYPEER is true */
|
||||||
|
CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 264),
|
||||||
|
|
||||||
CURLOPT_LASTENTRY /* the last unused */
|
CURLOPT_LASTENTRY /* the last unused */
|
||||||
} CURLoption;
|
} CURLoption;
|
||||||
|
|
||||||
|
14
lib/url.c
14
lib/url.c
@ -2181,7 +2181,19 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
|
|||||||
* Set pinned public key for SSL connection.
|
* Set pinned public key for SSL connection.
|
||||||
* Specify file name of the public key in DER format.
|
* Specify file name of the public key in DER format.
|
||||||
*/
|
*/
|
||||||
result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG],
|
||||||
|
va_arg(param, char *));
|
||||||
|
#else
|
||||||
|
result = CURLE_NOT_BUILT_IN;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case CURLOPT_PROXY_PINNEDPUBLICKEY:
|
||||||
|
#ifdef have_curlssl_pinnedpubkey /* only by supported backends */
|
||||||
|
/*
|
||||||
|
* Set pinned public key for SSL connection.
|
||||||
|
* Specify file name of the public key in DER format.
|
||||||
|
*/
|
||||||
|
result = setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY],
|
||||||
va_arg(param, char *));
|
va_arg(param, char *));
|
||||||
#else
|
#else
|
||||||
result = CURLE_NOT_BUILT_IN;
|
result = CURLE_NOT_BUILT_IN;
|
||||||
|
@ -1471,7 +1471,8 @@ enum dupstring {
|
|||||||
STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */
|
STRING_SSL_CAPATH_PROXY, /* CA directory name (doesn't work on windows) */
|
||||||
STRING_SSL_CAFILE_ORIG, /* certificate file to verify peer against */
|
STRING_SSL_CAFILE_ORIG, /* certificate file to verify peer against */
|
||||||
STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
|
STRING_SSL_CAFILE_PROXY, /* certificate file to verify peer against */
|
||||||
STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */
|
STRING_SSL_PINNEDPUBLICKEY_ORIG, /* public key file to verify peer against */
|
||||||
|
STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */
|
||||||
STRING_SSL_CIPHER_LIST_ORIG, /* list of ciphers to use */
|
STRING_SSL_CIPHER_LIST_ORIG, /* list of ciphers to use */
|
||||||
STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */
|
STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */
|
||||||
STRING_SSL_EGDSOCKET, /* path to file containing the EGD daemon socket */
|
STRING_SSL_EGDSOCKET, /* path to file containing the EGD daemon socket */
|
||||||
|
@ -424,6 +424,10 @@ cyassl_connect_step2(struct connectdata *conn,
|
|||||||
conn->host.name;
|
conn->host.name;
|
||||||
const char * const dispname = SSL_IS_PROXY() ?
|
const char * const dispname = SSL_IS_PROXY() ?
|
||||||
conn->http_proxy.host.dispname : conn->host.dispname;
|
conn->http_proxy.host.dispname : conn->host.dispname;
|
||||||
|
const char * const pinnedpubkey = SSL_IS_PROXY() ?
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
|
conn->http_proxy.host.dispname : conn->host.dispname;
|
||||||
|
|
||||||
conn->recv[sockindex] = cyassl_recv;
|
conn->recv[sockindex] = cyassl_recv;
|
||||||
conn->send[sockindex] = cyassl_send;
|
conn->send[sockindex] = cyassl_send;
|
||||||
@ -497,7 +501,7 @@ cyassl_connect_step2(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
if(pinnedpubkey) {
|
||||||
#ifdef KEEP_PEER_CERT
|
#ifdef KEEP_PEER_CERT
|
||||||
X509 *x509;
|
X509 *x509;
|
||||||
const char *x509_der;
|
const char *x509_der;
|
||||||
@ -529,7 +533,7 @@ cyassl_connect_step2(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = Curl_pin_peer_pubkey(data,
|
result = Curl_pin_peer_pubkey(data,
|
||||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
pinnedpubkey,
|
||||||
(const unsigned char *)pubkey->header,
|
(const unsigned char *)pubkey->header,
|
||||||
(size_t)(pubkey->end - pubkey->header));
|
(size_t)(pubkey->end - pubkey->header));
|
||||||
if(result) {
|
if(result) {
|
||||||
|
@ -1096,7 +1096,8 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check pinned public key. */
|
/* Check pinned public key. */
|
||||||
ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
|
ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
if(!result && ptr) {
|
if(!result && ptr) {
|
||||||
curl_X509certificate x509;
|
curl_X509certificate x509;
|
||||||
curl_asn1Element *p;
|
curl_asn1Element *p;
|
||||||
|
@ -1229,7 +1229,8 @@ gtls_connect_step3(struct connectdata *conn,
|
|||||||
infof(data, "\t server certificate activation date OK\n");
|
infof(data, "\t server certificate activation date OK\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
|
ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
if(ptr) {
|
if(ptr) {
|
||||||
result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
|
result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
|
||||||
if(result != CURLE_OK) {
|
if(result != CURLE_OK) {
|
||||||
|
@ -171,7 +171,6 @@ mbed_connect_step1(struct connectdata *conn,
|
|||||||
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
|
||||||
conn->host.name;
|
conn->host.name;
|
||||||
const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
|
const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
|
||||||
|
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
char errorbuf[128];
|
char errorbuf[128];
|
||||||
errorbuf[0]=0;
|
errorbuf[0]=0;
|
||||||
@ -453,6 +452,9 @@ mbed_connect_step2(struct connectdata *conn,
|
|||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
||||||
const mbedtls_x509_crt *peercert;
|
const mbedtls_x509_crt *peercert;
|
||||||
|
const char * const pinnedpubkey = SSL_IS_PROXY() ?
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
|
|
||||||
#ifdef HAS_ALPN
|
#ifdef HAS_ALPN
|
||||||
const char *next_protocol;
|
const char *next_protocol;
|
||||||
@ -524,7 +526,7 @@ mbed_connect_step2(struct connectdata *conn,
|
|||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
if(pinnedpubkey) {
|
||||||
int size;
|
int size;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
mbedtls_x509_crt *p;
|
mbedtls_x509_crt *p;
|
||||||
@ -563,7 +565,7 @@ mbed_connect_step2(struct connectdata *conn,
|
|||||||
|
|
||||||
/* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */
|
/* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */
|
||||||
result = Curl_pin_peer_pubkey(data,
|
result = Curl_pin_peer_pubkey(data,
|
||||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
pinnedpubkey,
|
||||||
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
||||||
if(result) {
|
if(result) {
|
||||||
mbedtls_x509_crt_free(p);
|
mbedtls_x509_crt_free(p);
|
||||||
|
@ -1926,6 +1926,10 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
|
|||||||
PRUint32 timeout;
|
PRUint32 timeout;
|
||||||
long * const certverifyresult = SSL_IS_PROXY() ?
|
long * const certverifyresult = SSL_IS_PROXY() ?
|
||||||
&data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult;
|
&data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult;
|
||||||
|
const char * const pinnedpubkey = SSL_IS_PROXY() ?
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
|
|
||||||
|
|
||||||
/* check timeout situation */
|
/* check timeout situation */
|
||||||
const long time_left = Curl_timeleft(data, NULL, TRUE);
|
const long time_left = Curl_timeleft(data, NULL, TRUE);
|
||||||
@ -1971,7 +1975,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = cmp_peer_pubkey(connssl, data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
|
result = cmp_peer_pubkey(connssl, pinnedpubkey);
|
||||||
if(result)
|
if(result)
|
||||||
/* status already printed */
|
/* status already printed */
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2891,7 +2891,8 @@ static CURLcode servercert(struct connectdata *conn,
|
|||||||
/* when not strict, we don't bother about the verify cert problems */
|
/* when not strict, we don't bother about the verify cert problems */
|
||||||
result = CURLE_OK;
|
result = CURLE_OK;
|
||||||
|
|
||||||
ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
|
ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
if(!result && ptr) {
|
if(!result && ptr) {
|
||||||
result = pkp_pin_peer_pubkey(data, connssl->server_cert, ptr);
|
result = pkp_pin_peer_pubkey(data, connssl->server_cert, ptr);
|
||||||
if(result)
|
if(result)
|
||||||
|
@ -397,6 +397,10 @@ polarssl_connect_step2(struct connectdata *conn,
|
|||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
|
const char * const pinnedpubkey = SSL_IS_PROXY() ?
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
|
||||||
|
|
||||||
|
|
||||||
char errorbuf[128];
|
char errorbuf[128];
|
||||||
errorbuf[0] = 0;
|
errorbuf[0] = 0;
|
||||||
@ -458,7 +462,7 @@ polarssl_connect_step2(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* adapted from mbedtls.c */
|
/* adapted from mbedtls.c */
|
||||||
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
if(pinnedpubkey) {
|
||||||
int size;
|
int size;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
x509_crt *p;
|
x509_crt *p;
|
||||||
@ -500,7 +504,7 @@ polarssl_connect_step2(struct connectdata *conn,
|
|||||||
|
|
||||||
/* pk_write_pubkey_der writes data at the end of the buffer. */
|
/* pk_write_pubkey_der writes data at the end of the buffer. */
|
||||||
result = Curl_pin_peer_pubkey(data,
|
result = Curl_pin_peer_pubkey(data,
|
||||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
pinnedpubkey,
|
||||||
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
||||||
if(result) {
|
if(result) {
|
||||||
x509_crt_free(p);
|
x509_crt_free(p);
|
||||||
|
Loading…
Reference in New Issue
Block a user