|
113 | 113 | #define ioErr -36 |
114 | 114 | #define paramErr -50 |
115 | 115 |
|
| 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 | + |
116 | 146 | /* The following two functions were ripped from Apple sample code, |
117 | 147 | * with some modifications: */ |
118 | 148 | static OSStatus SocketRead(SSLConnectionRef connection, |
@@ -1996,6 +2026,112 @@ static int verify_cert(const char *cafile, struct Curl_easy *data, |
1996 | 2026 | } |
1997 | 2027 | } |
1998 | 2028 |
|
| 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 | + |
1999 | 2135 | static CURLcode |
2000 | 2136 | darwinssl_connect_step2(struct connectdata *conn, int sockindex) |
2001 | 2137 | { |
@@ -2102,6 +2238,17 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) |
2102 | 2238 | /* we have been connected fine, we're not waiting for anything else. */ |
2103 | 2239 | connssl->connecting_state = ssl_connect_3; |
2104 | 2240 |
|
| 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 | + |
2105 | 2252 | /* Informational message */ |
2106 | 2253 | (void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher); |
2107 | 2254 | (void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol); |
@@ -2573,6 +2720,15 @@ void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ |
2573 | 2720 | (void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum); |
2574 | 2721 | } |
2575 | 2722 |
|
| 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 | + |
2576 | 2732 | bool Curl_darwinssl_false_start(void) |
2577 | 2733 | { |
2578 | 2734 | #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
|
0 commit comments