| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 2012, Nick Zitzmann, <nickzman@gmail.com>. |
| * Copyright (C) 2012, 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 http://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. |
| * |
| ***************************************************************************/ |
| |
| /* |
| * Source file for all iOS and Mac OS X SecureTransport-specific code for the |
| * TLS/SSL layer. No code but sslgen.c should ever call or use these functions. |
| */ |
| |
| #include "setup.h" |
| |
| #ifdef USE_DARWINSSL |
| |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| #ifdef HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| |
| #include <Security/Security.h> |
| #include <Security/SecureTransport.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <CommonCrypto/CommonDigest.h> |
| |
| #include "urldata.h" |
| #include "sendf.h" |
| #include "inet_pton.h" |
| #include "connect.h" |
| #include "select.h" |
| #include "sslgen.h" |
| #include "curl_darwinssl.h" |
| |
| #define _MPRINTF_REPLACE /* use our functions only */ |
| #include <curl/mprintf.h> |
| |
| #include "curl_memory.h" |
| /* The last #include file should be: */ |
| #include "memdebug.h" |
| |
| /* 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, |
| void *data, /* owned by |
| * caller, data |
| * RETURNED */ |
| size_t *dataLength) /* IN/OUT */ |
| { |
| UInt32 bytesToGo = *dataLength; |
| UInt32 initLen = bytesToGo; |
| UInt8 *currData = (UInt8 *)data; |
| /*int sock = *(int *)connection;*/ |
| struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; |
| int sock = connssl->ssl_sockfd; |
| OSStatus rtn = noErr; |
| UInt32 bytesRead; |
| int rrtn; |
| int theErr; |
| |
| *dataLength = 0; |
| |
| for(;;) { |
| bytesRead = 0; |
| rrtn = read(sock, currData, bytesToGo); |
| if(rrtn <= 0) { |
| /* this is guesswork... */ |
| theErr = errno; |
| if((rrtn == 0) && (theErr == 0)) { |
| /* try fix for iSync */ |
| rtn = errSSLClosedGraceful; |
| } |
| else /* do the switch */ |
| switch(theErr) { |
| case ENOENT: |
| /* connection closed */ |
| rtn = errSSLClosedGraceful; |
| break; |
| case ECONNRESET: |
| rtn = errSSLClosedAbort; |
| break; |
| case EAGAIN: |
| rtn = errSSLWouldBlock; |
| connssl->ssl_direction = false; |
| break; |
| default: |
| rtn = ioErr; |
| break; |
| } |
| break; |
| } |
| else { |
| bytesRead = rrtn; |
| } |
| bytesToGo -= bytesRead; |
| currData += bytesRead; |
| |
| if(bytesToGo == 0) { |
| /* filled buffer with incoming data, done */ |
| break; |
| } |
| } |
| *dataLength = initLen - bytesToGo; |
| |
| return rtn; |
| } |
| |
| static OSStatus SocketWrite(SSLConnectionRef connection, |
| const void *data, |
| size_t *dataLength) /* IN/OUT */ |
| { |
| UInt32 bytesSent = 0; |
| /*int sock = *(int *)connection;*/ |
| struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection; |
| int sock = connssl->ssl_sockfd; |
| int length; |
| UInt32 dataLen = *dataLength; |
| const UInt8 *dataPtr = (UInt8 *)data; |
| OSStatus ortn; |
| int theErr; |
| |
| *dataLength = 0; |
| |
| do { |
| length = write(sock, |
| (char*)dataPtr + bytesSent, |
| dataLen - bytesSent); |
| } while((length > 0) && |
| ( (bytesSent += length) < dataLen) ); |
| |
| if(length <= 0) { |
| theErr = errno; |
| if(theErr == EAGAIN) { |
| ortn = errSSLWouldBlock; |
| connssl->ssl_direction = true; |
| } |
| else { |
| ortn = ioErr; |
| } |
| } |
| else { |
| ortn = noErr; |
| } |
| *dataLength = bytesSent; |
| return ortn; |
| } |
| |
| 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; |
| case SSL_RSA_WITH_NULL_SHA: |
| return "SSL_RSA_WITH_NULL_SHA"; |
| break; |
| case SSL_RSA_EXPORT_WITH_RC4_40_MD5: |
| return "SSL_RSA_EXPORT_WITH_RC4_40_MD5"; |
| break; |
| case SSL_RSA_WITH_RC4_128_MD5: |
| return "SSL_RSA_WITH_RC4_128_MD5"; |
| break; |
| case SSL_RSA_WITH_RC4_128_SHA: |
| return "SSL_RSA_WITH_RC4_128_SHA"; |
| break; |
| case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: |
| return "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5"; |
| break; |
| case SSL_RSA_WITH_IDEA_CBC_SHA: |
| return "SSL_RSA_WITH_IDEA_CBC_SHA"; |
| break; |
| case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_RSA_WITH_DES_CBC_SHA: |
| return "SSL_RSA_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_DH_DSS_WITH_DES_CBC_SHA: |
| return "SSL_DH_DSS_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_DH_RSA_WITH_DES_CBC_SHA: |
| return "SSL_DH_RSA_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_DHE_DSS_WITH_DES_CBC_SHA: |
| return "SSL_DHE_DSS_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_DHE_RSA_WITH_DES_CBC_SHA: |
| return "SSL_DHE_RSA_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: |
| return "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5"; |
| break; |
| case SSL_DH_anon_WITH_RC4_128_MD5: |
| return "SSL_DH_anon_WITH_RC4_128_MD5"; |
| break; |
| case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: |
| return "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA"; |
| break; |
| case SSL_DH_anon_WITH_DES_CBC_SHA: |
| return "SSL_DH_anon_WITH_DES_CBC_SHA"; |
| break; |
| case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: |
| return "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_FORTEZZA_DMS_WITH_NULL_SHA: |
| return "SSL_FORTEZZA_DMS_WITH_NULL_SHA"; |
| break; |
| 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; |
| case TLS_DH_DSS_WITH_AES_128_CBC_SHA: |
| return "TLS_DH_DSS_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_DH_RSA_WITH_AES_128_CBC_SHA: |
| return "TLS_DH_RSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: |
| return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: |
| return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_DH_anon_WITH_AES_128_CBC_SHA: |
| return "TLS_DH_anon_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_RSA_WITH_AES_256_CBC_SHA: |
| return "TLS_RSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_DH_DSS_WITH_AES_256_CBC_SHA: |
| return "TLS_DH_DSS_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_DH_RSA_WITH_AES_256_CBC_SHA: |
| return "TLS_DH_RSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: |
| return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: |
| return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"; |
| break; |
| 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; |
| case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: |
| return "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"; |
| break; |
| case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: |
| return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: |
| return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_ECDHE_ECDSA_WITH_NULL_SHA: |
| return "TLS_ECDHE_ECDSA_WITH_NULL_SHA"; |
| break; |
| case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: |
| return "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"; |
| break; |
| case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: |
| return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: |
| return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_ECDH_RSA_WITH_NULL_SHA: |
| return "TLS_ECDH_RSA_WITH_NULL_SHA"; |
| break; |
| case TLS_ECDH_RSA_WITH_RC4_128_SHA: |
| return "TLS_ECDH_RSA_WITH_RC4_128_SHA"; |
| break; |
| case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: |
| return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: |
| return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_ECDHE_RSA_WITH_NULL_SHA: |
| return "TLS_ECDHE_RSA_WITH_NULL_SHA"; |
| break; |
| case TLS_ECDHE_RSA_WITH_RC4_128_SHA: |
| return "TLS_ECDHE_RSA_WITH_RC4_128_SHA"; |
| break; |
| case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: |
| return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: |
| return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"; |
| break; |
| case TLS_ECDH_anon_WITH_NULL_SHA: |
| return "TLS_ECDH_anon_WITH_NULL_SHA"; |
| break; |
| case TLS_ECDH_anon_WITH_RC4_128_SHA: |
| return "TLS_ECDH_anon_WITH_RC4_128_SHA"; |
| break; |
| case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: |
| return "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"; |
| break; |
| case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: |
| return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"; |
| break; |
| #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 TLS_RSA_WITH_NULL_SHA: |
| return "TLS_RSA_WITH_NULL_SHA"; |
| break; |
| case TLS_RSA_WITH_RC4_128_MD5: |
| return "TLS_RSA_WITH_RC4_128_MD5"; |
| break; |
| 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; |
| #else |
| case SSL_RSA_WITH_NULL_MD5: |
| return "TLS_RSA_WITH_NULL_MD5"; |
| break; |
| case SSL_RSA_WITH_NULL_SHA: |
| return "TLS_RSA_WITH_NULL_SHA"; |
| break; |
| case SSL_RSA_WITH_RC4_128_MD5: |
| return "TLS_RSA_WITH_RC4_128_MD5"; |
| break; |
| case SSL_RSA_WITH_RC4_128_SHA: |
| return "TLS_RSA_WITH_RC4_128_SHA"; |
| break; |
| case SSL_RSA_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| case SSL_DH_anon_WITH_RC4_128_MD5: |
| return "TLS_DH_anon_WITH_RC4_128_MD5"; |
| break; |
| case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: |
| return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"; |
| break; |
| #endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ |
| } |
| return "TLS_NULL_WITH_NULL_NULL"; |
| } |
| |
| static CURLcode darwinssl_connect_step1(struct connectdata *conn, |
| int sockindex) |
| { |
| struct SessionHandle *data = conn->data; |
| curl_socket_t sockfd = conn->sock[sockindex]; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| bool sni = true; |
| #ifdef ENABLE_IPV6 |
| struct in6_addr addr; |
| #else |
| struct in_addr addr; |
| #endif |
| /*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 { |
| #if TARGET_OS_EMBEDDED == 0 /* the older API does not exist on iOS */ |
| if(connssl->ssl_ctx) |
| (void)SSLDisposeContext(connssl->ssl_ctx); |
| err = SSLNewContext(false, &(connssl->ssl_ctx)); |
| if(err != noErr) { |
| failf(data, "SSL: couldn't create a context: OSStatus %d", err); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| #endif /* TARGET_OS_EMBEDDED == 0 */ |
| } |
| #else |
| if(connssl->ssl_ctx) |
| (void)SSLDisposeContext(connssl->ssl_ctx); |
| err = SSLNewContext(false, &(connssl->ssl_ctx)); |
| if(err != noErr) { |
| 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) */ |
| |
| /* 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 TARGET_OS_EMBEDDED == 0 |
| (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 == 0 */ |
| } |
| #else |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false); |
| switch(data->set.ssl.version) { |
| default: |
| case CURL_SSLVERSION_DEFAULT: |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, |
| kSSLProtocol3, |
| true); |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, |
| kTLSProtocol1, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1: |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, |
| kTLSProtocol1, |
| true); |
| break; |
| case CURL_SSLVERSION_SSLv2: |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, |
| kSSLProtocol2, |
| true); |
| break; |
| case CURL_SSLVERSION_SSLv3: |
| (void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, |
| kSSLProtocol3, |
| 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. */ |
| |
| /* SSL always tries to verify the peer, this only says whether it should |
| * 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 { |
| #if TARGET_OS_EMBEDDED == 0 |
| 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 /* TARGET_OS_EMBEDDED == 0 */ |
| } |
| #else |
| 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 this is a domain name and not an IP address, then configure SNI: */ |
| if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) && |
| #ifdef ENABLE_IPV6 |
| (0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) && |
| #endif |
| sni) { |
| err = SSLSetPeerDomainName(connssl->ssl_ctx, conn->host.name, |
| strlen(conn->host.name)); |
| if(err != noErr) { |
| infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d", |
| err); |
| } |
| } |
| |
| err = SSLSetIOFuncs(connssl->ssl_ctx, SocketRead, SocketWrite); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| |
| /* pass the raw socket into the SSL layers */ |
| /* We need to store the FD in a constant memory address, because |
| * SSLSetConnection() will not copy that address. I've found that |
| * conn->sock[sockindex] may change on its own. */ |
| connssl->ssl_sockfd = sockfd; |
| /*ssl_connection = &(connssl->ssl_sockfd); |
| err = SSLSetConnection(connssl->ssl_ctx, ssl_connection);*/ |
| err = SSLSetConnection(connssl->ssl_ctx, connssl); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetConnection() failed: %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| |
| connssl->connecting_state = ssl_connect_2; |
| return CURLE_OK; |
| } |
| |
| static CURLcode |
| darwinssl_connect_step2(struct connectdata *conn, int sockindex) |
| { |
| struct SessionHandle *data = conn->data; |
| 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 |
| || ssl_connect_2_writing == connssl->connecting_state); |
| |
| /* Here goes nothing: */ |
| err = SSLHandshake(connssl->ssl_ctx); |
| |
| if(err != noErr) { |
| switch (err) { |
| case errSSLWouldBlock: /* they're not done with us yet */ |
| connssl->connecting_state = connssl->ssl_direction ? |
| ssl_connect_2_writing : ssl_connect_2_reading; |
| return CURLE_OK; |
| break; |
| |
| case errSSLServerAuthCompleted: |
| /* the documentation says we need to call SSLHandshake() again */ |
| return darwinssl_connect_step2(conn, sockindex); |
| |
| case errSSLXCertChainInvalid: |
| case errSSLUnknownRootCert: |
| case errSSLNoRootCert: |
| case errSSLCertExpired: |
| failf(data, "SSL certificate problem: OSStatus %d", err); |
| return CURLE_SSL_CACERT; |
| break; |
| |
| default: |
| failf(data, "Unknown SSL protocol error in connection to %s:%d", |
| conn->host.name, err); |
| return CURLE_SSL_CONNECT_ERROR; |
| break; |
| } |
| } |
| else { |
| /* we have been connected fine, we're not waiting for anything else. */ |
| connssl->connecting_state = ssl_connect_3; |
| |
| /* Informational message */ |
| (void)SSLGetNegotiatedCipher(connssl->ssl_ctx, &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; |
| } |
| } |
| |
| static CURLcode |
| darwinssl_connect_step3(struct connectdata *conn, |
| int sockindex) |
| { |
| struct SessionHandle *data = conn->data; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| CFStringRef server_cert_summary; |
| char server_cert_summary_c[128]; |
| CFArrayRef server_certs; |
| 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(SecTrustEvaluateAsync != 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 { |
| #if TARGET_OS_EMBEDDED == 0 |
| err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs); |
| if(err == noErr) { |
| count = CFArrayGetCount(server_certs); |
| for(i = 0L ; i < count ; i++) { |
| server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, |
| 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(server_certs); |
| } |
| #endif /* TARGET_OS_EMBEDDED == 0 */ |
| } |
| #else |
| #pragma unused(trust) |
| err = SSLCopyPeerCertificates(connssl->ssl_ctx, &server_certs); |
| if(err == noErr) { |
| count = CFArrayGetCount(server_certs); |
| for(i = 0L ; i < count ; i++) { |
| server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, 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(server_certs); |
| } |
| #endif /* defined(__MAC_10_7) || defined(__IPHONE_5_0) */ |
| |
| connssl->connecting_state = ssl_connect_done; |
| return CURLE_OK; |
| } |
| |
| static Curl_recv darwinssl_recv; |
| static Curl_send darwinssl_send; |
| |
| static CURLcode |
| darwinssl_connect_common(struct connectdata *conn, |
| int sockindex, |
| bool nonblocking, |
| bool *done) |
| { |
| CURLcode retcode; |
| struct SessionHandle *data = conn->data; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| curl_socket_t sockfd = conn->sock[sockindex]; |
| long timeout_ms; |
| int what; |
| |
| /* check if the connection has already been established */ |
| if(ssl_connection_complete == connssl->state) { |
| *done = TRUE; |
| return CURLE_OK; |
| } |
| |
| if(ssl_connect_1==connssl->connecting_state) { |
| /* Find out how much more time we're allowed */ |
| timeout_ms = Curl_timeleft(data, NULL, TRUE); |
| |
| if(timeout_ms < 0) { |
| /* no need to continue if time already is up */ |
| failf(data, "SSL connection timeout"); |
| return CURLE_OPERATION_TIMEDOUT; |
| } |
| retcode = darwinssl_connect_step1(conn, sockindex); |
| if(retcode) |
| return retcode; |
| } |
| |
| while(ssl_connect_2 == connssl->connecting_state || |
| ssl_connect_2_reading == connssl->connecting_state || |
| ssl_connect_2_writing == connssl->connecting_state) { |
| |
| /* check allowed time left */ |
| timeout_ms = Curl_timeleft(data, NULL, TRUE); |
| |
| if(timeout_ms < 0) { |
| /* no need to continue if time already is up */ |
| failf(data, "SSL connection timeout"); |
| return CURLE_OPERATION_TIMEDOUT; |
| } |
| |
| /* if ssl is expecting something, check if it's available. */ |
| if(connssl->connecting_state == ssl_connect_2_reading |
| || connssl->connecting_state == ssl_connect_2_writing) { |
| |
| curl_socket_t writefd = ssl_connect_2_writing == |
| connssl->connecting_state?sockfd:CURL_SOCKET_BAD; |
| curl_socket_t readfd = ssl_connect_2_reading == |
| connssl->connecting_state?sockfd:CURL_SOCKET_BAD; |
| |
| what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms); |
| if(what < 0) { |
| /* fatal error */ |
| failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| else if(0 == what) { |
| if(nonblocking) { |
| *done = FALSE; |
| return CURLE_OK; |
| } |
| else { |
| /* timeout */ |
| failf(data, "SSL connection timeout"); |
| return CURLE_OPERATION_TIMEDOUT; |
| } |
| } |
| /* socket is readable or writable */ |
| } |
| |
| /* Run transaction, and return to the caller if it failed or if this |
| * connection is done nonblocking and this loop would execute again. This |
| * permits the owner of a multi handle to abort a connection attempt |
| * before step2 has completed while ensuring that a client using select() |
| * or epoll() will always have a valid fdset to wait on. |
| */ |
| retcode = darwinssl_connect_step2(conn, sockindex); |
| if(retcode || (nonblocking && |
| (ssl_connect_2 == connssl->connecting_state || |
| ssl_connect_2_reading == connssl->connecting_state || |
| ssl_connect_2_writing == connssl->connecting_state))) |
| return retcode; |
| |
| } /* repeat step2 until all transactions are done. */ |
| |
| |
| if(ssl_connect_3==connssl->connecting_state) { |
| retcode = darwinssl_connect_step3(conn, sockindex); |
| if(retcode) |
| return retcode; |
| } |
| |
| if(ssl_connect_done==connssl->connecting_state) { |
| connssl->state = ssl_connection_complete; |
| conn->recv[sockindex] = darwinssl_recv; |
| conn->send[sockindex] = darwinssl_send; |
| *done = TRUE; |
| } |
| else |
| *done = FALSE; |
| |
| /* Reset our connect state machine */ |
| connssl->connecting_state = ssl_connect_1; |
| |
| return CURLE_OK; |
| } |
| |
| CURLcode |
| Curl_darwinssl_connect_nonblocking(struct connectdata *conn, |
| int sockindex, |
| bool *done) |
| { |
| return darwinssl_connect_common(conn, sockindex, TRUE, done); |
| } |
| |
| CURLcode |
| Curl_darwinssl_connect(struct connectdata *conn, |
| int sockindex) |
| { |
| CURLcode retcode; |
| bool done = FALSE; |
| |
| retcode = darwinssl_connect_common(conn, sockindex, FALSE, &done); |
| |
| if(retcode) |
| return retcode; |
| |
| DEBUGASSERT(done); |
| |
| return CURLE_OK; |
| } |
| |
| void Curl_darwinssl_close(struct connectdata *conn, int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| |
| 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 TARGET_OS_EMBEDDED == 0 |
| else |
| (void)SSLDisposeContext(connssl->ssl_ctx); |
| #endif /* TARGET_OS_EMBEDDED == 0 */ |
| #else |
| (void)SSLDisposeContext(connssl->ssl_ctx); |
| #endif /* defined(__MAC_10_8) || defined(__IPHONE_5_0) */ |
| connssl->ssl_ctx = NULL; |
| } |
| connssl->ssl_sockfd = 0; |
| } |
| |
| void Curl_darwinssl_close_all(struct SessionHandle *data) |
| { |
| /* SecureTransport doesn't separate sessions from contexts, so... */ |
| (void)data; |
| } |
| |
| int Curl_darwinssl_shutdown(struct connectdata *conn, int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct SessionHandle *data = conn->data; |
| ssize_t nread; |
| int what; |
| int rc; |
| char buf[120]; |
| |
| if(!connssl->ssl_ctx) |
| return 0; |
| |
| if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) |
| return 0; |
| |
| Curl_darwinssl_close(conn, sockindex); |
| |
| rc = 0; |
| |
| what = Curl_socket_ready(conn->sock[sockindex], |
| CURL_SOCKET_BAD, SSL_SHUTDOWN_TIMEOUT); |
| |
| for(;;) { |
| if(what < 0) { |
| /* anything that gets here is fatally bad */ |
| failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); |
| rc = -1; |
| break; |
| } |
| |
| if(!what) { /* timeout */ |
| failf(data, "SSL shutdown timeout"); |
| break; |
| } |
| |
| /* Something to read, let's do it and hope that it is the close |
| notify alert from the server. No way to SSL_Read now, so use read(). */ |
| |
| nread = read(conn->sock[sockindex], buf, sizeof(buf)); |
| |
| if(nread < 0) { |
| failf(data, "read: %s", strerror(errno)); |
| rc = -1; |
| } |
| |
| if(nread <= 0) |
| break; |
| |
| what = Curl_socket_ready(conn->sock[sockindex], CURL_SOCKET_BAD, 0); |
| } |
| |
| return rc; |
| } |
| |
| size_t Curl_darwinssl_version(char *buffer, size_t size) |
| { |
| return snprintf(buffer, size, "SecureTransport"); |
| } |
| |
| /* |
| * This function uses SSLGetSessionState to determine connection status. |
| * |
| * Return codes: |
| * 1 means the connection is still in place |
| * 0 means the connection has been closed |
| * -1 means the connection status is unknown |
| */ |
| int Curl_darwinssl_check_cxn(struct connectdata *conn) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; |
| OSStatus err; |
| SSLSessionState state; |
| |
| if(connssl->ssl_ctx) { |
| err = SSLGetSessionState(connssl->ssl_ctx, &state); |
| if(err == noErr) |
| return state == kSSLConnected || state == kSSLHandshake; |
| return -1; |
| } |
| return 0; |
| } |
| |
| bool Curl_darwinssl_data_pending(const struct connectdata *conn, |
| int connindex) |
| { |
| const struct ssl_connect_data *connssl = &conn->ssl[connindex]; |
| OSStatus err; |
| size_t buffer; |
| |
| if(connssl->ssl_ctx) { /* SSL is in use */ |
| err = SSLGetBufferedReadSize(connssl->ssl_ctx, &buffer); |
| if(err == noErr) |
| return buffer > 0UL; |
| return false; |
| } |
| else |
| return false; |
| } |
| |
| void Curl_darwinssl_random(struct SessionHandle *data, |
| unsigned char *entropy, |
| size_t length) |
| { |
| /* 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_number = 0; |
| |
| for(i = 0 ; i < length ; i++) { |
| if(i % sizeof(u_int32_t) == 0) |
| random_number = arc4random(); |
| entropy[i] = random_number & 0xFF; |
| random_number >>= 8; |
| } |
| i = random_number = 0; |
| (void)data; |
| } |
| |
| void Curl_darwinssl_md5sum(unsigned char *tmp, /* input */ |
| size_t tmplen, |
| unsigned char *md5sum, /* output */ |
| size_t md5len) |
| { |
| (void)md5len; |
| (void)CC_MD5(tmp, tmplen, md5sum); |
| } |
| |
| static ssize_t darwinssl_send(struct connectdata *conn, |
| int sockindex, |
| const void *mem, |
| size_t len, |
| CURLcode *curlcode) |
| { |
| /*struct SessionHandle *data = conn->data;*/ |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| size_t processed; |
| OSStatus err = SSLWrite(connssl->ssl_ctx, mem, len, &processed); |
| |
| if(err != noErr) { |
| switch (err) { |
| case errSSLWouldBlock: /* we're not done yet; keep sending */ |
| *curlcode = CURLE_AGAIN; |
| return -1; |
| break; |
| |
| default: |
| failf(conn->data, "SSLWrite() return error %d", err); |
| *curlcode = CURLE_SEND_ERROR; |
| return -1; |
| break; |
| } |
| } |
| return (ssize_t)processed; |
| } |
| |
| static ssize_t darwinssl_recv(struct connectdata *conn, |
| int num, |
| char *buf, |
| size_t buffersize, |
| CURLcode *curlcode) |
| { |
| /*struct SessionHandle *data = conn->data;*/ |
| struct ssl_connect_data *connssl = &conn->ssl[num]; |
| size_t processed; |
| OSStatus err = SSLRead(connssl->ssl_ctx, buf, buffersize, &processed); |
| |
| if(err != noErr) { |
| switch (err) { |
| case errSSLWouldBlock: /* we're not done yet; keep reading */ |
| *curlcode = CURLE_AGAIN; |
| return -1; |
| break; |
| |
| default: |
| failf(conn->data, "SSLRead() return error %d", err); |
| *curlcode = CURLE_RECV_ERROR; |
| return -1; |
| break; |
| } |
| } |
| return (ssize_t)processed; |
| } |
| |
| #endif /* USE_DARWINSSL */ |