curl/lib/curl_darwinssl.c

1529 lines
51 KiB
C
Raw Normal View History

/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012-2013, Nick Zitzmann, <nickzman@gmail.com>.
* Copyright (C) 2012-2013, 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.
*/
build: fix circular header inclusion with other packages This commit renames lib/setup.h to lib/curl_setup.h and renames lib/setup_once.h to lib/curl_setup_once.h. Removes the need and usage of a header inclusion guard foreign to libcurl. [1] Removes the need and presence of an alarming notice we carried in old setup_once.h [2] ---------------------------------------- 1 - lib/setup_once.h used __SETUP_ONCE_H macro as header inclusion guard up to commit ec691ca3 which changed this to HEADER_CURL_SETUP_ONCE_H, this single inclusion guard is enough to ensure that inclusion of lib/setup_once.h done from lib/setup.h is only done once. Additionally lib/setup.h has always used __SETUP_ONCE_H macro to protect inclusion of setup_once.h even after commit ec691ca3, this was to avoid a circular header inclusion triggered when building a c-ares enabled version with c-ares sources available which also has a setup_once.h header. Commit ec691ca3 exposes the real nature of __SETUP_ONCE_H usage in lib/setup.h, it is a header inclusion guard foreign to libcurl belonging to c-ares's setup_once.h The renaming this commit does, fixes the circular header inclusion, and as such removes the need and usage of a header inclusion guard foreign to libcurl. Macro __SETUP_ONCE_H no longer used in libcurl. 2 - Due to the circular interdependency of old lib/setup_once.h and the c-ares setup_once.h header, old file lib/setup_once.h has carried back from 2006 up to now days an alarming and prominent notice about the need of keeping libcurl's and c-ares's setup_once.h in sync. Given that this commit fixes the circular interdependency, the need and presence of mentioned notice is removed. All mentioned interdependencies come back from now old days when the c-ares project lived inside a curl subdirectory. This commit removes last traces of such fact.
2013-01-07 02:06:49 +08:00
#include "curl_setup.h"
#ifdef USE_DARWINSSL
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <Security/Security.h>
#include <Security/SecureTransport.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CommonCrypto/CommonDigest.h>
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
#include <sys/sysctl.h>
#endif
#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 */
{
size_t bytesToGo = *dataLength;
size_t 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;
size_t bytesRead;
ssize_t rrtn;
int theErr;
*dataLength = 0;
for(;;) {
bytesRead = 0;
rrtn = read(sock, currData, bytesToGo);
if(rrtn <= 0) {
/* this is guesswork... */
theErr = errno;
if(rrtn == 0) { /* EOF = server hung up */
/* the framework will turn this into errSSLClosedNoNotify */
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 */
{
size_t bytesSent = 0;
/*int sock = *(int *)connection;*/
struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
int sock = connssl->ssl_sockfd;
ssize_t length;
size_t 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;
/* TLS 1.0 with AES (RFC 3268)
(Apparently these are used in SSLv3 implementations as well.) */
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;
/* 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;
#if defined(__MAC_10_6) || defined(__IPHONE_5_0)
/* 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;
#endif /* defined(__MAC_10_6) || defined(__IPHONE_5_0) */
#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";
}
CF_INLINE bool IsRunningMountainLionOrLater(void)
{
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
int mib[2];
char *os_version;
size_t os_version_len;
char *os_version_major/*, *os_version_minor, *os_version_point*/;
int os_version_major_int;
/* Get the Darwin kernel version from the kernel using sysctl(): */
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
return false;
os_version = malloc(os_version_len*sizeof(char));
if(!os_version)
return false;
if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
free(os_version);
return false;
}
/* Parse the version. If it's version 12.0.0 or later, then this user is
using Mountain Lion. */
os_version_major = strtok(os_version, ".");
/*os_version_minor = strtok(NULL, ".");
os_version_point = strtok(NULL, ".");*/
os_version_major_int = atoi(os_version_major);
free(os_version);
return os_version_major_int >= 12;
#else
return true; /* iOS users: this doesn't concern you */
#endif
}
/* Apple provides a myriad of ways of getting information about a certificate
into a string. Some aren't available under iOS or newer cats. So here's
a unified function for getting a string describing the certificate that
ought to work in all cats starting with Leopard. */
CF_INLINE CFStringRef CopyCertSubject(SecCertificateRef cert)
{
CFStringRef server_cert_summary = CFSTR("(null)");
#if (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
/* iOS: There's only one way to do this. */
server_cert_summary = SecCertificateCopySubjectSummary(cert);
#else
#if defined(__MAC_10_7)
/* Lion & later: Get the long description if we can. */
if(SecCertificateCopyLongDescription != NULL)
server_cert_summary =
SecCertificateCopyLongDescription(NULL, cert, NULL);
else
#endif /* defined(__MAC_10_7) */
#if defined(__MAC_10_6)
/* Snow Leopard: Get the certificate summary. */
if(SecCertificateCopySubjectSummary != NULL)
server_cert_summary = SecCertificateCopySubjectSummary(cert);
else
#endif /* defined(__MAC_10_6) */
/* Leopard is as far back as we go... */
(void)SecCertificateCopyCommonName(cert, &server_cert_summary);
#endif /* (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) */
return server_cert_summary;
}
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];
#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 {
/* The old ST API does not exist under iOS, so don't compile it: */
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
}
#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) */
connssl->ssl_write_buffered_length = 0UL; /* reset buffered write length */
/* 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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
(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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
}
#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)
/* Snow Leopard introduced the SSLSetSessionOption() function, but due to
a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag
works, it doesn't work as expected under Snow Leopard or Lion.
So we need to call SSLSetEnableCertVerify() on those older cats in order
to disable certificate validation if the user turned that off.
(SecureTransport will always validate the certificate chain by
default.) */
if(SSLSetSessionOption != NULL && IsRunningMountainLionOrLater()) {
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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
}
#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) */
SSL: Several SSL-backend related fixes axTLS: This will make the axTLS backend perform the RFC2818 checks, honoring the VERIFYHOST setting similar to the OpenSSL backend. Generic for OpenSSL and axTLS: Move the hostcheck and cert_hostcheck functions from the lib/ssluse.c files to make them genericly available for both the OpenSSL, axTLS and other SSL backends. They are now in the new lib/hostcheck.c file. CyaSSL: CyaSSL now also has the RFC2818 checks enabled by default. There is a limitation that the verifyhost can not be enabled exclusively on the Subject CN field comparison. This SSL backend will thus behave like the NSS and the GnuTLS (meaning: RFC2818 ok, or bust). In other words: setting verifyhost to 0 or 1 will disable the Subject Alt Names checks too. Schannel: Updated the schannel information messages: Split the IP address usage message from the verifyhost setting and changed the message about disabling SNI (Server Name Indication, used in HTTP virtual hosting) into a message stating that the Subject Alternative Names checks are being disabled when verifyhost is set to 0 or 1. As a side effect of switching off the RFC2818 related servername checks with SCH_CRED_NO_SERVERNAME_CHECK (http://msdn.microsoft.com/en-us/library/aa923430.aspx) the SNI feature is being disabled. This effect is not documented in MSDN, but Wireshark output clearly shows the effect (details on the libcurl maillist). PolarSSL: Fix the prototype change in PolarSSL of ssl_set_session() and the move of the peer_cert from the ssl_context to the ssl_session. Found this change in the PolarSSL SVN between r1316 and r1317 where the POLARSSL_VERSION_NUMBER was at 0x01010100. But to accommodate the Ubuntu PolarSSL version 1.1.4 the check is to discriminate between lower then PolarSSL version 1.2.0 and 1.2.0 and higher. Note: The PolarSSL SVN trunk jumped from version 1.1.1 to 1.2.0. Generic: All the SSL backends are fixed and checked to work with the ssl.verifyhost as a boolean, which is an internal API change.
2012-11-03 09:06:51 +08:00
/* If this is a domain name and not an IP address, then configure SNI.
* Also: the verifyhost setting influences SNI usage */
/* 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
data->set.ssl.verifyhost) {
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;
/* The below is errSSLServerAuthCompleted; it's not defined in
Leopard's headers */
case -9841:
/* the documentation says we need to call SSLHandshake() again */
return darwinssl_connect_step2(conn, sockindex);
case errSSLXCertChainInvalid:
failf(data, "SSL certificate problem: Invalid certificate chain");
return CURLE_SSL_CACERT;
case errSSLUnknownRootCert:
failf(data, "SSL certificate problem: Untrusted root certificate");
return CURLE_SSL_CACERT;
case errSSLNoRootCert:
failf(data, "SSL certificate problem: No root certificate");
return CURLE_SSL_CACERT;
case errSSLCertExpired:
failf(data, "SSL certificate problem: Certificate chain had an "
"expired certificate");
return CURLE_SSL_CACERT;
SSL: Several SSL-backend related fixes axTLS: This will make the axTLS backend perform the RFC2818 checks, honoring the VERIFYHOST setting similar to the OpenSSL backend. Generic for OpenSSL and axTLS: Move the hostcheck and cert_hostcheck functions from the lib/ssluse.c files to make them genericly available for both the OpenSSL, axTLS and other SSL backends. They are now in the new lib/hostcheck.c file. CyaSSL: CyaSSL now also has the RFC2818 checks enabled by default. There is a limitation that the verifyhost can not be enabled exclusively on the Subject CN field comparison. This SSL backend will thus behave like the NSS and the GnuTLS (meaning: RFC2818 ok, or bust). In other words: setting verifyhost to 0 or 1 will disable the Subject Alt Names checks too. Schannel: Updated the schannel information messages: Split the IP address usage message from the verifyhost setting and changed the message about disabling SNI (Server Name Indication, used in HTTP virtual hosting) into a message stating that the Subject Alternative Names checks are being disabled when verifyhost is set to 0 or 1. As a side effect of switching off the RFC2818 related servername checks with SCH_CRED_NO_SERVERNAME_CHECK (http://msdn.microsoft.com/en-us/library/aa923430.aspx) the SNI feature is being disabled. This effect is not documented in MSDN, but Wireshark output clearly shows the effect (details on the libcurl maillist). PolarSSL: Fix the prototype change in PolarSSL of ssl_set_session() and the move of the peer_cert from the ssl_context to the ssl_session. Found this change in the PolarSSL SVN between r1316 and r1317 where the POLARSSL_VERSION_NUMBER was at 0x01010100. But to accommodate the Ubuntu PolarSSL version 1.1.4 the check is to discriminate between lower then PolarSSL version 1.2.0 and 1.2.0 and higher. Note: The PolarSSL SVN trunk jumped from version 1.1.1 to 1.2.0. Generic: All the SSL backends are fixed and checked to work with the ssl.verifyhost as a boolean, which is an internal API change.
2012-11-03 09:06:51 +08:00
case errSSLHostNameMismatch:
failf(data, "SSL certificate peer verification failed, the "
"certificate did not match \"%s\"\n", conn->host.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
case errSSLConnectionRefused:
failf(data, "Server dropped the connection during the SSL handshake");
return CURLE_SSL_CONNECT_ERROR;
default:
failf(data, "Unknown SSL protocol error in connection to %s:%d",
conn->host.name, err);
return CURLE_SSL_CONNECT_ERROR;
}
}
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 (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
#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 = CopyCertSubject(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
/* SSLCopyPeerCertificates() is deprecated as of Mountain Lion.
The function SecTrustGetCertificateAtIndex() is officially present
in Lion, but it is unfortunately also present in Snow Leopard as
private API and doesn't work as expected. So we have to look for
a different symbol to make sure this code is only executed under
Lion or later. */
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 = CopyCertSubject(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 {
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 = CopyCertSubject(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 || TARGET_OS_IPHONE) */
#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 = CopyCertSubject(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_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
else
(void)SSLDisposeContext(connssl->ssl_ctx);
#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
#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, (CC_LONG)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 = 0UL;
OSStatus err;
/* The SSLWrite() function works a little differently than expected. The
fourth argument (processed) is currently documented in Apple's
documentation as: "On return, the length, in bytes, of the data actually
written."
Now, one could interpret that as "written to the socket," but actually,
it returns the amount of data that was written to a buffer internal to
the SSLContextRef instead. So it's possible for SSLWrite() to return
errSSLWouldBlock and a number of bytes "written" because those bytes were
encrypted and written to a buffer, not to the socket.
So if this happens, then we need to keep calling SSLWrite() over and
over again with no new data until it quits returning errSSLWouldBlock. */
/* Do we have buffered data to write from the last time we were called? */
if(connssl->ssl_write_buffered_length) {
/* Write the buffered data: */
err = SSLWrite(connssl->ssl_ctx, NULL, 0UL, &processed);
switch (err) {
case noErr:
/* processed is always going to be 0 because we didn't write to
the buffer, so return how much was written to the socket */
processed = connssl->ssl_write_buffered_length;
connssl->ssl_write_buffered_length = 0UL;
break;
case errSSLWouldBlock: /* argh, try again */
*curlcode = CURLE_AGAIN;
return -1L;
default:
failf(conn->data, "SSLWrite() returned error %d", err);
*curlcode = CURLE_SEND_ERROR;
return -1L;
}
}
else {
/* We've got new data to write: */
err = SSLWrite(connssl->ssl_ctx, mem, len, &processed);
if(err != noErr) {
switch (err) {
case errSSLWouldBlock:
/* Data was buffered but not sent, we have to tell the caller
to try sending again, and remember how much was buffered */
connssl->ssl_write_buffered_length = len;
*curlcode = CURLE_AGAIN;
return -1L;
default:
failf(conn->data, "SSLWrite() returned error %d", err);
*curlcode = CURLE_SEND_ERROR;
return -1L;
}
}
}
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 = 0UL;
OSStatus err = SSLRead(connssl->ssl_ctx, buf, buffersize, &processed);
if(err != noErr) {
switch (err) {
case errSSLWouldBlock: /* return how much we read (if anything) */
if(processed)
return (ssize_t)processed;
*curlcode = CURLE_AGAIN;
return -1L;
break;
/* errSSLClosedGraceful - server gracefully shut down the SSL session
errSSLClosedNoNotify - server hung up on us instead of sending a
closure alert notice, read() is returning 0
Either way, inform the caller that the server disconnected. */
case errSSLClosedGraceful:
case errSSLClosedNoNotify:
*curlcode = CURLE_OK;
return -1L;
break;
default:
failf(conn->data, "SSLRead() return error %d", err);
*curlcode = CURLE_RECV_ERROR;
return -1L;
break;
}
}
return (ssize_t)processed;
}
#endif /* USE_DARWINSSL */