mirror of
https://github.com/moparisthebest/curl
synced 2024-12-21 23:58:49 -05:00
SecureTransport/DarwinSSL: Implement public key pinning
This commit is contained in:
parent
33cfcfd9f0
commit
db4e79037d
@ -103,6 +103,8 @@ PEM/DER support:
|
|||||||
|
|
||||||
7.49.0: PolarSSL
|
7.49.0: PolarSSL
|
||||||
|
|
||||||
|
7.55.0: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+
|
||||||
|
|
||||||
sha256 support:
|
sha256 support:
|
||||||
|
|
||||||
7.44.0: OpenSSL, GnuTLS, NSS and wolfSSL/CyaSSL
|
7.44.0: OpenSSL, GnuTLS, NSS and wolfSSL/CyaSSL
|
||||||
@ -111,6 +113,8 @@ sha256 support:
|
|||||||
|
|
||||||
7.49.0: PolarSSL
|
7.49.0: PolarSSL
|
||||||
|
|
||||||
|
7.55.0: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+
|
||||||
|
|
||||||
Other SSL backends not supported.
|
Other SSL backends not supported.
|
||||||
.SH RETURN VALUE
|
.SH RETURN VALUE
|
||||||
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
|
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or
|
||||||
|
@ -113,6 +113,36 @@
|
|||||||
#define ioErr -36
|
#define ioErr -36
|
||||||
#define paramErr -50
|
#define paramErr -50
|
||||||
|
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY
|
||||||
|
/* both new and old APIs return rsa keys missing the spki header (not DER) */
|
||||||
|
static const unsigned char rsa4096SpkiHeader[] = {
|
||||||
|
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
|
||||||
|
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||||
|
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
|
||||||
|
0x00, 0x03, 0x82, 0x02, 0x0f, 0x00};
|
||||||
|
|
||||||
|
static const unsigned char rsa2048SpkiHeader[] = {
|
||||||
|
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
|
||||||
|
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
|
||||||
|
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
|
||||||
|
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
|
||||||
|
/* the *new* version doesn't return DER encoded ecdsa certs like the old... */
|
||||||
|
static const unsigned char ecDsaSecp256r1SpkiHeader[] = {
|
||||||
|
0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
|
||||||
|
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
|
||||||
|
0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
|
||||||
|
0x42, 0x00};
|
||||||
|
|
||||||
|
static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
|
||||||
|
0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
|
||||||
|
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
|
||||||
|
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
|
||||||
|
0x00, 0x22, 0x03, 0x62, 0x00};
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY_V1 */
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY */
|
||||||
|
|
||||||
/* The following two functions were ripped from Apple sample code,
|
/* The following two functions were ripped from Apple sample code,
|
||||||
* with some modifications: */
|
* with some modifications: */
|
||||||
static OSStatus SocketRead(SSLConnectionRef connection,
|
static OSStatus SocketRead(SSLConnectionRef connection,
|
||||||
@ -1996,6 +2026,112 @@ static int verify_cert(const char *cafile, struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY
|
||||||
|
static CURLcode pkp_pin_peer_pubkey(struct SessionHandle *data,
|
||||||
|
SSLContextRef ctx,
|
||||||
|
const char *pinnedpubkey)
|
||||||
|
{ /* Scratch */
|
||||||
|
size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
|
||||||
|
unsigned char *pubkey = NULL, *realpubkey = NULL, *spkiHeader = NULL;
|
||||||
|
CFDataRef publicKeyBits = NULL;
|
||||||
|
|
||||||
|
/* Result is returned to caller */
|
||||||
|
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
|
||||||
|
|
||||||
|
/* if a path wasn't specified, don't pin */
|
||||||
|
if(!pinnedpubkey)
|
||||||
|
return CURLE_OK;
|
||||||
|
|
||||||
|
|
||||||
|
if(!ctx)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
do {
|
||||||
|
SecTrustRef trust;
|
||||||
|
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
|
||||||
|
if(ret != noErr || trust == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
|
||||||
|
CFRelease(trust);
|
||||||
|
if(keyRef == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
|
||||||
|
|
||||||
|
publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL);
|
||||||
|
CFRelease(keyRef);
|
||||||
|
if(publicKeyBits == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
#elif DARWIN_SSL_PINNEDPUBKEY_V2
|
||||||
|
|
||||||
|
OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
|
||||||
|
&publicKeyBits);
|
||||||
|
CFRelease(keyRef);
|
||||||
|
if(success != errSecSuccess || publicKeyBits == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
|
||||||
|
|
||||||
|
pubkeylen = CFDataGetLength(publicKeyBits);
|
||||||
|
pubkey = CFDataGetBytePtr(publicKeyBits);
|
||||||
|
|
||||||
|
switch(pubkeylen) {
|
||||||
|
case 526:
|
||||||
|
/* 4096 bit RSA pubkeylen == 526 */
|
||||||
|
spkiHeader = rsa4096SpkiHeader;
|
||||||
|
break;
|
||||||
|
case 270:
|
||||||
|
/* 2048 bit RSA pubkeylen == 270 */
|
||||||
|
spkiHeader = rsa2048SpkiHeader;
|
||||||
|
break;
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
|
||||||
|
case 65:
|
||||||
|
/* ecDSA secp256r1 pubkeylen == 65 */
|
||||||
|
spkiHeader = ecDsaSecp256r1SpkiHeader;
|
||||||
|
spkiHeaderLength = 26;
|
||||||
|
break;
|
||||||
|
case 97:
|
||||||
|
/* ecDSA secp384r1 pubkeylen == 97 */
|
||||||
|
spkiHeader = ecDsaSecp384r1SpkiHeader;
|
||||||
|
spkiHeaderLength = 23;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
|
||||||
|
#elif DARWIN_SSL_PINNEDPUBKEY_V2
|
||||||
|
default:
|
||||||
|
/* ecDSA secp256r1 pubkeylen == 91 header already included?
|
||||||
|
* ecDSA secp384r1 header already included too
|
||||||
|
* we assume rest of algorithms do same, so do nothing
|
||||||
|
*/
|
||||||
|
result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey,
|
||||||
|
pubkeylen);
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
|
||||||
|
continue; /* break from loop */
|
||||||
|
}
|
||||||
|
|
||||||
|
realpubkeylen = pubkeylen + spkiHeaderLength;
|
||||||
|
realpubkey = malloc(realpubkeylen);
|
||||||
|
if(!realpubkey)
|
||||||
|
break;
|
||||||
|
|
||||||
|
memcpy(realpubkey, spkiHeader, spkiHeaderLength);
|
||||||
|
memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen);
|
||||||
|
|
||||||
|
result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey,
|
||||||
|
realpubkeylen);
|
||||||
|
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
Curl_safefree(realpubkey);
|
||||||
|
if(publicKeyBits != NULL)
|
||||||
|
CFRelease(publicKeyBits);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY */
|
||||||
|
|
||||||
static CURLcode
|
static CURLcode
|
||||||
darwinssl_connect_step2(struct connectdata *conn, int sockindex)
|
darwinssl_connect_step2(struct connectdata *conn, int sockindex)
|
||||||
{
|
{
|
||||||
@ -2102,6 +2238,17 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
|
|||||||
/* we have been connected fine, we're not waiting for anything else. */
|
/* we have been connected fine, we're not waiting for anything else. */
|
||||||
connssl->connecting_state = ssl_connect_3;
|
connssl->connecting_state = ssl_connect_3;
|
||||||
|
|
||||||
|
#ifdef DARWIN_SSL_PINNEDPUBKEY
|
||||||
|
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]) {
|
||||||
|
CURLcode result = pkp_pin_peer_pubkey(data, connssl->ssl_ctx,
|
||||||
|
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]);
|
||||||
|
if(result) {
|
||||||
|
failf(data, "SSL: public key does not match pinned public key!");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY */
|
||||||
|
|
||||||
/* Informational message */
|
/* Informational message */
|
||||||
(void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher);
|
(void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher);
|
||||||
(void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol);
|
(void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol);
|
||||||
@ -2573,6 +2720,15 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
|
|||||||
(void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum);
|
(void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curl_darwinssl_sha256sum(unsigned char *tmp, /* input */
|
||||||
|
size_t tmplen,
|
||||||
|
unsigned char *sha256sum, /* output */
|
||||||
|
size_t sha256len)
|
||||||
|
{
|
||||||
|
assert(sha256len >= SHA256_DIGEST_LENGTH);
|
||||||
|
(void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum);
|
||||||
|
}
|
||||||
|
|
||||||
bool Curl_darwinssl_false_start(void)
|
bool Curl_darwinssl_false_start(void)
|
||||||
{
|
{
|
||||||
#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
|
#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
|
||||||
|
@ -48,11 +48,34 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
|
|||||||
size_t tmplen,
|
size_t tmplen,
|
||||||
unsigned char *md5sum, /* output */
|
unsigned char *md5sum, /* output */
|
||||||
size_t md5len);
|
size_t md5len);
|
||||||
|
void Curl_darwinssl_sha256sum(unsigned char *tmp, /* input */
|
||||||
|
size_t tmplen,
|
||||||
|
unsigned char *sha256sum, /* output */
|
||||||
|
size_t sha256len);
|
||||||
bool Curl_darwinssl_false_start(void);
|
bool Curl_darwinssl_false_start(void);
|
||||||
|
|
||||||
/* Set the API backend definition to SecureTransport */
|
/* Set the API backend definition to SecureTransport */
|
||||||
#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL
|
#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL
|
||||||
|
|
||||||
|
/* pinned public key support tests */
|
||||||
|
|
||||||
|
/* version 1 supports macOS 10.12+ and iOS 10+ */
|
||||||
|
#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \
|
||||||
|
(!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
|
||||||
|
#define DARWIN_SSL_PINNEDPUBKEY_V1 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* version 2 supports MacOSX 10.7+ */
|
||||||
|
#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
|
||||||
|
#define DARWIN_SSL_PINNEDPUBKEY_V2 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DARWIN_SSL_PINNEDPUBKEY_V1) || defined(DARWIN_SSL_PINNEDPUBKEY_V2)
|
||||||
|
/* this backend supports CURLOPT_PINNEDPUBLICKEY */
|
||||||
|
#define DARWIN_SSL_PINNEDPUBKEY 1
|
||||||
|
#define have_curlssl_pinnedpubkey 1
|
||||||
|
#endif /* DARWIN_SSL_PINNEDPUBKEY */
|
||||||
|
|
||||||
/* API setup for SecureTransport */
|
/* API setup for SecureTransport */
|
||||||
#define curlssl_init() (1)
|
#define curlssl_init() (1)
|
||||||
#define curlssl_cleanup() Curl_nop_stmt
|
#define curlssl_cleanup() Curl_nop_stmt
|
||||||
@ -70,6 +93,7 @@ bool Curl_darwinssl_false_start(void);
|
|||||||
#define curlssl_data_pending(x,y) Curl_darwinssl_data_pending(x, y)
|
#define curlssl_data_pending(x,y) Curl_darwinssl_data_pending(x, y)
|
||||||
#define curlssl_random(x,y,z) ((void)x, Curl_darwinssl_random(y,z))
|
#define curlssl_random(x,y,z) ((void)x, Curl_darwinssl_random(y,z))
|
||||||
#define curlssl_md5sum(a,b,c,d) Curl_darwinssl_md5sum(a,b,c,d)
|
#define curlssl_md5sum(a,b,c,d) Curl_darwinssl_md5sum(a,b,c,d)
|
||||||
|
#define curlssl_sha256sum(a,b,c,d) Curl_darwinssl_sha256sum(a,b,c,d)
|
||||||
#define curlssl_false_start() Curl_darwinssl_false_start()
|
#define curlssl_false_start() Curl_darwinssl_false_start()
|
||||||
|
|
||||||
#endif /* USE_DARWINSSL */
|
#endif /* USE_DARWINSSL */
|
||||||
|
@ -2412,6 +2412,7 @@ sub checksystem {
|
|||||||
}
|
}
|
||||||
elsif ($libcurl =~ /securetransport/i) {
|
elsif ($libcurl =~ /securetransport/i) {
|
||||||
$has_darwinssl=1;
|
$has_darwinssl=1;
|
||||||
|
$has_sslpinning=1;
|
||||||
$ssllib="DarwinSSL";
|
$ssllib="DarwinSSL";
|
||||||
}
|
}
|
||||||
elsif ($libcurl =~ /BoringSSL/i) {
|
elsif ($libcurl =~ /BoringSSL/i) {
|
||||||
|
Loading…
Reference in New Issue
Block a user