diff --git a/lib/curl_darwinssl.c b/lib/curl_darwinssl.c index c848be746..1626b57e2 100644 --- a/lib/curl_darwinssl.c +++ b/lib/curl_darwinssl.c @@ -60,6 +60,16 @@ /* From MacTypes.h (which we can't include because it isn't present in iOS: */ #define ioErr -36 +/* In Mountain Lion and iOS 5, Apple made some changes to the API. They + added TLS 1.1 and 1.2 support, and deprecated and replaced some + functions. You need to build against the Mountain Lion or iOS 5 SDK + or later to get TLS 1.1 or 1.2 support working in cURL. We'll weak-link + to the newer functions and use them if present in the user's OS. + + Builders: If you want TLS 1.1 and 1.2 but still want to retain support + for older cats, don't forget to set the MACOSX_DEPLOYMENT_TARGET + environmental variable prior to building cURL. */ + /* The following two functions were ripped from Apple sample code, * with some modifications: */ static OSStatus SocketRead(SSLConnectionRef connection, @@ -166,8 +176,9 @@ static OSStatus SocketWrite(SSLConnectionRef connection, return ortn; } -CF_INLINE const char *CipherNameForNumber(SSLCipherSuite cipher) { +CF_INLINE const char *SSLCipherNameForNumber(SSLCipherSuite cipher) { switch (cipher) { + /* SSL version 3.0 */ case SSL_RSA_WITH_NULL_MD5: return "SSL_RSA_WITH_NULL_MD5"; break; @@ -255,6 +266,26 @@ CF_INLINE const char *CipherNameForNumber(SSLCipherSuite cipher) { case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: return "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"; break; + /* SSL version 2.0 */ + case SSL_RSA_WITH_RC2_CBC_MD5: + return "SSL_RSA_WITH_RC2_CBC_MD5"; + break; + case SSL_RSA_WITH_IDEA_CBC_MD5: + return "SSL_RSA_WITH_IDEA_CBC_MD5"; + break; + case SSL_RSA_WITH_DES_CBC_MD5: + return "SSL_RSA_WITH_DES_CBC_MD5"; + break; + case SSL_RSA_WITH_3DES_EDE_CBC_MD5: + return "SSL_RSA_WITH_3DES_EDE_CBC_MD5"; + break; + } + return "SSL_NULL_WITH_NULL_NULL"; +} + +CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) { + switch(cipher) { + /* TLS 1.0 with AES (RFC 3268) */ case TLS_RSA_WITH_AES_128_CBC_SHA: return "TLS_RSA_WITH_AES_128_CBC_SHA"; break; @@ -291,6 +322,7 @@ CF_INLINE const char *CipherNameForNumber(SSLCipherSuite cipher) { case TLS_DH_anon_WITH_AES_256_CBC_SHA: return "TLS_DH_anon_WITH_AES_256_CBC_SHA"; break; + /* TLS 1.0 with ECDSA (RFC 4492) */ case TLS_ECDH_ECDSA_WITH_NULL_SHA: return "TLS_ECDH_ECDSA_WITH_NULL_SHA"; break; @@ -366,20 +398,172 @@ CF_INLINE const char *CipherNameForNumber(SSLCipherSuite cipher) { case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; break; - case SSL_RSA_WITH_RC2_CBC_MD5: - return "SSL_RSA_WITH_RC2_CBC_MD5"; +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + /* TLS 1.2 (RFC 5246) */ + case TLS_RSA_WITH_NULL_MD5: + return "TLS_RSA_WITH_NULL_MD5"; break; - case SSL_RSA_WITH_IDEA_CBC_MD5: - return "SSL_RSA_WITH_IDEA_CBC_MD5"; + case TLS_RSA_WITH_NULL_SHA: + return "TLS_RSA_WITH_NULL_SHA"; break; - case SSL_RSA_WITH_DES_CBC_MD5: - return "SSL_RSA_WITH_DES_CBC_MD5"; + case TLS_RSA_WITH_RC4_128_MD5: + return "TLS_RSA_WITH_RC4_128_MD5"; break; - case SSL_RSA_WITH_3DES_EDE_CBC_MD5: - return "SSL_RSA_WITH_3DES_EDE_CBC_MD5"; + case TLS_RSA_WITH_RC4_128_SHA: + return "TLS_RSA_WITH_RC4_128_SHA"; break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_RSA_WITH_NULL_SHA256: + return "TLS_RSA_WITH_NULL_SHA256"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + return "TLS_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + return "TLS_RSA_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + return "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + return "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + return "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + return "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + return "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + return "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + return "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + return "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + return "TLS_DH_anon_WITH_RC4_128_MD5"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + return "TLS_DH_anon_WITH_AES_128_CBC_SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + return "TLS_DH_anon_WITH_AES_256_CBC_SHA256"; + break; + /* TLS 1.2 with AES GCM (RFC 5288) */ + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return "TLS_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return "TLS_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + return "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + return "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + return "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + return "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + return "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + return "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + return "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"; + break; + case TLS_DH_anon_WITH_AES_128_GCM_SHA256: + return "TLS_DH_anon_WITH_AES_128_GCM_SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_GCM_SHA384: + return "TLS_DH_anon_WITH_AES_256_GCM_SHA384"; + break; + /* TLS 1.2 with elliptic curve ciphers (RFC 5289) */ + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"; + break; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + return "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + return "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + return "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"; + break; + case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + return "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"; + break; + case TLS_EMPTY_RENEGOTIATION_INFO_SCSV: + return "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; + break; +#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ } - return "(NONE)"; + return "TLS_NULL_WITH_NULL_NULL"; } static CURLcode darwinssl_connect_step1(struct connectdata *conn, @@ -397,6 +581,18 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, /*SSLConnectionRef ssl_connection;*/ OSStatus err = noErr; +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + if(SSLCreateContext != NULL) { /* use the newer API if avaialble */ + if(connssl->ssl_ctx) + CFRelease(connssl->ssl_ctx); + connssl->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); + if(!connssl->ssl_ctx) { + failf(data, "SSL: couldn't create a context!"); + return CURLE_OUT_OF_MEMORY; + } + } + else { +#elif !defined(TARGET_OS_EMBEDDED) if(connssl->ssl_ctx) (void)SSLDisposeContext(connssl->ssl_ctx); err = SSLNewContext(false, &(connssl->ssl_ctx)); @@ -404,8 +600,77 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, failf(data, "SSL: couldn't create a context: OSStatus %d", err); return CURLE_OUT_OF_MEMORY; } +#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + } +#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ /* check to see if we've been told to use an explicit SSL/TLS version */ +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + if(SSLSetProtocolVersionMax != NULL) { + switch(data->set.ssl.version) { + case CURL_SSLVERSION_DEFAULT: default: + (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3); + (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12); + break; + case CURL_SSLVERSION_TLSv1: + (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kTLSProtocol1); + (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kTLSProtocol12); + break; + case CURL_SSLVERSION_SSLv3: + (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol3); + (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol3); + break; + case CURL_SSLVERSION_SSLv2: + (void)SSLSetProtocolVersionMin(connssl->ssl_ctx, kSSLProtocol2); + (void)SSLSetProtocolVersionMax(connssl->ssl_ctx, kSSLProtocol2); + } + } + else { +#if !defined(TARGET_OS_EMBEDDED) + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kSSLProtocolAll, + false); + switch (data->set.ssl.version) { + case CURL_SSLVERSION_DEFAULT: default: + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kSSLProtocol3, + true); + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol1, + true); + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol11, + true); + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol12, + true); + break; + case CURL_SSLVERSION_TLSv1: + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol1, + true); + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol11, + true); + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kTLSProtocol12, + true); + break; + case CURL_SSLVERSION_SSLv3: + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kSSLProtocol3, + true); + break; + case CURL_SSLVERSION_SSLv2: + (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, + kSSLProtocol2, + true); + break; + } +#endif /* TARGET_OS_EMBEDDED */ + } +#else (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false); switch(data->set.ssl.version) { default: @@ -433,6 +698,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, true); break; } +#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ /* No need to load certificates here. SecureTransport uses the Keychain * (which is also part of the Security framework) to evaluate trust. */ @@ -441,12 +707,28 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn, * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ +#if defined(__MAC_10_6) || defined(__IPHONE_5_0) + if(SSLSetSessionOption != NULL) { + err = SSLSetSessionOption(connssl->ssl_ctx, + kSSLSessionOptionBreakOnServerAuth, + data->set.ssl.verifypeer?false:true); + if(err != noErr) { + failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err); + return CURLE_SSL_CONNECT_ERROR; + } + } + else { +#elif !defined(TARGET_OS_EMBEDDED) err = SSLSetEnableCertVerify(connssl->ssl_ctx, data->set.ssl.verifypeer?true:false); if(err != noErr) { failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); return CURLE_SSL_CONNECT_ERROR; } +#endif /* defined(__MAC_10_6) || defined(__IPHONE_5_0) */ +#if defined(__MAC_10_6) || defined(__IPHONE_5_0) + } +#endif /* defined(__MAC_10_6) || defined(__IPHONE_5_0) */ /* If this is a domain name and not an IP address, then configure SNI: */ if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) && @@ -492,6 +774,7 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; OSStatus err; SSLCipherSuite cipher; + SSLProtocol protocol = 0; DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state @@ -533,7 +816,34 @@ darwinssl_connect_step2(struct connectdata *conn, int sockindex) /* Informational message */ (void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &cipher); - infof (data, "SSL connection using %s\n", CipherNameForNumber(cipher)); + (void)SSLGetNegotiatedProtocolVersion(connssl->ssl_ctx, &protocol); + switch (protocol) { + case kSSLProtocol2: + infof(data, "SSL 2.0 connection using %s\n", + SSLCipherNameForNumber(cipher)); + break; + case kSSLProtocol3: + infof(data, "SSL 3.0 connection using %s\n", + SSLCipherNameForNumber(cipher)); + break; + case kTLSProtocol1: + infof(data, "TLS 1.0 connection using %s\n", + TLSCipherNameForNumber(cipher)); + break; +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + case kTLSProtocol11: + infof(data, "TLS 1.1 connection using %s\n", + TLSCipherNameForNumber(cipher)); + break; + case kTLSProtocol12: + infof(data, "TLS 1.2 connection using %s\n", + TLSCipherNameForNumber(cipher)); + break; +#endif + default: + infof(data, "Unknown protocol connection\n"); + break; + } return CURLE_OK; } @@ -551,10 +861,35 @@ darwinssl_connect_step3(struct connectdata *conn, SecCertificateRef server_cert; OSStatus err; CFIndex i, count; + SecTrustRef trust; /* There is no step 3! * Well, okay, if verbose mode is on, let's print the details of the * server certificates. */ +#if defined(__MAC_10_7) || defined(__IPHONE_5_0) + if(SecTrustGetCertificateCount != NULL) { +#pragma unused(server_certs) + err = SSLCopyPeerTrust(connssl->ssl_ctx, &trust); + if(err == noErr) { + count = SecTrustGetCertificateCount(trust); + for(i = 0L ; i < count ; i++) { + server_cert = SecTrustGetCertificateAtIndex(trust, i); + server_cert_summary = SecCertificateCopySubjectSummary(server_cert); + memset(server_cert_summary_c, 0, 128); + if(CFStringGetCString(server_cert_summary, + server_cert_summary_c, + 128, + kCFStringEncodingUTF8)) { + infof(data, "Server certificate: %s\n", server_cert_summary_c); + } + CFRelease(server_cert_summary); + } + CFRelease(trust); + } + } + else { +#elif !defined(TARGET_OS_EMBEDDED) +#pragma unused(trust) err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs); if(err == noErr) { count = CFArrayGetCount(server_certs); @@ -573,6 +908,10 @@ darwinssl_connect_step3(struct connectdata *conn, } CFRelease(server_certs); } +#endif /* defined(__MAC_10_7) || defined(__IPHONE_5_0) */ +#if defined(__MAC_10_7) || defined(__IPHONE_5_0) + } +#endif /* defined(__MAC_10_7) || defined(__IPHONE_5_0) */ connssl->connecting_state = ssl_connect_done; return CURLE_OK; @@ -722,9 +1061,20 @@ void Curl_darwinssl_close(struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - (void)SSLClose(connssl->ssl_ctx); - (void)SSLDisposeContext(connssl->ssl_ctx); - connssl->ssl_ctx = NULL; + if(connssl->ssl_ctx) { + (void)SSLClose(connssl->ssl_ctx); +#if defined(__MAC_10_8) || defined(__IPHONE_5_0) + if(SSLCreateContext != NULL) + CFRelease(connssl->ssl_ctx); +#if !defined(TARGET_OS_EMBEDDED) + else + (void)SSLDisposeContext(connssl->ssl_ctx); +#endif /* !defined(TARGET_OS_EMBEDDED */ +#else + (void)SSLDisposeContext(connssl->ssl_ctx); +#endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ + connssl->ssl_ctx = NULL; + } connssl->ssl_sockfd = 0; } @@ -840,15 +1190,15 @@ void Curl_darwinssl_random(struct SessionHandle *data, /* arc4random_buf() isn't available on cats older than Lion, so let's do this manually for the benefit of the older cats. */ size_t i; - u_int32_t random = 0; + u_int32_t random_number = 0; for(i = 0 ; i < length ; i++) { if(i % sizeof(u_int32_t) == 0) - random = arc4random(); - entropy[i] = random & 0xFF; - random >>= 8; + random_number = arc4random(); + entropy[i] = random_number & 0xFF; + random_number >>= 8; } - i = random = 0; + i = random_number = 0; (void)data; }