HTTPS Proxy: Implement CURLOPT_PROXY_PINNEDPUBLICKEY
This commit is contained in:
Родитель
1232dbb8bd
Коммит
4f8b17743d
|
@ -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_TYPE 7.52.0
|
||||
CURLOPT_PROXY_TLSAUTH_USERNAME 7.52.0
|
||||
CURLOPT_PROXY_PINNEDPUBLICKEY 7.52.0
|
||||
CURLOPT_PROXY_TRANSFER_MODE 7.18.0
|
||||
CURLOPT_PUT 7.1
|
||||
CURLOPT_QUOTE 7.1
|
||||
|
|
|
@ -1770,6 +1770,10 @@ typedef enum {
|
|||
CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */
|
||||
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 */
|
||||
} 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.
|
||||
* 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 *));
|
||||
#else
|
||||
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_CAFILE_ORIG, /* 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_PROXY, /* list of ciphers to use */
|
||||
STRING_SSL_EGDSOCKET, /* path to file containing the EGD daemon socket */
|
||||
|
|
|
@ -424,6 +424,10 @@ cyassl_connect_step2(struct connectdata *conn,
|
|||
conn->host.name;
|
||||
const char * const dispname = SSL_IS_PROXY() ?
|
||||
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->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
|
||||
X509 *x509;
|
||||
const char *x509_der;
|
||||
|
@ -529,7 +533,7 @@ cyassl_connect_step2(struct connectdata *conn,
|
|||
}
|
||||
|
||||
result = Curl_pin_peer_pubkey(data,
|
||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
||||
pinnedpubkey,
|
||||
(const unsigned char *)pubkey->header,
|
||||
(size_t)(pubkey->end - pubkey->header));
|
||||
if(result) {
|
||||
|
|
|
@ -1096,7 +1096,8 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
|
|||
}
|
||||
|
||||
/* 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) {
|
||||
curl_X509certificate x509;
|
||||
curl_asn1Element *p;
|
||||
|
|
|
@ -1229,7 +1229,8 @@ gtls_connect_step3(struct connectdata *conn,
|
|||
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) {
|
||||
result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
|
||||
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 :
|
||||
conn->host.name;
|
||||
const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
|
||||
|
||||
int ret = -1;
|
||||
char errorbuf[128];
|
||||
errorbuf[0]=0;
|
||||
|
@ -453,6 +452,9 @@ mbed_connect_step2(struct connectdata *conn,
|
|||
struct Curl_easy *data = conn->data;
|
||||
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
||||
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
|
||||
const char *next_protocol;
|
||||
|
@ -524,7 +526,7 @@ mbed_connect_step2(struct connectdata *conn,
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
||||
if(pinnedpubkey) {
|
||||
int size;
|
||||
CURLcode result;
|
||||
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. */
|
||||
result = Curl_pin_peer_pubkey(data,
|
||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
||||
pinnedpubkey,
|
||||
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
||||
if(result) {
|
||||
mbedtls_x509_crt_free(p);
|
||||
|
|
|
@ -1926,6 +1926,10 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
|
|||
PRUint32 timeout;
|
||||
long * const certverifyresult = SSL_IS_PROXY() ?
|
||||
&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 */
|
||||
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)
|
||||
/* status already printed */
|
||||
goto error;
|
||||
|
|
|
@ -2891,7 +2891,8 @@ static CURLcode servercert(struct connectdata *conn,
|
|||
/* when not strict, we don't bother about the verify cert problems */
|
||||
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) {
|
||||
result = pkp_pin_peer_pubkey(data, connssl->server_cert, ptr);
|
||||
if(result)
|
||||
|
|
|
@ -397,6 +397,10 @@ polarssl_connect_step2(struct connectdata *conn,
|
|||
struct Curl_easy *data = conn->data;
|
||||
struct ssl_connect_data* connssl = &conn->ssl[sockindex];
|
||||
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];
|
||||
errorbuf[0] = 0;
|
||||
|
@ -458,7 +462,7 @@ polarssl_connect_step2(struct connectdata *conn,
|
|||
}
|
||||
|
||||
/* adapted from mbedtls.c */
|
||||
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) {
|
||||
if(pinnedpubkey) {
|
||||
int size;
|
||||
CURLcode result;
|
||||
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. */
|
||||
result = Curl_pin_peer_pubkey(data,
|
||||
data->set.str[STRING_SSL_PINNEDPUBLICKEY],
|
||||
pinnedpubkey,
|
||||
&pubkey[PUB_DER_MAX_BYTES - size], size);
|
||||
if(result) {
|
||||
x509_crt_free(p);
|
||||
|
|
Загрузка…
Ссылка в новой задаче