X Tutup
Skip to content

Commit eb16305

Browse files
moparisthebestbagder
authored andcommitted
SecureTransport/DarwinSSL: Implement public key pinning
Closes #1400
1 parent 1919569 commit eb16305

File tree

4 files changed

+186
-1
lines changed

4 files changed

+186
-1
lines changed

docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.3

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
.\" * | (__| |_| | _ <| |___
66
.\" * \___|\___/|_| \_\_____|
77
.\" *
8-
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
8+
.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
99
.\" *
1010
.\" * This software is licensed as described in the file COPYING, which
1111
.\" * you should have received as part of this distribution. The terms
@@ -103,6 +103,8 @@ PEM/DER support:
103103

104104
7.49.0: PolarSSL
105105

106+
7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+
107+
106108
sha256 support:
107109

108110
7.44.0: OpenSSL, GnuTLS, NSS and wolfSSL/CyaSSL
@@ -111,6 +113,8 @@ sha256 support:
111113

112114
7.49.0: PolarSSL
113115

116+
7.54.1: SecureTransport/DarwinSSL on macOS 10.7+/iOS 10+
117+
114118
Other SSL backends not supported.
115119
.SH RETURN VALUE
116120
Returns CURLE_OK if TLS enabled, CURLE_UNKNOWN_OPTION if not, or

lib/vtls/darwinssl.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,36 @@
113113
#define ioErr -36
114114
#define paramErr -50
115115

116+
#ifdef DARWIN_SSL_PINNEDPUBKEY
117+
/* both new and old APIs return rsa keys missing the spki header (not DER) */
118+
static const unsigned char rsa4096SpkiHeader[] = {
119+
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
120+
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
121+
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
122+
0x00, 0x03, 0x82, 0x02, 0x0f, 0x00};
123+
124+
static const unsigned char rsa2048SpkiHeader[] = {
125+
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
126+
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
127+
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
128+
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
129+
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
130+
/* the *new* version doesn't return DER encoded ecdsa certs like the old... */
131+
static const unsigned char ecDsaSecp256r1SpkiHeader[] = {
132+
0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
133+
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
134+
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
135+
0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
136+
0x42, 0x00};
137+
138+
static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
139+
0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
140+
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
141+
0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
142+
0x00, 0x22, 0x03, 0x62, 0x00};
143+
#endif /* DARWIN_SSL_PINNEDPUBKEY_V1 */
144+
#endif /* DARWIN_SSL_PINNEDPUBKEY */
145+
116146
/* The following two functions were ripped from Apple sample code,
117147
* with some modifications: */
118148
static OSStatus SocketRead(SSLConnectionRef connection,
@@ -1996,6 +2026,112 @@ static int verify_cert(const char *cafile, struct Curl_easy *data,
19962026
}
19972027
}
19982028

2029+
#ifdef DARWIN_SSL_PINNEDPUBKEY
2030+
static CURLcode pkp_pin_peer_pubkey(struct SessionHandle *data,
2031+
SSLContextRef ctx,
2032+
const char *pinnedpubkey)
2033+
{ /* Scratch */
2034+
size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
2035+
unsigned char *pubkey = NULL, *realpubkey = NULL, *spkiHeader = NULL;
2036+
CFDataRef publicKeyBits = NULL;
2037+
2038+
/* Result is returned to caller */
2039+
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
2040+
2041+
/* if a path wasn't specified, don't pin */
2042+
if(!pinnedpubkey)
2043+
return CURLE_OK;
2044+
2045+
2046+
if(!ctx)
2047+
return result;
2048+
2049+
do {
2050+
SecTrustRef trust;
2051+
OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
2052+
if(ret != noErr || trust == NULL)
2053+
break;
2054+
2055+
SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
2056+
CFRelease(trust);
2057+
if(keyRef == NULL)
2058+
break;
2059+
2060+
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
2061+
2062+
publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL);
2063+
CFRelease(keyRef);
2064+
if(publicKeyBits == NULL)
2065+
break;
2066+
2067+
#elif DARWIN_SSL_PINNEDPUBKEY_V2
2068+
2069+
OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
2070+
&publicKeyBits);
2071+
CFRelease(keyRef);
2072+
if(success != errSecSuccess || publicKeyBits == NULL)
2073+
break;
2074+
2075+
#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
2076+
2077+
pubkeylen = CFDataGetLength(publicKeyBits);
2078+
pubkey = CFDataGetBytePtr(publicKeyBits);
2079+
2080+
switch(pubkeylen) {
2081+
case 526:
2082+
/* 4096 bit RSA pubkeylen == 526 */
2083+
spkiHeader = rsa4096SpkiHeader;
2084+
break;
2085+
case 270:
2086+
/* 2048 bit RSA pubkeylen == 270 */
2087+
spkiHeader = rsa2048SpkiHeader;
2088+
break;
2089+
#ifdef DARWIN_SSL_PINNEDPUBKEY_V1
2090+
case 65:
2091+
/* ecDSA secp256r1 pubkeylen == 65 */
2092+
spkiHeader = ecDsaSecp256r1SpkiHeader;
2093+
spkiHeaderLength = 26;
2094+
break;
2095+
case 97:
2096+
/* ecDSA secp384r1 pubkeylen == 97 */
2097+
spkiHeader = ecDsaSecp384r1SpkiHeader;
2098+
spkiHeaderLength = 23;
2099+
break;
2100+
default:
2101+
infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
2102+
#elif DARWIN_SSL_PINNEDPUBKEY_V2
2103+
default:
2104+
/* ecDSA secp256r1 pubkeylen == 91 header already included?
2105+
* ecDSA secp384r1 header already included too
2106+
* we assume rest of algorithms do same, so do nothing
2107+
*/
2108+
result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey,
2109+
pubkeylen);
2110+
#endif /* DARWIN_SSL_PINNEDPUBKEY_V2 */
2111+
continue; /* break from loop */
2112+
}
2113+
2114+
realpubkeylen = pubkeylen + spkiHeaderLength;
2115+
realpubkey = malloc(realpubkeylen);
2116+
if(!realpubkey)
2117+
break;
2118+
2119+
memcpy(realpubkey, spkiHeader, spkiHeaderLength);
2120+
memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen);
2121+
2122+
result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey,
2123+
realpubkeylen);
2124+
2125+
} while(0);
2126+
2127+
Curl_safefree(realpubkey);
2128+
if(publicKeyBits != NULL)
2129+
CFRelease(publicKeyBits);
2130+
2131+
return result;
2132+
}
2133+
#endif /* DARWIN_SSL_PINNEDPUBKEY */
2134+
19992135
static CURLcode
20002136
darwinssl_connect_step2(struct connectdata *conn, int sockindex)
20012137
{
@@ -2102,6 +2238,17 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex)
21022238
/* we have been connected fine, we're not waiting for anything else. */
21032239
connssl->connecting_state = ssl_connect_3;
21042240

2241+
#ifdef DARWIN_SSL_PINNEDPUBKEY
2242+
if(data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]) {
2243+
CURLcode result = pkp_pin_peer_pubkey(data, connssl->ssl_ctx,
2244+
data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]);
2245+
if(result) {
2246+
failf(data, "SSL: public key does not match pinned public key!");
2247+
return result;
2248+
}
2249+
}
2250+
#endif /* DARWIN_SSL_PINNEDPUBKEY */
2251+
21052252
/* Informational message */
21062253
(void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher);
21072254
(void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol);
@@ -2573,6 +2720,15 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
25732720
(void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum);
25742721
}
25752722

2723+
void Curl_darwinssl_sha256sum(unsigned char *tmp, /* input */
2724+
size_t tmplen,
2725+
unsigned char *sha256sum, /* output */
2726+
size_t sha256len)
2727+
{
2728+
assert(sha256len >= SHA256_DIGEST_LENGTH);
2729+
(void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum);
2730+
}
2731+
25762732
bool Curl_darwinssl_false_start(void)
25772733
{
25782734
#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7

lib/vtls/darwinssl.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,34 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */
4848
size_t tmplen,
4949
unsigned char *md5sum, /* output */
5050
size_t md5len);
51+
void Curl_darwinssl_sha256sum(unsigned char *tmp, /* input */
52+
size_t tmplen,
53+
unsigned char *sha256sum, /* output */
54+
size_t sha256len);
5155
bool Curl_darwinssl_false_start(void);
5256

5357
/* Set the API backend definition to SecureTransport */
5458
#define CURL_SSL_BACKEND CURLSSLBACKEND_DARWINSSL
5559

60+
/* pinned public key support tests */
61+
62+
/* version 1 supports macOS 10.12+ and iOS 10+ */
63+
#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \
64+
(!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
65+
#define DARWIN_SSL_PINNEDPUBKEY_V1 1
66+
#endif
67+
68+
/* version 2 supports MacOSX 10.7+ */
69+
#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
70+
#define DARWIN_SSL_PINNEDPUBKEY_V2 1
71+
#endif
72+
73+
#if defined(DARWIN_SSL_PINNEDPUBKEY_V1) || defined(DARWIN_SSL_PINNEDPUBKEY_V2)
74+
/* this backend supports CURLOPT_PINNEDPUBLICKEY */
75+
#define DARWIN_SSL_PINNEDPUBKEY 1
76+
#define have_curlssl_pinnedpubkey 1
77+
#endif /* DARWIN_SSL_PINNEDPUBKEY */
78+
5679
/* API setup for SecureTransport */
5780
#define curlssl_init() (1)
5881
#define curlssl_cleanup() Curl_nop_stmt
@@ -70,6 +93,7 @@ bool Curl_darwinssl_false_start(void);
7093
#define curlssl_data_pending(x,y) Curl_darwinssl_data_pending(x, y)
7194
#define curlssl_random(x,y,z) ((void)x, Curl_darwinssl_random(y,z))
7295
#define curlssl_md5sum(a,b,c,d) Curl_darwinssl_md5sum(a,b,c,d)
96+
#define curlssl_sha256sum(a,b,c,d) Curl_darwinssl_sha256sum(a,b,c,d)
7397
#define curlssl_false_start() Curl_darwinssl_false_start()
7498

7599
#endif /* USE_DARWINSSL */

tests/runtests.pl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,6 +2412,7 @@ sub checksystem {
24122412
}
24132413
elsif ($libcurl =~ /securetransport/i) {
24142414
$has_darwinssl=1;
2415+
$has_sslpinning=1;
24152416
$ssllib="DarwinSSL";
24162417
}
24172418
elsif ($libcurl =~ /BoringSSL/i) {

0 commit comments

Comments
 (0)
X Tutup