| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 2012 - 2021, Daniel Stenberg, <[email protected]>, et al. |
| * Copyright (C) 2012 - 2017, Nick Zitzmann, <[email protected]>. |
| * |
| * 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 https://curl.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 macOS SecureTransport-specific code for the |
| * TLS/SSL layer. No code but vtls.c should ever call or use these functions. |
| */ |
| |
| #include "curl_setup.h" |
| |
| #include "urldata.h" /* for the Curl_easy definition */ |
| #include "curl_base64.h" |
| #include "strtok.h" |
| #include "multiif.h" |
| #include "strcase.h" |
| |
| #ifdef USE_SECTRANSP |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wtautological-pointer-compare" |
| #endif /* __clang__ */ |
| |
| #include <limits.h> |
| |
| #include <Security/Security.h> |
| /* For some reason, when building for iOS, the omnibus header above does |
| * not include SecureTransport.h as of iOS SDK 5.1. */ |
| #include <Security/SecureTransport.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <CommonCrypto/CommonDigest.h> |
| |
| /* The Security framework has changed greatly between iOS and different macOS |
| versions, and we will try to support as many of them as we can (back to |
| Leopard and iOS 5) by using macros and weak-linking. |
| |
| In general, you want to build this using the most recent OS SDK, since some |
| features require curl to be built against the latest SDK. TLS 1.1 and 1.2 |
| support, for instance, require the macOS 10.8 SDK or later. TLS 1.3 |
| requires the macOS 10.13 or iOS 11 SDK or later. */ |
| #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) |
| |
| #if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 |
| #error "The Secure Transport back-end requires Leopard or later." |
| #endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ |
| |
| #define CURL_BUILD_IOS 0 |
| #define CURL_BUILD_IOS_7 0 |
| #define CURL_BUILD_IOS_9 0 |
| #define CURL_BUILD_IOS_11 0 |
| #define CURL_BUILD_IOS_13 0 |
| #define CURL_BUILD_MAC 1 |
| /* This is the maximum API level we are allowed to use when building: */ |
| #define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 |
| #define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 |
| #define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 |
| #define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 |
| #define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 |
| #define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 |
| #define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 |
| #define CURL_BUILD_MAC_10_15 MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 |
| /* These macros mean "the following code is present to allow runtime backward |
| compatibility with at least this cat or earlier": |
| (You set this at build-time using the compiler command line option |
| "-mmacosx-version-min.") */ |
| #define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 |
| #define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 |
| #define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 |
| #define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 |
| #define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 |
| |
| #elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE |
| #define CURL_BUILD_IOS 1 |
| #define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 |
| #define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 |
| #define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 |
| #define CURL_BUILD_IOS_13 __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 |
| #define CURL_BUILD_MAC 0 |
| #define CURL_BUILD_MAC_10_5 0 |
| #define CURL_BUILD_MAC_10_6 0 |
| #define CURL_BUILD_MAC_10_7 0 |
| #define CURL_BUILD_MAC_10_8 0 |
| #define CURL_BUILD_MAC_10_9 0 |
| #define CURL_BUILD_MAC_10_11 0 |
| #define CURL_BUILD_MAC_10_13 0 |
| #define CURL_BUILD_MAC_10_15 0 |
| #define CURL_SUPPORT_MAC_10_5 0 |
| #define CURL_SUPPORT_MAC_10_6 0 |
| #define CURL_SUPPORT_MAC_10_7 0 |
| #define CURL_SUPPORT_MAC_10_8 0 |
| #define CURL_SUPPORT_MAC_10_9 0 |
| |
| #else |
| #error "The Secure Transport back-end requires iOS or macOS." |
| #endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ |
| |
| #if CURL_BUILD_MAC |
| #include <sys/sysctl.h> |
| #endif /* CURL_BUILD_MAC */ |
| |
| #include "urldata.h" |
| #include "sendf.h" |
| #include "inet_pton.h" |
| #include "connect.h" |
| #include "select.h" |
| #include "vtls.h" |
| #include "sectransp.h" |
| #include "curl_printf.h" |
| #include "strdup.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 |
| #define paramErr -50 |
| |
| struct ssl_backend_data { |
| SSLContextRef ssl_ctx; |
| curl_socket_t ssl_sockfd; |
| bool ssl_direction; /* true if writing, false if reading */ |
| size_t ssl_write_buffered_length; |
| }; |
| |
| struct st_cipher { |
| const char *name; /* Cipher suite IANA name. It starts with "TLS_" prefix */ |
| const char *alias_name; /* Alias name is the same as OpenSSL cipher name */ |
| SSLCipherSuite num; /* Cipher suite code/number defined in IANA registry */ |
| bool weak; /* Flag to mark cipher as weak based on previous implementation |
| of Secure Transport back-end by CURL */ |
| }; |
| |
| /* Macro to initialize st_cipher data structure: stringify id to name, cipher |
| number/id, 'weak' suite flag |
| */ |
| #define CIPHER_DEF(num, alias, weak) \ |
| { #num, alias, num, weak } |
| |
| /* |
| Macro to initialize st_cipher data structure with name, code (IANA cipher |
| number/id value), and 'weak' suite flag. The first 28 cipher suite numbers |
| have the same IANA code for both SSL and TLS standards: numbers 0x0000 to |
| 0x001B. They have different names though. The first 4 letters of the cipher |
| suite name are the protocol name: "SSL_" or "TLS_", rest of the IANA name is |
| the same for both SSL and TLS cipher suite name. |
| The second part of the problem is that macOS/iOS SDKs don't define all TLS |
| codes but only 12 of them. The SDK defines all SSL codes though, i.e. SSL_NUM |
| constant is always defined for those 28 ciphers while TLS_NUM is defined only |
| for 12 of the first 28 ciphers. Those 12 TLS cipher codes match to |
| corresponding SSL enum value and represent the same cipher suite. Therefore |
| we'll use the SSL enum value for those cipher suites because it is defined |
| for all 28 of them. |
| We make internal data consistent and based on TLS names, i.e. all st_cipher |
| item names start with the "TLS_" prefix. |
| Summarizing all the above, those 28 first ciphers are presented in our table |
| with both TLS and SSL names. Their cipher numbers are assigned based on the |
| SDK enum value for the SSL cipher, which matches to IANA TLS number. |
| */ |
| #define CIPHER_DEF_SSLTLS(num_wo_prefix, alias, weak) \ |
| { "TLS_" #num_wo_prefix, alias, SSL_##num_wo_prefix, weak } |
| |
| /* |
| Cipher suites were marked as weak based on the following: |
| RC4 encryption - rfc7465, the document contains a list of deprecated ciphers. |
| Marked in the code below as weak. |
| RC2 encryption - many mentions, was found vulnerable to a relatively easy |
| attack https://link.springer.com/chapter/10.1007%2F3-540-69710-1_14 |
| Marked in the code below as weak. |
| DES and IDEA encryption - rfc5469, has a list of deprecated ciphers. |
| Marked in the code below as weak. |
| Anonymous Diffie-Hellman authentication and anonymous elliptic curve |
| Diffie-Hellman - vulnerable to a man-in-the-middle attack. Deprecated by |
| RFC 4346 aka TLS 1.1 (section A.5, page 60) |
| Null bulk encryption suites - not encrypted communication |
| Export ciphers, i.e. ciphers with restrictions to be used outside the US for |
| software exported to some countries, they were excluded from TLS 1.1 |
| version. More precisely, they were noted as ciphers which MUST NOT be |
| negotiated in RFC 4346 aka TLS 1.1 (section A.5, pages 60 and 61). |
| All of those filters were considered weak because they contain a weak |
| algorithm like DES, RC2 or RC4, and already considered weak by other |
| criteria. |
| 3DES - NIST deprecated it and is going to retire it by 2023 |
| https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA |
| OpenSSL https://www.openssl.org/blog/blog/2016/08/24/sweet32/ also |
| deprecated those ciphers. Some other libraries also consider it |
| vulnerable or at least not strong enough. |
| |
| CBC ciphers are vulnerable with SSL3.0 and TLS1.0: |
| https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance |
| /118518-technote-esa-00.html |
| We don't take care of this issue because it is resolved by later TLS |
| versions and for us, it requires more complicated checks, we need to |
| check a protocol version also. Vulnerability doesn't look very critical |
| and we do not filter out those cipher suites. |
| */ |
| |
| #define CIPHER_WEAK_NOT_ENCRYPTED TRUE |
| #define CIPHER_WEAK_RC_ENCRYPTION TRUE |
| #define CIPHER_WEAK_DES_ENCRYPTION TRUE |
| #define CIPHER_WEAK_IDEA_ENCRYPTION TRUE |
| #define CIPHER_WEAK_ANON_AUTH TRUE |
| #define CIPHER_WEAK_3DES_ENCRYPTION TRUE |
| #define CIPHER_STRONG_ENOUGH FALSE |
| |
| /* Please do not change the order of the first ciphers available for SSL. |
| Do not insert and do not delete any of them. Code below |
| depends on their order and continuity. |
| If you add a new cipher, please maintain order by number, i.e. |
| insert in between existing items to appropriate place based on |
| cipher suite IANA number |
| */ |
| const static struct st_cipher ciphertable[] = { |
| /* SSL version 3.0 and initial TLS 1.0 cipher suites. |
| Defined since SDK 10.2.8 */ |
| CIPHER_DEF_SSLTLS(NULL_WITH_NULL_NULL, /* 0x0000 */ |
| NULL, |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF_SSLTLS(RSA_WITH_NULL_MD5, /* 0x0001 */ |
| "NULL-MD5", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF_SSLTLS(RSA_WITH_NULL_SHA, /* 0x0002 */ |
| "NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC4_40_MD5, /* 0x0003 */ |
| "EXP-RC4-MD5", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_MD5, /* 0x0004 */ |
| "RC4-MD5", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_WITH_RC4_128_SHA, /* 0x0005 */ |
| "RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_RC2_CBC_40_MD5, /* 0x0006 */ |
| "EXP-RC2-CBC-MD5", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_WITH_IDEA_CBC_SHA, /* 0x0007 */ |
| "IDEA-CBC-SHA", |
| CIPHER_WEAK_IDEA_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0008 */ |
| "EXP-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_WITH_DES_CBC_SHA, /* 0x0009 */ |
| "DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ |
| "DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x000B */ |
| "EXP-DH-DSS-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_DSS_WITH_DES_CBC_SHA, /* 0x000C */ |
| "DH-DSS-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x000D */ |
| "DH-DSS-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x000E */ |
| "EXP-DH-RSA-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_RSA_WITH_DES_CBC_SHA, /* 0x000F */ |
| "DH-RSA-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0010 */ |
| "DH-RSA-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, /* 0x0011 */ |
| "EXP-EDH-DSS-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_DSS_WITH_DES_CBC_SHA, /* 0x0012 */ |
| "EDH-DSS-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_DSS_WITH_3DES_EDE_CBC_SHA, /* 0x0013 */ |
| "DHE-DSS-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, /* 0x0014 */ |
| "EXP-EDH-RSA-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_RSA_WITH_DES_CBC_SHA, /* 0x0015 */ |
| "EDH-RSA-DES-CBC-SHA", |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x0016 */ |
| "DHE-RSA-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_RC4_40_MD5, /* 0x0017 */ |
| "EXP-ADH-RC4-MD5", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF_SSLTLS(DH_anon_WITH_RC4_128_MD5, /* 0x0018 */ |
| "ADH-RC4-MD5", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF_SSLTLS(DH_anon_EXPORT_WITH_DES40_CBC_SHA, /* 0x0019 */ |
| "EXP-ADH-DES-CBC-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF_SSLTLS(DH_anon_WITH_DES_CBC_SHA, /* 0x001A */ |
| "ADH-DES-CBC-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF_SSLTLS(DH_anon_WITH_3DES_EDE_CBC_SHA, /* 0x001B */ |
| "ADH-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_NULL_SHA, /* 0x001C */ |
| NULL, |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, /* 0x001D */ |
| NULL, |
| CIPHER_STRONG_ENOUGH), |
| |
| #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
| /* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */ |
| CIPHER_DEF(TLS_PSK_WITH_NULL_SHA, /* 0x002C */ |
| "PSK-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA, /* 0x002D */ |
| "DHE-PSK-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA, /* 0x002E */ |
| "RSA-PSK-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ |
| |
| /* TLS addenda using AES, per RFC 3268. Defined since SDK 10.4u */ |
| CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ |
| "AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA, /* 0x0030 */ |
| "DH-DSS-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA, /* 0x0031 */ |
| "DH-RSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA, /* 0x0032 */ |
| "DHE-DSS-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA, /* 0x0033 */ |
| "DHE-RSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA, /* 0x0034 */ |
| "ADH-AES128-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ |
| "AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA, /* 0x0036 */ |
| "DH-DSS-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA, /* 0x0037 */ |
| "DH-RSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA, /* 0x0038 */ |
| "DHE-DSS-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA, /* 0x0039 */ |
| "DHE-RSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA, /* 0x003A */ |
| "ADH-AES256-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| /* TLS 1.2 addenda, RFC 5246 */ |
| /* Server provided RSA certificate for key exchange. */ |
| CIPHER_DEF(TLS_RSA_WITH_NULL_SHA256, /* 0x003B */ |
| "NULL-SHA256", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ |
| "AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ |
| "AES256-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| /* Server-authenticated (and optionally client-authenticated) |
| Diffie-Hellman. */ |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, /* 0x003E */ |
| "DH-DSS-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, /* 0x003F */ |
| "DH-RSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, /* 0x0040 */ |
| "DHE-DSS-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| |
| /* TLS 1.2 addenda, RFC 5246 */ |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ |
| "DHE-RSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, /* 0x0068 */ |
| "DH-DSS-AES256-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, /* 0x0069 */ |
| "DH-RSA-AES256-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, /* 0x006A */ |
| "DHE-DSS-AES256-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ |
| "DHE-RSA-AES256-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_128_CBC_SHA256, /* 0x006C */ |
| "ADH-AES128-SHA256", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_256_CBC_SHA256, /* 0x006D */ |
| "ADH-AES256-SHA256", |
| CIPHER_WEAK_ANON_AUTH), |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| |
| #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
| /* Addendum from RFC 4279, TLS PSK */ |
| CIPHER_DEF(TLS_PSK_WITH_RC4_128_SHA, /* 0x008A */ |
| "PSK-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008B */ |
| "PSK-3DES-EDE-CBC-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA, /* 0x008C */ |
| "PSK-AES128-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA, /* 0x008D */ |
| "PSK-AES256-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_RC4_128_SHA, /* 0x008E */ |
| "DHE-PSK-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x008F */ |
| "DHE-PSK-3DES-EDE-CBC-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA, /* 0x0090 */ |
| "DHE-PSK-AES128-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA, /* 0x0091 */ |
| "DHE-PSK-AES256-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_RC4_128_SHA, /* 0x0092 */ |
| "RSA-PSK-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, /* 0x0093 */ |
| "RSA-PSK-3DES-EDE-CBC-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA, /* 0x0094 */ |
| "RSA-PSK-AES128-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, /* 0x0095 */ |
| "RSA-PSK-AES256-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| /* Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites |
| for TLS. */ |
| CIPHER_DEF(TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ |
| "AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ |
| "AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ |
| "DHE-RSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ |
| "DHE-RSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_128_GCM_SHA256, /* 0x00A0 */ |
| "DH-RSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_RSA_WITH_AES_256_GCM_SHA384, /* 0x00A1 */ |
| "DH-RSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A2 */ |
| "DHE-DSS-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A3 */ |
| "DHE-DSS-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_128_GCM_SHA256, /* 0x00A4 */ |
| "DH-DSS-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_DSS_WITH_AES_256_GCM_SHA384, /* 0x00A5 */ |
| "DH-DSS-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_128_GCM_SHA256, /* 0x00A6 */ |
| "ADH-AES128-GCM-SHA256", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_DH_anon_WITH_AES_256_GCM_SHA384, /* 0x00A7 */ |
| "ADH-AES256-GCM-SHA384", |
| CIPHER_WEAK_ANON_AUTH), |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| |
| #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
| /* RFC 5487 - PSK with SHA-256/384 and AES GCM */ |
| CIPHER_DEF(TLS_PSK_WITH_AES_128_GCM_SHA256, /* 0x00A8 */ |
| "PSK-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_PSK_WITH_AES_256_GCM_SHA384, /* 0x00A9 */ |
| "PSK-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AA */ |
| "DHE-PSK-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AB */ |
| "DHE-PSK-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, /* 0x00AC */ |
| "RSA-PSK-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, /* 0x00AD */ |
| "RSA-PSK-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_PSK_WITH_AES_128_CBC_SHA256, /* 0x00AE */ |
| "PSK-AES128-CBC-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_PSK_WITH_AES_256_CBC_SHA384, /* 0x00AF */ |
| "PSK-AES256-CBC-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_PSK_WITH_NULL_SHA256, /* 0x00B0 */ |
| "PSK-NULL-SHA256", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_PSK_WITH_NULL_SHA384, /* 0x00B1 */ |
| "PSK-NULL-SHA384", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B2 */ |
| "DHE-PSK-AES128-CBC-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B3 */ |
| "DHE-PSK-AES256-CBC-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA256, /* 0x00B4 */ |
| "DHE-PSK-NULL-SHA256", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_DHE_PSK_WITH_NULL_SHA384, /* 0x00B5 */ |
| "DHE-PSK-NULL-SHA384", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, /* 0x00B6 */ |
| "RSA-PSK-AES128-CBC-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, /* 0x00B7 */ |
| "RSA-PSK-AES256-CBC-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA256, /* 0x00B8 */ |
| "RSA-PSK-NULL-SHA256", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_RSA_PSK_WITH_NULL_SHA384, /* 0x00B9 */ |
| "RSA-PSK-NULL-SHA384", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ |
| |
| /* RFC 5746 - Secure Renegotiation. This is not a real suite, |
| it is a response to initiate negotiation again */ |
| CIPHER_DEF(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* 0x00FF */ |
| NULL, |
| CIPHER_STRONG_ENOUGH), |
| |
| #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 |
| /* TLS 1.3 standard cipher suites for ChaCha20+Poly1305. |
| Note: TLS 1.3 ciphersuites do not specify the key exchange |
| algorithm -- they only specify the symmetric ciphers. |
| Cipher alias name matches to OpenSSL cipher name, and for |
| TLS 1.3 ciphers */ |
| CIPHER_DEF(TLS_AES_128_GCM_SHA256, /* 0x1301 */ |
| NULL, /* The OpenSSL cipher name matches to the IANA name */ |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_AES_256_GCM_SHA384, /* 0x1302 */ |
| NULL, /* The OpenSSL cipher name matches to the IANA name */ |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ |
| NULL, /* The OpenSSL cipher name matches to the IANA name */ |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_AES_128_CCM_SHA256, /* 0x1304 */ |
| NULL, /* The OpenSSL cipher name matches to the IANA name */ |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_AES_128_CCM_8_SHA256, /* 0x1305 */ |
| NULL, /* The OpenSSL cipher name matches to the IANA name */ |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ |
| |
| #if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS |
| /* ECDSA addenda, RFC 4492 */ |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_NULL_SHA, /* 0xC001 */ |
| "ECDH-ECDSA-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_RC4_128_SHA, /* 0xC002 */ |
| "ECDH-ECDSA-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ |
| "ECDH-ECDSA-DES-CBC3-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ |
| "ECDH-ECDSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ |
| "ECDH-ECDSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_NULL_SHA, /* 0xC006 */ |
| "ECDHE-ECDSA-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, /* 0xC007 */ |
| "ECDHE-ECDSA-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ |
| "ECDHE-ECDSA-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ |
| "ECDHE-ECDSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ |
| "ECDHE-ECDSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_NULL_SHA, /* 0xC00B */ |
| "ECDH-RSA-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_RC4_128_SHA, /* 0xC00C */ |
| "ECDH-RSA-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ |
| "ECDH-RSA-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ |
| "ECDH-RSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ |
| "ECDH-RSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_NULL_SHA, /* 0xC010 */ |
| "ECDHE-RSA-NULL-SHA", |
| CIPHER_WEAK_NOT_ENCRYPTED), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_RC4_128_SHA, /* 0xC011 */ |
| "ECDHE-RSA-RC4-SHA", |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ |
| "ECDHE-RSA-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ |
| "ECDHE-RSA-AES128-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ |
| "ECDHE-RSA-AES256-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_anon_WITH_NULL_SHA, /* 0xC015 */ |
| "AECDH-NULL-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_ECDH_anon_WITH_RC4_128_SHA, /* 0xC016 */ |
| "AECDH-RC4-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, /* 0xC017 */ |
| "AECDH-DES-CBC3-SHA", |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| CIPHER_DEF(TLS_ECDH_anon_WITH_AES_128_CBC_SHA, /* 0xC018 */ |
| "AECDH-AES128-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| CIPHER_DEF(TLS_ECDH_anon_WITH_AES_256_CBC_SHA, /* 0xC019 */ |
| "AECDH-AES256-SHA", |
| CIPHER_WEAK_ANON_AUTH), |
| #endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with |
| HMAC SHA-256/384. */ |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ |
| "ECDHE-ECDSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ |
| "ECDHE-ECDSA-AES256-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ |
| "ECDH-ECDSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ |
| "ECDH-ECDSA-AES256-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ |
| "ECDHE-RSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ |
| "ECDHE-RSA-AES256-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ |
| "ECDH-RSA-AES128-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ |
| "ECDH-RSA-AES256-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| /* Addenda from rfc 5289 Elliptic Curve Cipher Suites with |
| SHA-256/384 and AES Galois Counter Mode (GCM) */ |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ |
| "ECDHE-ECDSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ |
| "ECDHE-ECDSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ |
| "ECDH-ECDSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ |
| "ECDH-ECDSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ |
| "ECDHE-RSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ |
| "ECDHE-RSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ |
| "ECDH-RSA-AES128-GCM-SHA256", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ |
| "ECDH-RSA-AES256-GCM-SHA384", |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| |
| #if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 |
| /* ECDHE_PSK Cipher Suites for Transport Layer Security (TLS), RFC 5489 */ |
| CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, /* 0xC035 */ |
| "ECDHE-PSK-AES128-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, /* 0xC036 */ |
| "ECDHE-PSK-AES256-CBC-SHA", |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ |
| |
| #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 |
| /* Addenda from rfc 7905 ChaCha20-Poly1305 Cipher Suites for |
| Transport Layer Security (TLS). */ |
| CIPHER_DEF(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ |
| "ECDHE-RSA-CHACHA20-POLY1305", |
| CIPHER_STRONG_ENOUGH), |
| CIPHER_DEF(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ |
| "ECDHE-ECDSA-CHACHA20-POLY1305", |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ |
| |
| #if CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 |
| /* ChaCha20-Poly1305 Cipher Suites for Transport Layer Security (TLS), |
| RFC 7905 */ |
| CIPHER_DEF(TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCAB */ |
| "PSK-CHACHA20-POLY1305", |
| CIPHER_STRONG_ENOUGH), |
| #endif /* CURL_BUILD_MAC_10_15 || CURL_BUILD_IOS_13 */ |
| |
| /* Tags for SSL 2 cipher kinds which are not specified for SSL 3. |
| Defined since SDK 10.2.8 */ |
| CIPHER_DEF(SSL_RSA_WITH_RC2_CBC_MD5, /* 0xFF80 */ |
| NULL, |
| CIPHER_WEAK_RC_ENCRYPTION), |
| CIPHER_DEF(SSL_RSA_WITH_IDEA_CBC_MD5, /* 0xFF81 */ |
| NULL, |
| CIPHER_WEAK_IDEA_ENCRYPTION), |
| CIPHER_DEF(SSL_RSA_WITH_DES_CBC_MD5, /* 0xFF82 */ |
| NULL, |
| CIPHER_WEAK_DES_ENCRYPTION), |
| CIPHER_DEF(SSL_RSA_WITH_3DES_EDE_CBC_MD5, /* 0xFF83 */ |
| NULL, |
| CIPHER_WEAK_3DES_ENCRYPTION), |
| }; |
| |
| #define NUM_OF_CIPHERS sizeof(ciphertable)/sizeof(ciphertable[0]) |
| |
| |
| /* pinned public key support tests */ |
| |
| /* version 1 supports macOS 10.12+ and iOS 10+ */ |
| #if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \ |
| (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) |
| #define SECTRANSP_PINNEDPUBKEY_V1 1 |
| #endif |
| |
| /* version 2 supports MacOSX 10.7+ */ |
| #if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) |
| #define SECTRANSP_PINNEDPUBKEY_V2 1 |
| #endif |
| |
| #if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2) |
| /* this backend supports CURLOPT_PINNEDPUBLICKEY */ |
| #define SECTRANSP_PINNEDPUBKEY 1 |
| #endif /* SECTRANSP_PINNEDPUBKEY */ |
| |
| #ifdef SECTRANSP_PINNEDPUBKEY |
| /* both new and old APIs return rsa keys missing the spki header (not DER) */ |
| static const unsigned char rsa4096SpkiHeader[] = { |
| 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, |
| 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, |
| 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, |
| 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00}; |
| |
| static const unsigned char rsa2048SpkiHeader[] = { |
| 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, |
| 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, |
| 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, |
| 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00}; |
| #ifdef SECTRANSP_PINNEDPUBKEY_V1 |
| /* the *new* version doesn't return DER encoded ecdsa certs like the old... */ |
| static const unsigned char ecDsaSecp256r1SpkiHeader[] = { |
| 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, |
| 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, |
| 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, |
| 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, |
| 0x42, 0x00}; |
| |
| static const unsigned char ecDsaSecp384r1SpkiHeader[] = { |
| 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, |
| 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, |
| 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, |
| 0x00, 0x22, 0x03, 0x62, 0x00}; |
| #endif /* SECTRANSP_PINNEDPUBKEY_V1 */ |
| #endif /* SECTRANSP_PINNEDPUBKEY */ |
| |
| /* 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; |
| struct ssl_backend_data *backend = connssl->backend; |
| int sock = backend->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; |
| backend->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; |
| struct ssl_backend_data *backend = connssl->backend; |
| int sock = backend->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; |
| backend->ssl_direction = true; |
| } |
| else { |
| ortn = ioErr; |
| } |
| } |
| else { |
| ortn = noErr; |
| } |
| *dataLength = bytesSent; |
| return ortn; |
| } |
| |
| #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher) |
| { |
| /* The first ciphers in the ciphertable are continuos. Here we do small |
| optimization and instead of loop directly get SSL name by cipher number. |
| */ |
| if(cipher <= SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) { |
| return ciphertable[cipher].name; |
| } |
| /* Iterate through the rest of the ciphers */ |
| for(size_t i = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA + 1; |
| i < NUM_OF_CIPHERS; |
| ++i) { |
| if(ciphertable[i].num == cipher) { |
| return ciphertable[i].name; |
| } |
| } |
| return ciphertable[SSL_NULL_WITH_NULL_NULL].name; |
| } |
| #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ |
| |
| #if CURL_BUILD_MAC |
| CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) |
| { |
| int mib[2]; |
| char *os_version; |
| size_t os_version_len; |
| char *os_version_major, *os_version_minor; |
| char *tok_buf; |
| |
| /* 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; |
| os_version = malloc(os_version_len*sizeof(char)); |
| if(!os_version) |
| return; |
| if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) { |
| free(os_version); |
| return; |
| } |
| |
| /* Parse the version: */ |
| os_version_major = strtok_r(os_version, ".", &tok_buf); |
| os_version_minor = strtok_r(NULL, ".", &tok_buf); |
| *major = atoi(os_version_major); |
| *minor = atoi(os_version_minor); |
| free(os_version); |
| } |
| #endif /* CURL_BUILD_MAC */ |
| |
| /* 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 getsubject(SecCertificateRef cert) |
| { |
| CFStringRef server_cert_summary = CFSTR("(null)"); |
| |
| #if CURL_BUILD_IOS |
| /* iOS: There's only one way to do this. */ |
| server_cert_summary = SecCertificateCopySubjectSummary(cert); |
| #else |
| #if CURL_BUILD_MAC_10_7 |
| /* Lion & later: Get the long description if we can. */ |
| if(SecCertificateCopyLongDescription != NULL) |
| server_cert_summary = |
| SecCertificateCopyLongDescription(NULL, cert, NULL); |
| else |
| #endif /* CURL_BUILD_MAC_10_7 */ |
| #if CURL_BUILD_MAC_10_6 |
| /* Snow Leopard: Get the certificate summary. */ |
| if(SecCertificateCopySubjectSummary != NULL) |
| server_cert_summary = SecCertificateCopySubjectSummary(cert); |
| else |
| #endif /* CURL_BUILD_MAC_10_6 */ |
| /* Leopard is as far back as we go... */ |
| (void)SecCertificateCopyCommonName(cert, &server_cert_summary); |
| #endif /* CURL_BUILD_IOS */ |
| return server_cert_summary; |
| } |
| |
| static CURLcode CopyCertSubject(struct Curl_easy *data, |
| SecCertificateRef cert, char **certp) |
| { |
| CFStringRef c = getsubject(cert); |
| CURLcode result = CURLE_OK; |
| const char *direct; |
| char *cbuf = NULL; |
| *certp = NULL; |
| |
| if(!c) { |
| failf(data, "SSL: invalid CA certificate subject"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| |
| /* If the subject is already available as UTF-8 encoded (ie 'direct') then |
| use that, else convert it. */ |
| direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8); |
| if(direct) { |
| *certp = strdup(direct); |
| if(!*certp) { |
| failf(data, "SSL: out of memory"); |
| result = CURLE_OUT_OF_MEMORY; |
| } |
| } |
| else { |
| size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; |
| cbuf = calloc(cbuf_size, 1); |
| if(cbuf) { |
| if(!CFStringGetCString(c, cbuf, cbuf_size, |
| kCFStringEncodingUTF8)) { |
| failf(data, "SSL: invalid CA certificate subject"); |
| result = CURLE_PEER_FAILED_VERIFICATION; |
| } |
| else |
| /* pass back the buffer */ |
| *certp = cbuf; |
| } |
| else { |
| failf(data, "SSL: couldn't allocate %zu bytes of memory", cbuf_size); |
| result = CURLE_OUT_OF_MEMORY; |
| } |
| } |
| if(result) |
| free(cbuf); |
| CFRelease(c); |
| return result; |
| } |
| |
| #if CURL_SUPPORT_MAC_10_6 |
| /* The SecKeychainSearch API was deprecated in Lion, and using it will raise |
| deprecation warnings, so let's not compile this unless it's necessary: */ |
| static OSStatus CopyIdentityWithLabelOldSchool(char *label, |
| SecIdentityRef *out_c_a_k) |
| { |
| OSStatus status = errSecItemNotFound; |
| SecKeychainAttributeList attr_list; |
| SecKeychainAttribute attr; |
| SecKeychainSearchRef search = NULL; |
| SecCertificateRef cert = NULL; |
| |
| /* Set up the attribute list: */ |
| attr_list.count = 1L; |
| attr_list.attr = &attr; |
| |
| /* Set up our lone search criterion: */ |
| attr.tag = kSecLabelItemAttr; |
| attr.data = label; |
| attr.length = (UInt32)strlen(label); |
| |
| /* Start searching: */ |
| status = SecKeychainSearchCreateFromAttributes(NULL, |
| kSecCertificateItemClass, |
| &attr_list, |
| &search); |
| if(status == noErr) { |
| status = SecKeychainSearchCopyNext(search, |
| (SecKeychainItemRef *)&cert); |
| if(status == noErr && cert) { |
| /* If we found a certificate, does it have a private key? */ |
| status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k); |
| CFRelease(cert); |
| } |
| } |
| |
| if(search) |
| CFRelease(search); |
| return status; |
| } |
| #endif /* CURL_SUPPORT_MAC_10_6 */ |
| |
| static OSStatus CopyIdentityWithLabel(char *label, |
| SecIdentityRef *out_cert_and_key) |
| { |
| OSStatus status = errSecItemNotFound; |
| |
| #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS |
| CFArrayRef keys_list; |
| CFIndex keys_list_count; |
| CFIndex i; |
| CFStringRef common_name; |
| |
| /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. |
| kSecClassIdentity was introduced in Lion. If both exist, let's use them |
| to find the certificate. */ |
| if(SecItemCopyMatching != NULL && kSecClassIdentity != NULL) { |
| CFTypeRef keys[5]; |
| CFTypeRef values[5]; |
| CFDictionaryRef query_dict; |
| CFStringRef label_cf = CFStringCreateWithCString(NULL, label, |
| kCFStringEncodingUTF8); |
| |
| /* Set up our search criteria and expected results: */ |
| values[0] = kSecClassIdentity; /* we want a certificate and a key */ |
| keys[0] = kSecClass; |
| values[1] = kCFBooleanTrue; /* we want a reference */ |
| keys[1] = kSecReturnRef; |
| values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the |
| * label matching below worked correctly */ |
| keys[2] = kSecMatchLimit; |
| /* identity searches need a SecPolicyRef in order to work */ |
| values[3] = SecPolicyCreateSSL(false, NULL); |
| keys[3] = kSecMatchPolicy; |
| /* match the name of the certificate (doesn't work in macOS 10.12.1) */ |
| values[4] = label_cf; |
| keys[4] = kSecAttrLabel; |
| query_dict = CFDictionaryCreate(NULL, (const void **)keys, |
| (const void **)values, 5L, |
| &kCFCopyStringDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks); |
| CFRelease(values[3]); |
| |
| /* Do we have a match? */ |
| status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list); |
| |
| /* Because kSecAttrLabel matching doesn't work with kSecClassIdentity, |
| * we need to find the correct identity ourselves */ |
| if(status == noErr) { |
| keys_list_count = CFArrayGetCount(keys_list); |
| *out_cert_and_key = NULL; |
| status = 1; |
| for(i = 0; i<keys_list_count; i++) { |
| OSStatus err = noErr; |
| SecCertificateRef cert = NULL; |
| SecIdentityRef identity = |
| (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i); |
| err = SecIdentityCopyCertificate(identity, &cert); |
| if(err == noErr) { |
| OSStatus copy_status = noErr; |
| #if CURL_BUILD_IOS |
| common_name = SecCertificateCopySubjectSummary(cert); |
| #elif CURL_BUILD_MAC_10_7 |
| copy_status = SecCertificateCopyCommonName(cert, &common_name); |
| #endif |
| if(copy_status == noErr && |
| CFStringCompare(common_name, label_cf, 0) == kCFCompareEqualTo) { |
| CFRelease(cert); |
| CFRelease(common_name); |
| CFRetain(identity); |
| *out_cert_and_key = identity; |
| status = noErr; |
| break; |
| } |
| CFRelease(common_name); |
| } |
| CFRelease(cert); |
| } |
| } |
| |
| if(keys_list) |
| CFRelease(keys_list); |
| CFRelease(query_dict); |
| CFRelease(label_cf); |
| } |
| else { |
| #if CURL_SUPPORT_MAC_10_6 |
| /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */ |
| status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); |
| #endif /* CURL_SUPPORT_MAC_10_6 */ |
| } |
| #elif CURL_SUPPORT_MAC_10_6 |
| /* For developers building on older cats, we have no choice but to fall back |
| to SecKeychainSearch. */ |
| status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); |
| #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ |
| return status; |
| } |
| |
| static OSStatus CopyIdentityFromPKCS12File(const char *cPath, |
| const struct curl_blob *blob, |
| const char *cPassword, |
| SecIdentityRef *out_cert_and_key) |
| { |
| OSStatus status = errSecItemNotFound; |
| CFURLRef pkcs_url = NULL; |
| CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, |
| cPassword, kCFStringEncodingUTF8) : NULL; |
| CFDataRef pkcs_data = NULL; |
| |
| /* We can import P12 files on iOS or OS X 10.7 or later: */ |
| /* These constants are documented as having first appeared in 10.6 but they |
| raise linker errors when used on that cat for some reason. */ |
| #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS |
| bool resource_imported; |
| |
| if(blob) { |
| pkcs_data = CFDataCreate(kCFAllocatorDefault, |
| (const unsigned char *)blob->data, blob->len); |
| status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; |
| resource_imported = (pkcs_data != NULL); |
| } |
| else { |
| pkcs_url = |
| CFURLCreateFromFileSystemRepresentation(NULL, |
| (const UInt8 *)cPath, |
| strlen(cPath), false); |
| resource_imported = |
| CFURLCreateDataAndPropertiesFromResource(NULL, |
| pkcs_url, &pkcs_data, |
| NULL, NULL, &status); |
| } |
| |
| if(resource_imported) { |
| CFArrayRef items = NULL; |
| |
| /* On iOS SecPKCS12Import will never add the client certificate to the |
| * Keychain. |
| * |
| * It gives us back a SecIdentityRef that we can use directly. */ |
| #if CURL_BUILD_IOS |
| const void *cKeys[] = {kSecImportExportPassphrase}; |
| const void *cValues[] = {password}; |
| CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, |
| password ? 1L : 0L, NULL, NULL); |
| |
| if(options != NULL) { |
| status = SecPKCS12Import(pkcs_data, options, &items); |
| CFRelease(options); |
| } |
| |
| |
| /* On macOS SecPKCS12Import will always add the client certificate to |
| * the Keychain. |
| * |
| * As this doesn't match iOS, and apps may not want to see their client |
| * certificate saved in the user's keychain, we use SecItemImport |
| * with a NULL keychain to avoid importing it. |
| * |
| * This returns a SecCertificateRef from which we can construct a |
| * SecIdentityRef. |
| */ |
| #elif CURL_BUILD_MAC_10_7 |
| SecItemImportExportKeyParameters keyParams; |
| SecExternalFormat inputFormat = kSecFormatPKCS12; |
| SecExternalItemType inputType = kSecItemTypeCertificate; |
| |
| memset(&keyParams, 0x00, sizeof(keyParams)); |
| keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; |
| keyParams.passphrase = password; |
| |
| status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, |
| 0, &keyParams, NULL, &items); |
| #endif |
| |
| |
| /* Extract the SecIdentityRef */ |
| if(status == errSecSuccess && items && CFArrayGetCount(items)) { |
| CFIndex i, count; |
| count = CFArrayGetCount(items); |
| |
| for(i = 0; i < count; i++) { |
| CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i); |
| CFTypeID itemID = CFGetTypeID(item); |
| |
| if(itemID == CFDictionaryGetTypeID()) { |
| CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue( |
| (CFDictionaryRef) item, |
| kSecImportItemIdentity); |
| CFRetain(identity); |
| *out_cert_and_key = (SecIdentityRef) identity; |
| break; |
| } |
| #if CURL_BUILD_MAC_10_7 |
| else if(itemID == SecCertificateGetTypeID()) { |
| status = SecIdentityCreateWithCertificate(NULL, |
| (SecCertificateRef) item, |
| out_cert_and_key); |
| break; |
| } |
| #endif |
| } |
| } |
| |
| if(items) |
| CFRelease(items); |
| CFRelease(pkcs_data); |
| } |
| #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ |
| if(password) |
| CFRelease(password); |
| if(pkcs_url) |
| CFRelease(pkcs_url); |
| return status; |
| } |
| |
| /* This code was borrowed from nss.c, with some modifications: |
| * Determine whether the nickname passed in is a filename that needs to |
| * be loaded as a PEM or a regular NSS nickname. |
| * |
| * returns 1 for a file |
| * returns 0 for not a file |
| */ |
| CF_INLINE bool is_file(const char *filename) |
| { |
| struct_stat st; |
| |
| if(!filename) |
| return false; |
| |
| if(stat(filename, &st) == 0) |
| return S_ISREG(st.st_mode); |
| return false; |
| } |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver, |
| long ssl_version) |
| { |
| switch(ssl_version) { |
| case CURL_SSLVERSION_TLSv1_0: |
| *darwinver = kTLSProtocol1; |
| return CURLE_OK; |
| case CURL_SSLVERSION_TLSv1_1: |
| *darwinver = kTLSProtocol11; |
| return CURLE_OK; |
| case CURL_SSLVERSION_TLSv1_2: |
| *darwinver = kTLSProtocol12; |
| return CURLE_OK; |
| case CURL_SSLVERSION_TLSv1_3: |
| /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */ |
| #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 |
| if(__builtin_available(macOS 10.13, iOS 11.0, *)) { |
| *darwinver = kTLSProtocol13; |
| return CURLE_OK; |
| } |
| #endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && |
| HAVE_BUILTIN_AVAILABLE == 1 */ |
| break; |
| } |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| #endif |
| |
| static CURLcode |
| set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, |
| int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| long ssl_version = SSL_CONN_CONFIG(version); |
| long ssl_version_max = SSL_CONN_CONFIG(version_max); |
| long max_supported_version_by_os; |
| |
| /* macOS 10.5-10.7 supported TLS 1.0 only. |
| macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2. |
| macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */ |
| #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 |
| if(__builtin_available(macOS 10.13, iOS 11.0, *)) { |
| max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3; |
| } |
| else { |
| max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; |
| } |
| #else |
| max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2; |
| #endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && |
| HAVE_BUILTIN_AVAILABLE == 1 */ |
| |
| switch(ssl_version) { |
| case CURL_SSLVERSION_DEFAULT: |
| case CURL_SSLVERSION_TLSv1: |
| ssl_version = CURL_SSLVERSION_TLSv1_0; |
| break; |
| } |
| |
| switch(ssl_version_max) { |
| case CURL_SSLVERSION_MAX_NONE: |
| case CURL_SSLVERSION_MAX_DEFAULT: |
| ssl_version_max = max_supported_version_by_os; |
| break; |
| } |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| if(SSLSetProtocolVersionMax != NULL) { |
| SSLProtocol darwin_ver_min = kTLSProtocol1; |
| SSLProtocol darwin_ver_max = kTLSProtocol1; |
| CURLcode result = sectransp_version_from_curl(&darwin_ver_min, |
| ssl_version); |
| if(result) { |
| failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); |
| return result; |
| } |
| result = sectransp_version_from_curl(&darwin_ver_max, |
| ssl_version_max >> 16); |
| if(result) { |
| failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); |
| return result; |
| } |
| |
| (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min); |
| (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max); |
| return result; |
| } |
| else { |
| #if CURL_SUPPORT_MAC_10_8 |
| long i = ssl_version; |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kSSLProtocolAll, |
| false); |
| for(; i <= (ssl_version_max >> 16); i++) { |
| switch(i) { |
| case CURL_SSLVERSION_TLSv1_0: |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol1, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1_1: |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol11, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1_2: |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol12, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1_3: |
| failf(data, "Your version of the OS does not support TLSv1.3"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| } |
| return CURLE_OK; |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| } |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| failf(data, "Secure Transport: cannot set SSL protocol"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| |
| static bool is_cipher_suite_strong(SSLCipherSuite suite_num) |
| { |
| for(size_t i = 0; i < NUM_OF_CIPHERS; ++i) { |
| if(ciphertable[i].num == suite_num) { |
| return !ciphertable[i].weak; |
| } |
| } |
| /* If the cipher is not in our list, assume it is a new one |
| and therefore strong. Previous implementation was the same, |
| if cipher suite is not in the list, it was considered strong enough */ |
| return true; |
| } |
| |
| static bool is_separator(char c) |
| { |
| /* Return whether character is a cipher list separator. */ |
| switch(c) { |
| case ' ': |
| case '\t': |
| case ':': |
| case ',': |
| case ';': |
| return true; |
| } |
| return false; |
| } |
| |
| static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, |
| SSLContextRef ssl_ctx) |
| { |
| size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i; |
| SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL; |
| OSStatus err = noErr; |
| |
| #if CURL_BUILD_MAC |
| int darwinver_maj = 0, darwinver_min = 0; |
| |
| GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); |
| #endif /* CURL_BUILD_MAC */ |
| |
| /* Disable cipher suites that ST supports but are not safe. These ciphers |
| are unlikely to be used in any case since ST gives other ciphers a much |
| higher priority, but it's probably better that we not connect at all than |
| to give the user a false sense of security if the server only supports |
| insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */ |
| err = SSLGetNumberSupportedCiphers(ssl_ctx, &all_ciphers_count); |
| if(err != noErr) { |
| failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d", |
| err); |
| return CURLE_SSL_CIPHER; |
| } |
| all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); |
| if(!all_ciphers) { |
| failf(data, "SSL: Failed to allocate memory for all ciphers"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite)); |
| if(!allowed_ciphers) { |
| Curl_safefree(all_ciphers); |
| failf(data, "SSL: Failed to allocate memory for allowed ciphers"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| err = SSLGetSupportedCiphers(ssl_ctx, all_ciphers, |
| &all_ciphers_count); |
| if(err != noErr) { |
| Curl_safefree(all_ciphers); |
| Curl_safefree(allowed_ciphers); |
| return CURLE_SSL_CIPHER; |
| } |
| for(i = 0UL ; i < all_ciphers_count ; i++) { |
| #if CURL_BUILD_MAC |
| /* There's a known bug in early versions of Mountain Lion where ST's ECC |
| ciphers (cipher suite 0xC001 through 0xC032) simply do not work. |
| Work around the problem here by disabling those ciphers if we are |
| running in an affected version of OS X. */ |
| if(darwinver_maj == 12 && darwinver_min <= 3 && |
| all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) { |
| continue; |
| } |
| #endif /* CURL_BUILD_MAC */ |
| if(is_cipher_suite_strong(all_ciphers[i])) { |
| allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i]; |
| } |
| } |
| err = SSLSetEnabledCiphers(ssl_ctx, allowed_ciphers, |
| allowed_ciphers_count); |
| Curl_safefree(all_ciphers); |
| Curl_safefree(allowed_ciphers); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); |
| return CURLE_SSL_CIPHER; |
| } |
| return CURLE_OK; |
| } |
| |
| static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, |
| SSLContextRef ssl_ctx, |
| const char *ciphers) |
| { |
| size_t ciphers_count = 0; |
| const char *cipher_start = ciphers; |
| OSStatus err = noErr; |
| SSLCipherSuite selected_ciphers[NUM_OF_CIPHERS]; |
| |
| if(!ciphers) |
| return CURLE_OK; |
| |
| while(is_separator(*ciphers)) /* Skip initial separators. */ |
| ciphers++; |
| if(!*ciphers) |
| return CURLE_OK; |
| |
| cipher_start = ciphers; |
| while(*cipher_start && ciphers_count < NUM_OF_CIPHERS) { |
| bool cipher_found = FALSE; |
| size_t cipher_len = 0; |
| const char *cipher_end = NULL; |
| bool tls_name = FALSE; |
| |
| /* Skip separators */ |
| while(is_separator(*cipher_start)) |
| cipher_start++; |
| if(*cipher_start == '\0') { |
| break; |
| } |
| /* Find last position of a cipher in the ciphers string */ |
| cipher_end = cipher_start; |
| while (*cipher_end != '\0' && !is_separator(*cipher_end)) { |
| ++cipher_end; |
| } |
| |
| /* IANA cipher names start with the TLS_ or SSL_ prefix. |
| If the 4th symbol of the cipher is '_' we look for a cipher in the |
| table by its (TLS) name. |
| Otherwise, we try to match cipher by an alias. */ |
| if(cipher_start[3] == '_') { |
| tls_name = TRUE; |
| } |
| /* Iterate through the cipher table and look for the cipher, starting |
| the cipher number 0x01 because the 0x00 is not the real cipher */ |
| cipher_len = cipher_end - cipher_start; |
| for(size_t i = 1; i < NUM_OF_CIPHERS; ++i) { |
| const char *table_cipher_name = NULL; |
| if(tls_name) { |
| table_cipher_name = ciphertable[i].name; |
| } |
| else if(ciphertable[i].alias_name != NULL) { |
| table_cipher_name = ciphertable[i].alias_name; |
| } |
| else { |
| continue; |
| } |
| /* Compare a part of the string between separators with a cipher name |
| in the table and make sure we matched the whole cipher name */ |
| if(strncmp(cipher_start, table_cipher_name, cipher_len) == 0 |
| && table_cipher_name[cipher_len] == '\0') { |
| selected_ciphers[ciphers_count] = ciphertable[i].num; |
| ++ciphers_count; |
| cipher_found = TRUE; |
| break; |
| } |
| } |
| if(!cipher_found) { |
| /* It would be more human-readable if we print the wrong cipher name |
| but we don't want to allocate any additional memory and copy the name |
| into it, then add it into logs. |
| Also, we do not modify an original cipher list string. We just point |
| to positions where cipher starts and ends in the cipher list string. |
| The message is a bit cryptic and longer than necessary but can be |
| understood by humans. */ |
| failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name" |
| " starting position %d and ending position %d", |
| ciphers, |
| cipher_start - ciphers, |
| cipher_end - ciphers); |
| return CURLE_SSL_CIPHER; |
| } |
| if(*cipher_end) { |
| cipher_start = cipher_end + 1; |
| } |
| else { |
| break; |
| } |
| } |
| /* All cipher suites in the list are found. Report to logs as-is */ |
| infof(data, "SSL: Setting cipher suites list \"%s\"", ciphers); |
| |
| err = SSLSetEnabledCiphers(ssl_ctx, selected_ciphers, ciphers_count); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); |
| return CURLE_SSL_CIPHER; |
| } |
| return CURLE_OK; |
| } |
| |
| static CURLcode sectransp_connect_step1(struct Curl_easy *data, |
| struct connectdata *conn, |
| int sockindex) |
| { |
| curl_socket_t sockfd = conn->sock[sockindex]; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| const struct curl_blob *ssl_cablob = SSL_CONN_CONFIG(ca_info_blob); |
| const char * const ssl_cafile = |
| /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ |
| (ssl_cablob ? NULL : SSL_CONN_CONFIG(CAfile)); |
| const bool verifypeer = SSL_CONN_CONFIG(verifypeer); |
| char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); |
| const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); |
| bool isproxy = SSL_IS_PROXY(); |
| const char * const hostname = SSL_HOST_NAME(); |
| const long int port = SSL_HOST_PORT(); |
| #ifdef ENABLE_IPV6 |
| struct in6_addr addr; |
| #else |
| struct in_addr addr; |
| #endif /* ENABLE_IPV6 */ |
| char *ciphers; |
| OSStatus err = noErr; |
| #if CURL_BUILD_MAC |
| int darwinver_maj = 0, darwinver_min = 0; |
| |
| GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); |
| #endif /* CURL_BUILD_MAC */ |
| |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| if(SSLCreateContext != NULL) { /* use the newer API if available */ |
| if(backend->ssl_ctx) |
| CFRelease(backend->ssl_ctx); |
| backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); |
| if(!backend->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 CURL_SUPPORT_MAC_10_8 |
| if(backend->ssl_ctx) |
| (void)SSLDisposeContext(backend->ssl_ctx); |
| err = SSLNewContext(false, &(backend->ssl_ctx)); |
| if(err != noErr) { |
| failf(data, "SSL: couldn't create a context: OSStatus %d", err); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| } |
| #else |
| if(backend->ssl_ctx) |
| (void)SSLDisposeContext(backend->ssl_ctx); |
| err = SSLNewContext(false, &(backend->ssl_ctx)); |
| if(err != noErr) { |
| failf(data, "SSL: couldn't create a context: OSStatus %d", err); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| backend->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 CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| if(SSLSetProtocolVersionMax != NULL) { |
| switch(conn->ssl_config.version) { |
| case CURL_SSLVERSION_TLSv1: |
| (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1); |
| #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 |
| if(__builtin_available(macOS 10.13, iOS 11.0, *)) { |
| (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13); |
| } |
| else { |
| (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); |
| } |
| #else |
| (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12); |
| #endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && |
| HAVE_BUILTIN_AVAILABLE == 1 */ |
| break; |
| case CURL_SSLVERSION_DEFAULT: |
| case CURL_SSLVERSION_TLSv1_0: |
| case CURL_SSLVERSION_TLSv1_1: |
| case CURL_SSLVERSION_TLSv1_2: |
| case CURL_SSLVERSION_TLSv1_3: |
| { |
| CURLcode result = set_ssl_version_min_max(data, conn, sockindex); |
| if(result != CURLE_OK) |
| return result; |
| break; |
| } |
| case CURL_SSLVERSION_SSLv3: |
| case CURL_SSLVERSION_SSLv2: |
| failf(data, "SSL versions not supported"); |
| return CURLE_NOT_BUILT_IN; |
| default: |
| failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| } |
| else { |
| #if CURL_SUPPORT_MAC_10_8 |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kSSLProtocolAll, |
| false); |
| switch(conn->ssl_config.version) { |
| case CURL_SSLVERSION_DEFAULT: |
| case CURL_SSLVERSION_TLSv1: |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol1, |
| true); |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol11, |
| true); |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol12, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1_0: |
| case CURL_SSLVERSION_TLSv1_1: |
| case CURL_SSLVERSION_TLSv1_2: |
| case CURL_SSLVERSION_TLSv1_3: |
| { |
| CURLcode result = set_ssl_version_min_max(data, conn, sockindex); |
| if(result != CURLE_OK) |
| return result; |
| break; |
| } |
| case CURL_SSLVERSION_SSLv3: |
| case CURL_SSLVERSION_SSLv2: |
| failf(data, "SSL versions not supported"); |
| return CURLE_NOT_BUILT_IN; |
| default: |
| failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| } |
| #else |
| if(conn->ssl_config.version_max != CURL_SSLVERSION_MAX_NONE) { |
| failf(data, "Your version of the OS does not support to set maximum" |
| " SSL/TLS version"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false); |
| switch(conn->ssl_config.version) { |
| case CURL_SSLVERSION_DEFAULT: |
| case CURL_SSLVERSION_TLSv1: |
| case CURL_SSLVERSION_TLSv1_0: |
| (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, |
| kTLSProtocol1, |
| true); |
| break; |
| case CURL_SSLVERSION_TLSv1_1: |
| failf(data, "Your version of the OS does not support TLSv1.1"); |
| return CURLE_SSL_CONNECT_ERROR; |
| case CURL_SSLVERSION_TLSv1_2: |
| failf(data, "Your version of the OS does not support TLSv1.2"); |
| return CURLE_SSL_CONNECT_ERROR; |
| case CURL_SSLVERSION_TLSv1_3: |
| failf(data, "Your version of the OS does not support TLSv1.3"); |
| return CURLE_SSL_CONNECT_ERROR; |
| case CURL_SSLVERSION_SSLv2: |
| case CURL_SSLVERSION_SSLv3: |
| failf(data, "SSL versions not supported"); |
| return CURLE_NOT_BUILT_IN; |
| default: |
| failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| |
| #if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 |
| if(conn->bits.tls_enable_alpn) { |
| if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { |
| CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, |
| &kCFTypeArrayCallBacks); |
| |
| #ifdef USE_HTTP2 |
| if(data->state.httpwant >= CURL_HTTP_VERSION_2 |
| #ifndef CURL_DISABLE_PROXY |
| && (!isproxy || !conn->bits.tunnel_proxy) |
| #endif |
| ) { |
| CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2)); |
| infof(data, "ALPN, offering %s", ALPN_H2); |
| } |
| #endif |
| |
| CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1)); |
| infof(data, "ALPN, offering %s", ALPN_HTTP_1_1); |
| |
| /* expects length prefixed preference ordered list of protocols in wire |
| * format |
| */ |
| err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); |
| if(err != noErr) |
| infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", |
| err); |
| CFRelease(alpnArr); |
| } |
| } |
| #endif |
| |
| if(SSL_SET_OPTION(key)) { |
| infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " |
| "Transport. The private key must be in the Keychain."); |
| } |
| |
| if(ssl_cert || ssl_cert_blob) { |
| bool is_cert_data = ssl_cert_blob != NULL; |
| bool is_cert_file = (!is_cert_data) && is_file(ssl_cert); |
| SecIdentityRef cert_and_key = NULL; |
| |
| /* User wants to authenticate with a client cert. Look for it. Assume that |
| the user wants to use an identity loaded from the Keychain. If not, try |
| it as a file on disk */ |
| |
| if(!is_cert_data) |
| err = CopyIdentityWithLabel(ssl_cert, &cert_and_key); |
| else |
| err = !noErr; |
| if((err != noErr) && (is_cert_file || is_cert_data)) { |
| if(!SSL_SET_OPTION(cert_type)) |
| infof(data, "SSL: Certificate type not set, assuming " |
| "PKCS#12 format."); |
| else if(!strcasecompare(SSL_SET_OPTION(cert_type), "P12")) { |
| failf(data, "SSL: The Security framework only supports " |
| "loading identities that are in PKCS#12 format."); |
| return CURLE_SSL_CERTPROBLEM; |
| } |
| |
| err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, |
| SSL_SET_OPTION(key_passwd), |
| &cert_and_key); |
| } |
| |
| if(err == noErr && cert_and_key) { |
| SecCertificateRef cert = NULL; |
| CFTypeRef certs_c[1]; |
| CFArrayRef certs; |
| |
| /* If we found one, print it out: */ |
| err = SecIdentityCopyCertificate(cert_and_key, &cert); |
| if(err == noErr) { |
| char *certp; |
| CURLcode result = CopyCertSubject(data, cert, &certp); |
| if(!result) { |
| infof(data, "Client certificate: %s", certp); |
| free(certp); |
| } |
| |
| CFRelease(cert); |
| if(result == CURLE_PEER_FAILED_VERIFICATION) |
| return CURLE_SSL_CERTPROBLEM; |
| if(result) |
| return result; |
| } |
| certs_c[0] = cert_and_key; |
| certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, |
| &kCFTypeArrayCallBacks); |
| err = SSLSetCertificate(backend->ssl_ctx, certs); |
| if(certs) |
| CFRelease(certs); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err); |
| return CURLE_SSL_CERTPROBLEM; |
| } |
| CFRelease(cert_and_key); |
| } |
| else { |
| const char *cert_showfilename_error = |
| is_cert_data ? "(memory blob)" : ssl_cert; |
| |
| switch(err) { |
| case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ |
| failf(data, "SSL: Incorrect password for the certificate \"%s\" " |
| "and its private key.", cert_showfilename_error); |
| break; |
| case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ |
| failf(data, "SSL: Couldn't make sense of the data in the " |
| "certificate \"%s\" and its private key.", |
| cert_showfilename_error); |
| break; |
| case -25260: /* errSecPassphraseRequired */ |
| failf(data, "SSL The certificate \"%s\" requires a password.", |
| cert_showfilename_error); |
| break; |
| case errSecItemNotFound: |
| failf(data, "SSL: Can't find the certificate \"%s\" and its private " |
| "key in the Keychain.", cert_showfilename_error); |
| break; |
| default: |
| failf(data, "SSL: Can't load the certificate \"%s\" and its private " |
| "key: OSStatus %d", cert_showfilename_error, err); |
| break; |
| } |
| return CURLE_SSL_CERTPROBLEM; |
| } |
| } |
| |
| /* 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 CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS |
| /* 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, Lion or |
| Mountain 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.) |
| Note: |
| Darwin 11.x.x is Lion (10.7) |
| Darwin 12.x.x is Mountain Lion (10.8) |
| Darwin 13.x.x is Mavericks (10.9) |
| Darwin 14.x.x is Yosemite (10.10) |
| Darwin 15.x.x is El Capitan (10.11) |
| */ |
| #if CURL_BUILD_MAC |
| if(SSLSetSessionOption != NULL && darwinver_maj >= 13) { |
| #else |
| if(SSLSetSessionOption != NULL) { |
| #endif /* CURL_BUILD_MAC */ |
| bool break_on_auth = !conn->ssl_config.verifypeer || |
| ssl_cafile || ssl_cablob; |
| err = SSLSetSessionOption(backend->ssl_ctx, |
| kSSLSessionOptionBreakOnServerAuth, |
| break_on_auth); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| } |
| else { |
| #if CURL_SUPPORT_MAC_10_8 |
| err = SSLSetEnableCertVerify(backend->ssl_ctx, |
| conn->ssl_config.verifypeer?true:false); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| } |
| #else |
| err = SSLSetEnableCertVerify(backend->ssl_ctx, |
| conn->ssl_config.verifypeer?true:false); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| #endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ |
| |
| if((ssl_cafile || ssl_cablob) && verifypeer) { |
| bool is_cert_data = ssl_cablob != NULL; |
| bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); |
| |
| if(!(is_cert_file || is_cert_data)) { |
| failf(data, "SSL: can't load CA certificate file %s", |
| ssl_cafile ? ssl_cafile : "(blob memory)"); |
| return CURLE_SSL_CACERT_BADFILE; |
| } |
| } |
| |
| /* Configure hostname check. SNI is used if available. |
| * Both hostname check and SNI require SSLSetPeerDomainName(). |
| * Also: the verifyhost setting influences SNI usage */ |
| if(conn->ssl_config.verifyhost) { |
| err = SSLSetPeerDomainName(backend->ssl_ctx, hostname, |
| strlen(hostname)); |
| |
| if(err != noErr) { |
| infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d", |
| err); |
| } |
| |
| if((Curl_inet_pton(AF_INET, hostname, &addr)) |
| #ifdef ENABLE_IPV6 |
| || (Curl_inet_pton(AF_INET6, hostname, &addr)) |
| #endif |
| ) { |
| infof(data, "WARNING: using IP address, SNI is being disabled by " |
| "the OS."); |
| } |
| } |
| else { |
| infof(data, "WARNING: disabling hostname validation also disables SNI."); |
| } |
| |
| ciphers = SSL_CONN_CONFIG(cipher_list); |
| if(ciphers) { |
| err = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); |
| } |
| else { |
| err = sectransp_set_default_ciphers(data, backend->ssl_ctx); |
| } |
| if(err != noErr) { |
| failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. " |
| "Error code: %d", err); |
| return CURLE_SSL_CIPHER; |
| } |
| |
| #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
| /* We want to enable 1/n-1 when using a CBC cipher unless the user |
| specifically doesn't want us doing that: */ |
| if(SSLSetSessionOption != NULL) { |
| SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, |
| !SSL_SET_OPTION(enable_beast)); |
| SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, |
| data->set.ssl.falsestart); /* false start support */ |
| } |
| #endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ |
| |
| /* Check if there's a cached ID we can/should use here! */ |
| if(SSL_SET_OPTION(primary.sessionid)) { |
| char *ssl_sessionid; |
| size_t ssl_sessionid_len; |
| |
| Curl_ssl_sessionid_lock(data); |
| if(!Curl_ssl_getsessionid(data, conn, isproxy, (void **)&ssl_sessionid, |
| &ssl_sessionid_len, sockindex)) { |
| /* we got a session id, use it! */ |
| err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); |
| Curl_ssl_sessionid_unlock(data); |
| if(err != noErr) { |
| failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| /* Informational message */ |
| infof(data, "SSL re-using session ID"); |
| } |
| /* If there isn't one, then let's make one up! This has to be done prior |
| to starting the handshake. */ |
| else { |
| CURLcode result; |
| ssl_sessionid = |
| aprintf("%s:%d:%d:%s:%ld", |
| ssl_cafile ? ssl_cafile : "(blob memory)", |
| verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port); |
| ssl_sessionid_len = strlen(ssl_sessionid); |
| |
| err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); |
| if(err != noErr) { |
| Curl_ssl_sessionid_unlock(data); |
| failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| |
| result = Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, |
| ssl_sessionid_len, sockindex); |
| Curl_ssl_sessionid_unlock(data); |
| if(result) { |
| failf(data, "failed to store ssl session"); |
| return result; |
| } |
| } |
| } |
| |
| err = SSLSetIOFuncs(backend->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. */ |
| backend->ssl_sockfd = sockfd; |
| err = SSLSetConnection(backend->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 long pem_to_der(const char *in, unsigned char **out, size_t *outlen) |
| { |
| char *sep_start, *sep_end, *cert_start, *cert_end; |
| size_t i, j, err; |
| size_t len; |
| unsigned char *b64; |
| |
| /* Jump through the separators at the beginning of the certificate. */ |
| sep_start = strstr(in, "-----"); |
| if(!sep_start) |
| return 0; |
| cert_start = strstr(sep_start + 1, "-----"); |
| if(!cert_start) |
| return -1; |
| |
| cert_start += 5; |
| |
| /* Find separator after the end of the certificate. */ |
| cert_end = strstr(cert_start, "-----"); |
| if(!cert_end) |
| return -1; |
| |
| sep_end = strstr(cert_end + 1, "-----"); |
| if(!sep_end) |
| return -1; |
| sep_end += 5; |
| |
| len = cert_end - cert_start; |
| b64 = malloc(len + 1); |
| if(!b64) |
| return -1; |
| |
| /* Create base64 string without linefeeds. */ |
| for(i = 0, j = 0; i < len; i++) { |
| if(cert_start[i] != '\r' && cert_start[i] != '\n') |
| b64[j++] = cert_start[i]; |
| } |
| b64[j] = '\0'; |
| |
| err = Curl_base64_decode((const char *)b64, out, outlen); |
| free(b64); |
| if(err) { |
| free(*out); |
| return -1; |
| } |
| |
| return sep_end - in; |
| } |
| |
| static int read_cert(const char *file, unsigned char **out, size_t *outlen) |
| { |
| int fd; |
| ssize_t n, len = 0, cap = 512; |
| unsigned char buf[512], *data; |
| |
| fd = open(file, 0); |
| if(fd < 0) |
| return -1; |
| |
| data = malloc(cap); |
| if(!data) { |
| close(fd); |
| return -1; |
| } |
| |
| for(;;) { |
| n = read(fd, buf, sizeof(buf)); |
| if(n < 0) { |
| close(fd); |
| free(data); |
| return -1; |
| } |
| else if(n == 0) { |
| close(fd); |
| break; |
| } |
| |
| if(len + n >= cap) { |
| cap *= 2; |
| data = Curl_saferealloc(data, cap); |
| if(!data) { |
| close(fd); |
| return -1; |
| } |
| } |
| |
| memcpy(data + len, buf, n); |
| len += n; |
| } |
| data[len] = '\0'; |
| |
| *out = data; |
| *outlen = len; |
| |
| return 0; |
| } |
| |
| static int append_cert_to_array(struct Curl_easy *data, |
| const unsigned char *buf, size_t buflen, |
| CFMutableArrayRef array) |
| { |
| CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen); |
| char *certp; |
| CURLcode result; |
| if(!certdata) { |
| failf(data, "SSL: failed to allocate array for CA certificate"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| |
| SecCertificateRef cacert = |
| SecCertificateCreateWithData(kCFAllocatorDefault, certdata); |
| CFRelease(certdata); |
| if(!cacert) { |
| failf(data, "SSL: failed to create SecCertificate from CA certificate"); |
| return CURLE_SSL_CACERT_BADFILE; |
| } |
| |
| /* Check if cacert is valid. */ |
| result = CopyCertSubject(data, cacert, &certp); |
| switch(result) { |
| case CURLE_OK: |
| break; |
| case CURLE_PEER_FAILED_VERIFICATION: |
| return CURLE_SSL_CACERT_BADFILE; |
| case CURLE_OUT_OF_MEMORY: |
| default: |
| return result; |
| } |
| free(certp); |
| |
| CFArrayAppendValue(array, cacert); |
| CFRelease(cacert); |
| |
| return CURLE_OK; |
| } |
| |
| static CURLcode verify_cert_buf(struct Curl_easy *data, |
| const unsigned char *certbuf, size_t buflen, |
| SSLContextRef ctx) |
| { |
| int n = 0, rc; |
| long res; |
| unsigned char *der; |
| size_t derlen, offset = 0; |
| |
| /* |
| * Certbuf now contains the contents of the certificate file, which can be |
| * - a single DER certificate, |
| * - a single PEM certificate or |
| * - a bunch of PEM certificates (certificate bundle). |
| * |
| * Go through certbuf, and convert any PEM certificate in it into DER |
| * format. |
| */ |
| CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0, |
| &kCFTypeArrayCallBacks); |
| if(!array) { |
| failf(data, "SSL: out of memory creating CA certificate array"); |
| return CURLE_OUT_OF_MEMORY; |
| } |
| |
| while(offset < buflen) { |
| n++; |
| |
| /* |
| * Check if the certificate is in PEM format, and convert it to DER. If |
| * this fails, we assume the certificate is in DER format. |
| */ |
| res = pem_to_der((const char *)certbuf + offset, &der, &derlen); |
| if(res < 0) { |
| CFRelease(array); |
| failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", |
| n, offset); |
| return CURLE_SSL_CACERT_BADFILE; |
| } |
| offset += res; |
| |
| if(res == 0 && offset == 0) { |
| /* This is not a PEM file, probably a certificate in DER format. */ |
| rc = append_cert_to_array(data, certbuf, buflen, array); |
| if(rc != CURLE_OK) { |
| CFRelease(array); |
| return rc; |
| } |
| break; |
| } |
| else if(res == 0) { |
| /* No more certificates in the bundle. */ |
| break; |
| } |
| |
| rc = append_cert_to_array(data, der, derlen, array); |
| free(der); |
| if(rc != CURLE_OK) { |
| CFRelease(array); |
| return rc; |
| } |
| } |
| |
| SecTrustRef trust; |
| OSStatus ret = SSLCopyPeerTrust(ctx, &trust); |
| if(!trust) { |
| failf(data, "SSL: error getting certificate chain"); |
| CFRelease(array); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| else if(ret != noErr) { |
| CFRelease(array); |
| failf(data, "SSLCopyPeerTrust() returned error %d", ret); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| |
| ret = SecTrustSetAnchorCertificates(trust, array); |
| if(ret != noErr) { |
| CFRelease(array); |
| CFRelease(trust); |
| failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| ret = SecTrustSetAnchorCertificatesOnly(trust, true); |
| if(ret != noErr) { |
| CFRelease(array); |
| CFRelease(trust); |
| failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| |
| SecTrustResultType trust_eval = 0; |
| ret = SecTrustEvaluate(trust, &trust_eval); |
| CFRelease(array); |
| CFRelease(trust); |
| if(ret != noErr) { |
| failf(data, "SecTrustEvaluate() returned error %d", ret); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| |
| switch(trust_eval) { |
| case kSecTrustResultUnspecified: |
| case kSecTrustResultProceed: |
| return CURLE_OK; |
| |
| case kSecTrustResultRecoverableTrustFailure: |
| case kSecTrustResultDeny: |
| default: |
| failf(data, "SSL: certificate verification failed (result: %d)", |
| trust_eval); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| } |
| } |
| |
| static CURLcode verify_cert(struct Curl_easy *data, const char *cafile, |
| const struct curl_blob *ca_info_blob, |
| SSLContextRef ctx) |
| { |
| int result; |
| unsigned char *certbuf; |
| size_t buflen; |
| |
| if(ca_info_blob) { |
| certbuf = (unsigned char *)malloc(ca_info_blob->len + 1); |
| if(!certbuf) { |
| return CURLE_OUT_OF_MEMORY; |
| } |
| buflen = ca_info_blob->len; |
| memcpy(certbuf, ca_info_blob->data, ca_info_blob->len); |
| certbuf[ca_info_blob->len]='\0'; |
| } |
| else if(cafile) { |
| if(read_cert(cafile, &certbuf, &buflen) < 0) { |
| failf(data, "SSL: failed to read or invalid CA certificate"); |
| return CURLE_SSL_CACERT_BADFILE; |
| } |
| } |
| else |
| return CURLE_SSL_CACERT_BADFILE; |
| |
| result = verify_cert_buf(data, certbuf, buflen, ctx); |
| free(certbuf); |
| return result; |
| } |
| |
| |
| #ifdef SECTRANSP_PINNEDPUBKEY |
| static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, |
| SSLContextRef ctx, |
| const char *pinnedpubkey) |
| { /* Scratch */ |
| size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24; |
| unsigned char *pubkey = NULL, *realpubkey = NULL; |
| const unsigned char *spkiHeader = NULL; |
| CFDataRef publicKeyBits = NULL; |
| |
| /* Result is returned to caller */ |
| CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; |
| |
| /* if a path wasn't specified, don't pin */ |
| if(!pinnedpubkey) |
| return CURLE_OK; |
| |
| |
| if(!ctx) |
| return result; |
| |
| do { |
| SecTrustRef trust; |
| OSStatus ret = SSLCopyPeerTrust(ctx, &trust); |
| if(ret != noErr || !trust) |
| break; |
| |
| SecKeyRef keyRef = SecTrustCopyPublicKey(trust); |
| CFRelease(trust); |
| if(!keyRef) |
| break; |
| |
| #ifdef SECTRANSP_PINNEDPUBKEY_V1 |
| |
| publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL); |
| CFRelease(keyRef); |
| if(!publicKeyBits) |
| break; |
| |
| #elif SECTRANSP_PINNEDPUBKEY_V2 |
| |
| OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, |
| &publicKeyBits); |
| CFRelease(keyRef); |
| if(success != errSecSuccess || !publicKeyBits) |
| break; |
| |
| #endif /* SECTRANSP_PINNEDPUBKEY_V2 */ |
| |
| pubkeylen = CFDataGetLength(publicKeyBits); |
| pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits); |
| |
| switch(pubkeylen) { |
| case 526: |
| /* 4096 bit RSA pubkeylen == 526 */ |
| spkiHeader = rsa4096SpkiHeader; |
| break; |
| case 270: |
| /* 2048 bit RSA pubkeylen == 270 */ |
| spkiHeader = rsa2048SpkiHeader; |
| break; |
| #ifdef SECTRANSP_PINNEDPUBKEY_V1 |
| case 65: |
| /* ecDSA secp256r1 pubkeylen == 65 */ |
| spkiHeader = ecDsaSecp256r1SpkiHeader; |
| spkiHeaderLength = 26; |
| break; |
| case 97: |
| /* ecDSA secp384r1 pubkeylen == 97 */ |
| spkiHeader = ecDsaSecp384r1SpkiHeader; |
| spkiHeaderLength = 23; |
| break; |
| default: |
| infof(data, "SSL: unhandled public key length: %d", pubkeylen); |
| #elif SECTRANSP_PINNEDPUBKEY_V2 |
| default: |
| /* ecDSA secp256r1 pubkeylen == 91 header already included? |
| * ecDSA secp384r1 header already included too |
| * we assume rest of algorithms do same, so do nothing |
| */ |
| result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey, |
| pubkeylen); |
| #endif /* SECTRANSP_PINNEDPUBKEY_V2 */ |
| continue; /* break from loop */ |
| } |
| |
| realpubkeylen = pubkeylen + spkiHeaderLength; |
| realpubkey = malloc(realpubkeylen); |
| if(!realpubkey) |
| break; |
| |
| memcpy(realpubkey, spkiHeader, spkiHeaderLength); |
| memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen); |
| |
| result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey, |
| realpubkeylen); |
| |
| } while(0); |
| |
| Curl_safefree(realpubkey); |
| if(publicKeyBits != NULL) |
| CFRelease(publicKeyBits); |
| |
| return result; |
| } |
| #endif /* SECTRANSP_PINNEDPUBKEY */ |
| |
| static CURLcode |
| sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, |
| int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| OSStatus err; |
| SSLCipherSuite cipher; |
| SSLProtocol protocol = 0; |
| const char * const hostname = SSL_HOST_NAME(); |
| |
| 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(backend->ssl_ctx); |
| |
| if(err != noErr) { |
| switch(err) { |
| case errSSLWouldBlock: /* they're not done with us yet */ |
| connssl->connecting_state = backend->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: |
| if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && |
| SSL_CONN_CONFIG(verifypeer)) { |
| CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), |
| SSL_CONN_CONFIG(ca_info_blob), |
| backend->ssl_ctx); |
| if(result) |
| return result; |
| } |
| /* the documentation says we need to call SSLHandshake() again */ |
| return sectransp_connect_step2(data, conn, sockindex); |
| |
| /* Problem with encrypt / decrypt */ |
| case errSSLPeerDecodeError: |
| failf(data, "Decode failed"); |
| break; |
| case errSSLDecryptionFail: |
| case errSSLPeerDecryptionFail: |
| failf(data, "Decryption failed"); |
| break; |
| case errSSLPeerDecryptError: |
| failf(data, "A decryption error occurred"); |
| break; |
| case errSSLBadCipherSuite: |
| failf(data, "A bad SSL cipher suite was encountered"); |
| break; |
| case errSSLCrypto: |
| failf(data, "An underlying cryptographic error was encountered"); |
| break; |
| #if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 |
| case errSSLWeakPeerEphemeralDHKey: |
| failf(data, "Indicates a weak ephemeral Diffie-Hellman key"); |
| break; |
| #endif |
| |
| /* Problem with the message record validation */ |
| case errSSLBadRecordMac: |
| case errSSLPeerBadRecordMac: |
| failf(data, "A record with a bad message authentication code (MAC) " |
| "was encountered"); |
| break; |
| case errSSLRecordOverflow: |
| case errSSLPeerRecordOverflow: |
| failf(data, "A record overflow occurred"); |
| break; |
| |
| /* Problem with zlib decompression */ |
| case errSSLPeerDecompressFail: |
| failf(data, "Decompression failed"); |
| break; |
| |
| /* Problem with access */ |
| case errSSLPeerAccessDenied: |
| failf(data, "Access was denied"); |
| break; |
| case errSSLPeerInsufficientSecurity: |
| failf(data, "There is insufficient security for this operation"); |
| break; |
| |
| /* These are all certificate problems with the server: */ |
| case errSSLXCertChainInvalid: |
| failf(data, "SSL certificate problem: Invalid certificate chain"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLUnknownRootCert: |
| failf(data, "SSL certificate problem: Untrusted root certificate"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLNoRootCert: |
| failf(data, "SSL certificate problem: No root certificate"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLCertNotYetValid: |
| failf(data, "SSL certificate problem: The certificate chain had a " |
| "certificate that is not yet valid"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLCertExpired: |
| case errSSLPeerCertExpired: |
| failf(data, "SSL certificate problem: Certificate chain had an " |
| "expired certificate"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLBadCert: |
| case errSSLPeerBadCert: |
| failf(data, "SSL certificate problem: Couldn't understand the server " |
| "certificate format"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLPeerUnsupportedCert: |
| failf(data, "SSL certificate problem: An unsupported certificate " |
| "format was encountered"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLPeerCertRevoked: |
| failf(data, "SSL certificate problem: The certificate was revoked"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| case errSSLPeerCertUnknown: |
| failf(data, "SSL certificate problem: The certificate is unknown"); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| |
| /* These are all certificate problems with the client: */ |
| case errSecAuthFailed: |
| failf(data, "SSL authentication failed"); |
| break; |
| case errSSLPeerHandshakeFail: |
| failf(data, "SSL peer handshake failed, the server most likely " |
| "requires a client certificate to connect"); |
| break; |
| case errSSLPeerUnknownCA: |
| failf(data, "SSL server rejected the client certificate due to " |
| "the certificate being signed by an unknown certificate " |
| "authority"); |
| break; |
| |
| /* This error is raised if the server's cert didn't match the server's |
| host name: */ |
| case errSSLHostNameMismatch: |
| failf(data, "SSL certificate peer verification failed, the " |
| "certificate did not match \"%s\"\n", conn->host.dispname); |
| return CURLE_PEER_FAILED_VERIFICATION; |
| |
| /* Problem with SSL / TLS negotiation */ |
| case errSSLNegotiation: |
| failf(data, "Could not negotiate an SSL cipher suite with the server"); |
| break; |
| case errSSLBadConfiguration: |
| failf(data, "A configuration error occurred"); |
| break; |
| case errSSLProtocol: |
| failf(data, "SSL protocol error"); |
| break; |
| case errSSLPeerProtocolVersion: |
| failf(data, "A bad protocol version was encountered"); |
| break; |
| case errSSLPeerNoRenegotiation: |
| failf(data, "No renegotiation is allowed"); |
| break; |
| |
| /* Generic handshake errors: */ |
| case errSSLConnectionRefused: |
| failf(data, "Server dropped the connection during the SSL handshake"); |
| break; |
| case errSSLClosedAbort: |
| failf(data, "Server aborted the SSL handshake"); |
| break; |
| case errSSLClosedGraceful: |
| failf(data, "The connection closed gracefully"); |
| break; |
| case errSSLClosedNoNotify: |
| failf(data, "The server closed the session with no notification"); |
| break; |
| /* Sometimes paramErr happens with buggy ciphers: */ |
| case paramErr: |
| case errSSLInternal: |
| case errSSLPeerInternalError: |
| failf(data, "Internal SSL engine error encountered during the " |
| "SSL handshake"); |
| break; |
| case errSSLFatalAlert: |
| failf(data, "Fatal SSL engine error encountered during the SSL " |
| "handshake"); |
| break; |
| /* Unclassified error */ |
| case errSSLBufferOverflow: |
| failf(data, "An insufficient buffer was provided"); |
| break; |
| case errSSLIllegalParam: |
| failf(data, "An illegal parameter was encountered"); |
| break; |
| case errSSLModuleAttach: |
| failf(data, "Module attach failure"); |
| break; |
| case errSSLSessionNotFound: |
| failf(data, "An attempt to restore an unknown session failed"); |
| break; |
| case errSSLPeerExportRestriction: |
| failf(data, "An export restriction occurred"); |
| break; |
| case errSSLPeerUserCancelled: |
| failf(data, "The user canceled the operation"); |
| break; |
| case errSSLPeerUnexpectedMsg: |
| failf(data, "Peer rejected unexpected message"); |
| break; |
| #if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 |
| /* Treaing non-fatal error as fatal like before */ |
| case errSSLClientHelloReceived: |
| failf(data, "A non-fatal result for providing a server name " |
| "indication"); |
| break; |
| #endif |
| |
| /* Error codes defined in the enum but should never be returned. |
| We list them here just in case. */ |
| #if CURL_BUILD_MAC_10_6 |
| /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */ |
| case errSSLClientCertRequested: |
| failf(data, "Server requested a client certificate during the " |
| "handshake"); |
| return CURLE_SSL_CLIENTCERT; |
| #endif |
| #if CURL_BUILD_MAC_10_9 |
| /* Alias for errSSLLast, end of error range */ |
| case errSSLUnexpectedRecord: |
| failf(data, "Unexpected (skipped) record in DTLS"); |
| break; |
| #endif |
| default: |
| /* May also return codes listed in Security Framework Result Codes */ |
| failf(data, "Unknown SSL protocol error in connection to %s:%d", |
| hostname, err); |
| break; |
| } |
| return CURLE_SSL_CONNECT_ERROR; |
| } |
| else { |
| /* we have been connected fine, we're not waiting for anything else. */ |
| connssl->connecting_state = ssl_connect_3; |
| |
| #ifdef SECTRANSP_PINNEDPUBKEY |
| if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) { |
| CURLcode result = |
| pkp_pin_peer_pubkey(data, backend->ssl_ctx, |
| data->set.str[STRING_SSL_PINNEDPUBLICKEY]); |
| if(result) { |
| failf(data, "SSL: public key does not match pinned public key!"); |
| return result; |
| } |
| } |
| #endif /* SECTRANSP_PINNEDPUBKEY */ |
| |
| /* Informational message */ |
| (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); |
| (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); |
| switch(protocol) { |
| case kSSLProtocol2: |
| infof(data, "SSL 2.0 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| case kSSLProtocol3: |
| infof(data, "SSL 3.0 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| case kTLSProtocol1: |
| infof(data, "TLS 1.0 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| case kTLSProtocol11: |
| infof(data, "TLS 1.1 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| case kTLSProtocol12: |
| infof(data, "TLS 1.2 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 |
| case kTLSProtocol13: |
| infof(data, "TLS 1.3 connection using %s", |
| TLSCipherNameForNumber(cipher)); |
| break; |
| #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ |
| default: |
| infof(data, "Unknown protocol connection"); |
| break; |
| } |
| |
| #if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1 |
| if(conn->bits.tls_enable_alpn) { |
| if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { |
| CFArrayRef alpnArr = NULL; |
| CFStringRef chosenProtocol = NULL; |
| err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr); |
| |
| if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1) |
| chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0); |
| |
| #ifdef USE_HTTP2 |
| if(chosenProtocol && |
| !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) { |
| conn->negnpn = CURL_HTTP_VERSION_2; |
| } |
| else |
| #endif |
| if(chosenProtocol && |
| !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) { |
| conn->negnpn = CURL_HTTP_VERSION_1_1; |
| } |
| else |
| infof(data, "ALPN, server did not agree to a protocol"); |
| |
| Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? |
| BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); |
| |
| /* chosenProtocol is a reference to the string within alpnArr |
| and doesn't need to be freed separately */ |
| if(alpnArr) |
| CFRelease(alpnArr); |
| } |
| } |
| #endif |
| |
| return CURLE_OK; |
| } |
| } |
| |
| #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| /* This should be called during step3 of the connection at the earliest */ |
| static void |
| show_verbose_server_cert(struct Curl_easy *data, |
| struct connectdata *conn, |
| int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| CFArrayRef server_certs = NULL; |
| SecCertificateRef server_cert; |
| OSStatus err; |
| CFIndex i, count; |
| SecTrustRef trust = NULL; |
| |
| if(!backend->ssl_ctx) |
| return; |
| |
| #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS |
| #if CURL_BUILD_IOS |
| #pragma unused(server_certs) |
| err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); |
| /* For some reason, SSLCopyPeerTrust() can return noErr and yet return |
| a null trust, so be on guard for that: */ |
| if(err == noErr && trust) { |
| count = SecTrustGetCertificateCount(trust); |
| for(i = 0L ; i < count ; i++) { |
| CURLcode result; |
| char *certp; |
| server_cert = SecTrustGetCertificateAtIndex(trust, i); |
| result = CopyCertSubject(data, server_cert, &certp); |
| if(!result) { |
| infof(data, "Server certificate: %s", certp); |
| free(certp); |
| } |
| } |
| 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(backend->ssl_ctx, &trust); |
| /* For some reason, SSLCopyPeerTrust() can return noErr and yet return |
| a null trust, so be on guard for that: */ |
| if(err == noErr && trust) { |
| count = SecTrustGetCertificateCount(trust); |
| for(i = 0L ; i < count ; i++) { |
| char *certp; |
| CURLcode result; |
| server_cert = SecTrustGetCertificateAtIndex(trust, i); |
| result = CopyCertSubject(data, server_cert, &certp); |
| if(!result) { |
| infof(data, "Server certificate: %s", certp); |
| free(certp); |
| } |
| } |
| CFRelease(trust); |
| } |
| } |
| else { |
| #if CURL_SUPPORT_MAC_10_8 |
| err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); |
| /* Just in case SSLCopyPeerCertificates() returns null too... */ |
| if(err == noErr && server_certs) { |
| count = CFArrayGetCount(server_certs); |
| for(i = 0L ; i < count ; i++) { |
| char *certp; |
| CURLcode result; |
| server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, |
| i); |
| result = CopyCertSubject(data, server_cert, &certp); |
| if(!result) { |
| infof(data, "Server certificate: %s", certp); |
| free(certp); |
| } |
| } |
| CFRelease(server_certs); |
| } |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| } |
| #endif /* CURL_BUILD_IOS */ |
| #else |
| #pragma unused(trust) |
| err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); |
| if(err == noErr) { |
| count = CFArrayGetCount(server_certs); |
| for(i = 0L ; i < count ; i++) { |
| CURLcode result; |
| char *certp; |
| server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i); |
| result = CopyCertSubject(data, server_cert, &certp); |
| if(!result) { |
| infof(data, "Server certificate: %s", certp); |
| free(certp); |
| } |
| } |
| CFRelease(server_certs); |
| } |
| #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ |
| } |
| #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ |
| |
| static CURLcode |
| sectransp_connect_step3(struct Curl_easy *data, struct connectdata *conn, |
| int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| |
| /* There is no step 3! |
| * Well, okay, if verbose mode is on, let's print the details of the |
| * server certificates. */ |
| #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| if(data->set.verbose) |
| show_verbose_server_cert(data, conn, sockindex); |
| #endif |
| |
| connssl->connecting_state = ssl_connect_done; |
| return CURLE_OK; |
| } |
| |
| static Curl_recv sectransp_recv; |
| static Curl_send sectransp_send; |
| |
| static CURLcode |
| sectransp_connect_common(struct Curl_easy *data, |
| struct connectdata *conn, |
| int sockindex, |
| bool nonblocking, |
| bool *done) |
| { |
| CURLcode result; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| curl_socket_t sockfd = conn->sock[sockindex]; |
| 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 */ |
| const timediff_t 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; |
| } |
| |
| result = sectransp_connect_step1(data, conn, sockindex); |
| if(result) |
| return result; |
| } |
| |
| 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 */ |
| const timediff_t 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_check(readfd, CURL_SOCKET_BAD, 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. |
| */ |
| result = sectransp_connect_step2(data, conn, sockindex); |
| if(result || (nonblocking && |
| (ssl_connect_2 == connssl->connecting_state || |
| ssl_connect_2_reading == connssl->connecting_state || |
| ssl_connect_2_writing == connssl->connecting_state))) |
| return result; |
| |
| } /* repeat step2 until all transactions are done. */ |
| |
| |
| if(ssl_connect_3 == connssl->connecting_state) { |
| result = sectransp_connect_step3(data, conn, sockindex); |
| if(result) |
| return result; |
| } |
| |
| if(ssl_connect_done == connssl->connecting_state) { |
| connssl->state = ssl_connection_complete; |
| conn->recv[sockindex] = sectransp_recv; |
| conn->send[sockindex] = sectransp_send; |
| *done = TRUE; |
| } |
| else |
| *done = FALSE; |
| |
| /* Reset our connect state machine */ |
| connssl->connecting_state = ssl_connect_1; |
| |
| return CURLE_OK; |
| } |
| |
| static CURLcode sectransp_connect_nonblocking(struct Curl_easy *data, |
| struct connectdata *conn, |
| int sockindex, bool *done) |
| { |
| return sectransp_connect_common(data, conn, sockindex, TRUE, done); |
| } |
| |
| static CURLcode sectransp_connect(struct Curl_easy *data, |
| struct connectdata *conn, int sockindex) |
| { |
| CURLcode result; |
| bool done = FALSE; |
| |
| result = sectransp_connect_common(data, conn, sockindex, FALSE, &done); |
| |
| if(result) |
| return result; |
| |
| DEBUGASSERT(done); |
| |
| return CURLE_OK; |
| } |
| |
| static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, |
| int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| |
| (void) data; |
| |
| if(backend->ssl_ctx) { |
| (void)SSLClose(backend->ssl_ctx); |
| #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS |
| if(SSLCreateContext != NULL) |
| CFRelease(backend->ssl_ctx); |
| #if CURL_SUPPORT_MAC_10_8 |
| else |
| (void)SSLDisposeContext(backend->ssl_ctx); |
| #endif /* CURL_SUPPORT_MAC_10_8 */ |
| #else |
| (void)SSLDisposeContext(backend->ssl_ctx); |
| #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ |
| backend->ssl_ctx = NULL; |
| } |
| backend->ssl_sockfd = 0; |
| } |
| |
| static int sectransp_shutdown(struct Curl_easy *data, |
| struct connectdata *conn, int sockindex) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| ssize_t nread; |
| int what; |
| int rc; |
| char buf[120]; |
| int loop = 10; /* avoid getting stuck */ |
| |
| if(!backend->ssl_ctx) |
| return 0; |
| |
| #ifndef CURL_DISABLE_FTP |
| if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) |
| return 0; |
| #endif |
| |
| sectransp_close(data, conn, sockindex); |
| |
| rc = 0; |
| |
| what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT); |
| |
| while(loop--) { |
| 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 = SOCKET_READABLE(conn->sock[sockindex], 0); |
| } |
| |
| return rc; |
| } |
| |
| static void sectransp_session_free(void *ptr) |
| { |
| /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a |
| cached session ID inside the Security framework. There is a private |
| function that does this, but I don't want to have to explain to you why I |
| got your application rejected from the App Store due to the use of a |
| private API, so the best we can do is free up our own char array that we |
| created way back in sectransp_connect_step1... */ |
| Curl_safefree(ptr); |
| } |
| |
| static size_t sectransp_version(char *buffer, size_t size) |
| { |
| return msnprintf(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 |
| */ |
| static int sectransp_check_cxn(struct connectdata *conn) |
| { |
| struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; |
| struct ssl_backend_data *backend = connssl->backend; |
| OSStatus err; |
| SSLSessionState state; |
| |
| if(backend->ssl_ctx) { |
| err = SSLGetSessionState(backend->ssl_ctx, &state); |
| if(err == noErr) |
| return state == kSSLConnected || state == kSSLHandshake; |
| return -1; |
| } |
| return 0; |
| } |
| |
| static bool sectransp_data_pending(const struct connectdata *conn, |
| int connindex) |
| { |
| const struct ssl_connect_data *connssl = &conn->ssl[connindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| OSStatus err; |
| size_t buffer; |
| |
| if(backend->ssl_ctx) { /* SSL is in use */ |
| err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); |
| if(err == noErr) |
| return buffer > 0UL; |
| return false; |
| } |
| else |
| return false; |
| } |
| |
| static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, |
| 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; |
| |
| (void)data; |
| |
| 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; |
| return CURLE_OK; |
| } |
| |
| static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ |
| size_t tmplen, |
| unsigned char *sha256sum, /* output */ |
| size_t sha256len) |
| { |
| assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); |
| (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); |
| return CURLE_OK; |
| } |
| |
| static bool sectransp_false_start(void) |
| { |
| #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 |
| if(SSLSetSessionOption != NULL) |
| return TRUE; |
| #endif |
| return FALSE; |
| } |
| |
| static ssize_t sectransp_send(struct Curl_easy *data, |
| int sockindex, |
| const void *mem, |
| size_t len, |
| CURLcode *curlcode) |
| { |
| struct connectdata *conn = data->conn; |
| struct ssl_connect_data *connssl = &conn->ssl[sockindex]; |
| struct ssl_backend_data *backend = connssl->backend; |
| 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(backend->ssl_write_buffered_length) { |
| /* Write the buffered data: */ |
| err = SSLWrite(backend->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 = backend->ssl_write_buffered_length; |
| backend->ssl_write_buffered_length = 0UL; |
| break; |
| case errSSLWouldBlock: /* argh, try again */ |
| *curlcode = CURLE_AGAIN; |
| return -1L; |
| default: |
| failf(data, "SSLWrite() returned error %d", err); |
| *curlcode = CURLE_SEND_ERROR; |
| return -1L; |
| } |
| } |
| else { |
| /* We've got new data to write: */ |
| err = SSLWrite(backend->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 */ |
| backend->ssl_write_buffered_length = len; |
| *curlcode = CURLE_AGAIN; |
| return -1L; |
| default: |
| failf(data, "SSLWrite() returned error %d", err); |
| *curlcode = CURLE_SEND_ERROR; |
| return -1L; |
| } |
| } |
| } |
| return (ssize_t)processed; |
| } |
| |
| static ssize_t sectransp_recv(struct Curl_easy *data, |
| int num, |
| char *buf, |
| size_t buffersize, |
| CURLcode *curlcode) |
| { |
| struct connectdata *conn = data->conn; |
| struct ssl_connect_data *connssl = &conn->ssl[num]; |
| struct ssl_backend_data *backend = connssl->backend; |
| size_t processed = 0UL; |
| OSStatus err; |
| |
| again: |
| err = SSLRead(backend->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; |
| |
| /* The below is errSSLPeerAuthCompleted; it's not defined in |
| Leopard's headers */ |
| case -9841: |
| if((SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && |
| SSL_CONN_CONFIG(verifypeer)) { |
| CURLcode result = verify_cert(data, SSL_CONN_CONFIG(CAfile), |
| SSL_CONN_CONFIG(ca_info_blob), |
| backend->ssl_ctx); |
| if(result) |
| return result; |
| } |
| goto again; |
| default: |
| failf(data, "SSLRead() return error %d", err); |
| *curlcode = CURLE_RECV_ERROR; |
| return -1L; |
| break; |
| } |
| } |
| return (ssize_t)processed; |
| } |
| |
| static void *sectransp_get_internals(struct ssl_connect_data *connssl, |
| CURLINFO info UNUSED_PARAM) |
| { |
| struct ssl_backend_data *backend = connssl->backend; |
| (void)info; |
| return backend->ssl_ctx; |
| } |
| |
| const struct Curl_ssl Curl_ssl_sectransp = { |
| { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */ |
| |
| SSLSUPP_CAINFO_BLOB | |
| #ifdef SECTRANSP_PINNEDPUBKEY |
| SSLSUPP_PINNEDPUBKEY, |
| #else |
| 0, |
| #endif /* SECTRANSP_PINNEDPUBKEY */ |
| |
| sizeof(struct ssl_backend_data), |
| |
| Curl_none_init, /* init */ |
| Curl_none_cleanup, /* cleanup */ |
| sectransp_version, /* version */ |
| sectransp_check_cxn, /* check_cxn */ |
| sectransp_shutdown, /* shutdown */ |
| sectransp_data_pending, /* data_pending */ |
| sectransp_random, /* random */ |
| Curl_none_cert_status_request, /* cert_status_request */ |
| sectransp_connect, /* connect */ |
| sectransp_connect_nonblocking, /* connect_nonblocking */ |
| Curl_ssl_getsock, /* getsock */ |
| sectransp_get_internals, /* get_internals */ |
| sectransp_close, /* close_one */ |
| Curl_none_close_all, /* close_all */ |
| sectransp_session_free, /* session_free */ |
| Curl_none_set_engine, /* set_engine */ |
| Curl_none_set_engine_default, /* set_engine_default */ |
| Curl_none_engines_list, /* engines_list */ |
| sectransp_false_start, /* false_start */ |
| sectransp_sha256sum, /* sha256sum */ |
| NULL, /* associate_connection */ |
| NULL /* disassociate_connection */ |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #endif /* USE_SECTRANSP */ |