/* * Mac OS X Secure Transport implementation of the schannel (SSL/TLS) provider. * * Copyright 2005 Juan Lang * Copyright 2008 Henri Verbeet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include #ifdef HAVE_SECURITY_SECURITY_H #include #define GetCurrentThread GetCurrentThread_Mac #define LoadResource LoadResource_Mac #include #undef GetCurrentThread #undef LoadResource #undef DPRINTF #endif #include "windef.h" #include "winbase.h" #include "sspi.h" #include "schannel.h" #include "secur32_priv.h" #include "wine/debug.h" #include "wine/library.h" #ifdef HAVE_SECURITY_SECURITY_H WINE_DEFAULT_DEBUG_CHANNEL(secur32); #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 /* Defined in in the 10.6 SDK or later. */ enum { TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005, TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B, TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F, TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010, TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, TLS_ECDH_anon_WITH_NULL_SHA = 0xC015, TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016, TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017, TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018, TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019, }; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED < 1080 /* Defined in in the 10.8 SDK or later. */ enum { TLS_NULL_WITH_NULL_NULL = 0x0000, TLS_RSA_WITH_NULL_MD5 = 0x0001, TLS_RSA_WITH_NULL_SHA = 0x0002, TLS_RSA_WITH_RC4_128_MD5 = 0x0004, TLS_RSA_WITH_RC4_128_SHA = 0x0005, TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, TLS_RSA_WITH_NULL_SHA256 = 0x003B, TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C, TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D, TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E, TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068, TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B, TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C, TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D, TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032, TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, }; /* Defined in in the 10.8 SDK or later. */ enum { kTLSProtocol11 = 7, /* TLS 1.1 */ kTLSProtocol12 = 8, /* TLS 1.2 */ }; #endif struct mac_session { SSLContextRef context; struct schan_transport *transport; }; enum { schan_proto_SSL, schan_proto_TLS, }; enum { schan_kx_DH_anon_EXPORT, schan_kx_DH_anon, schan_kx_DH_DSS_EXPORT, schan_kx_DH_DSS, schan_kx_DH_RSA_EXPORT, schan_kx_DH_RSA, schan_kx_DHE_DSS_EXPORT, schan_kx_DHE_DSS, schan_kx_DHE_RSA_EXPORT, schan_kx_DHE_RSA, schan_kx_ECDH_anon, schan_kx_ECDH_ECDSA, schan_kx_ECDH_RSA, schan_kx_ECDHE_ECDSA, schan_kx_ECDHE_RSA, schan_kx_FORTEZZA_DMS, schan_kx_NULL, schan_kx_RSA_EXPORT, schan_kx_RSA, }; enum { schan_enc_3DES_EDE_CBC, schan_enc_AES_128_CBC, schan_enc_AES_128_GCM, schan_enc_AES_256_CBC, schan_enc_AES_256_GCM, schan_enc_DES_CBC, schan_enc_DES40_CBC, schan_enc_FORTEZZA_CBC, schan_enc_IDEA_CBC, schan_enc_NULL, schan_enc_RC2_CBC, schan_enc_RC2_CBC_40, schan_enc_RC4_128, schan_enc_RC4_40, }; enum { schan_mac_MD5, schan_mac_NULL, schan_mac_SHA, schan_mac_SHA256, schan_mac_SHA384, }; struct cipher_suite { SSLCipherSuite suite; int protocol; int kx_alg; int enc_alg; int mac_alg; }; /* This table corresponds to the enum in . */ static const struct cipher_suite cipher_suites[] = { #define CIPHER_SUITE(p, kx, enc, mac) { p##_##kx##_WITH_##enc##_##mac, schan_proto_##p, \ schan_kx_##kx, schan_enc_##enc, schan_mac_##mac } CIPHER_SUITE(SSL, RSA, NULL, MD5), CIPHER_SUITE(SSL, RSA, NULL, MD5), CIPHER_SUITE(SSL, RSA, NULL, SHA), CIPHER_SUITE(SSL, RSA_EXPORT, RC4_40, MD5), CIPHER_SUITE(SSL, RSA, RC4_128, MD5), CIPHER_SUITE(SSL, RSA, RC4_128, SHA), CIPHER_SUITE(SSL, RSA_EXPORT, RC2_CBC_40, MD5), CIPHER_SUITE(SSL, RSA, IDEA_CBC, SHA), CIPHER_SUITE(SSL, RSA_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, RSA, DES_CBC, SHA), CIPHER_SUITE(SSL, RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, DH_DSS_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, DH_DSS, DES_CBC, SHA), CIPHER_SUITE(SSL, DH_DSS, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, DH_RSA_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, DH_RSA, DES_CBC, SHA), CIPHER_SUITE(SSL, DH_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, DHE_DSS_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, DHE_DSS, DES_CBC, SHA), CIPHER_SUITE(SSL, DHE_DSS, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, DHE_RSA_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, DHE_RSA, DES_CBC, SHA), CIPHER_SUITE(SSL, DHE_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, DH_anon_EXPORT, RC4_40, MD5), CIPHER_SUITE(SSL, DH_anon, RC4_128, MD5), CIPHER_SUITE(SSL, DH_anon_EXPORT, DES40_CBC, SHA), CIPHER_SUITE(SSL, DH_anon, DES_CBC, SHA), CIPHER_SUITE(SSL, DH_anon, 3DES_EDE_CBC, SHA), CIPHER_SUITE(SSL, FORTEZZA_DMS, NULL, SHA), CIPHER_SUITE(SSL, FORTEZZA_DMS, FORTEZZA_CBC, SHA), CIPHER_SUITE(TLS, RSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, DH_DSS, AES_128_CBC, SHA), CIPHER_SUITE(TLS, DH_RSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, DHE_DSS, AES_128_CBC, SHA), CIPHER_SUITE(TLS, DHE_RSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, DH_anon, AES_128_CBC, SHA), CIPHER_SUITE(TLS, RSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, DH_DSS, AES_256_CBC, SHA), CIPHER_SUITE(TLS, DH_RSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, DHE_DSS, AES_256_CBC, SHA), CIPHER_SUITE(TLS, DHE_RSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, DH_anon, AES_256_CBC, SHA), CIPHER_SUITE(TLS, ECDH_ECDSA, NULL, SHA), CIPHER_SUITE(TLS, ECDH_ECDSA, RC4_128, SHA), CIPHER_SUITE(TLS, ECDH_ECDSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_ECDSA, NULL, SHA), CIPHER_SUITE(TLS, ECDHE_ECDSA, RC4_128, SHA), CIPHER_SUITE(TLS, ECDHE_ECDSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, ECDH_RSA, NULL, SHA), CIPHER_SUITE(TLS, ECDH_RSA, RC4_128, SHA), CIPHER_SUITE(TLS, ECDH_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, ECDH_RSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, ECDH_RSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_RSA, NULL, SHA), CIPHER_SUITE(TLS, ECDHE_RSA, RC4_128, SHA), CIPHER_SUITE(TLS, ECDHE_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_RSA, AES_128_CBC, SHA), CIPHER_SUITE(TLS, ECDHE_RSA, AES_256_CBC, SHA), CIPHER_SUITE(TLS, ECDH_anon, NULL, SHA), CIPHER_SUITE(TLS, ECDH_anon, RC4_128, SHA), CIPHER_SUITE(TLS, ECDH_anon, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, ECDH_anon, AES_128_CBC, SHA), CIPHER_SUITE(TLS, ECDH_anon, AES_256_CBC, SHA), CIPHER_SUITE(TLS, NULL, NULL, NULL), CIPHER_SUITE(TLS, RSA, NULL, MD5), CIPHER_SUITE(TLS, RSA, NULL, SHA), CIPHER_SUITE(TLS, RSA, RC4_128, MD5), CIPHER_SUITE(TLS, RSA, RC4_128, SHA), CIPHER_SUITE(TLS, RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, RSA, NULL, SHA256), CIPHER_SUITE(TLS, RSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, RSA, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, DH_DSS, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, DH_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, DHE_DSS, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, DHE_RSA, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, DH_DSS, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, DH_RSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, DHE_DSS, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, DHE_RSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, DH_DSS, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, DH_RSA, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, DHE_DSS, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, DHE_RSA, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, DH_anon, RC4_128, MD5), CIPHER_SUITE(TLS, DH_anon, 3DES_EDE_CBC, SHA), CIPHER_SUITE(TLS, DH_anon, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, DH_anon, AES_256_CBC, SHA256), CIPHER_SUITE(TLS, RSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, RSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, DHE_RSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, DHE_RSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, DH_RSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, DH_RSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, DHE_DSS, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, DHE_DSS, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, DH_DSS, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, DH_DSS, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, DH_anon, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, DH_anon, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_256_CBC, SHA384), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_256_CBC, SHA384), CIPHER_SUITE(TLS, ECDHE_RSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, ECDHE_RSA, AES_256_CBC, SHA384), CIPHER_SUITE(TLS, ECDH_RSA, AES_128_CBC, SHA256), CIPHER_SUITE(TLS, ECDH_RSA, AES_256_CBC, SHA384), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, ECDH_ECDSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, ECDHE_RSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, ECDHE_RSA, AES_256_GCM, SHA384), CIPHER_SUITE(TLS, ECDH_RSA, AES_128_GCM, SHA256), CIPHER_SUITE(TLS, ECDH_RSA, AES_256_GCM, SHA384), CIPHER_SUITE(SSL, RSA, RC2_CBC, MD5), CIPHER_SUITE(SSL, RSA, IDEA_CBC, MD5), CIPHER_SUITE(SSL, RSA, DES_CBC, MD5), CIPHER_SUITE(SSL, RSA, 3DES_EDE_CBC, MD5), #undef CIPHER_SUITE }; static const struct cipher_suite* get_cipher_suite(SSLCipherSuite cipher_suite) { int i; for (i = 0; i < sizeof(cipher_suites)/sizeof(cipher_suites[0]); i++) { if (cipher_suites[i].suite == cipher_suite) return &cipher_suites[i]; } return NULL; } static DWORD schan_get_session_protocol(struct mac_session* s) { SSLProtocol protocol; int status; TRACE("(%p/%p)\n", s, s->context); status = SSLGetNegotiatedProtocolVersion(s->context, &protocol); if (status != noErr) { ERR("Failed to get session protocol: %d\n", status); return 0; } TRACE("protocol %d\n", protocol); switch (protocol) { case kSSLProtocol2: return SP_PROT_SSL2_CLIENT; case kSSLProtocol3: return SP_PROT_SSL3_CLIENT; case kTLSProtocol1: return SP_PROT_TLS1_CLIENT; case kTLSProtocol11: return SP_PROT_TLS1_1_CLIENT; case kTLSProtocol12: return SP_PROT_TLS1_2_CLIENT; default: FIXME("unknown protocol %d\n", protocol); return 0; } } static ALG_ID schan_get_cipher_algid(const struct cipher_suite* c) { TRACE("(%#x)\n", (unsigned int)c->suite); switch (c->enc_alg) { case schan_enc_3DES_EDE_CBC: return CALG_3DES; case schan_enc_AES_128_CBC: return CALG_AES_128; case schan_enc_AES_256_CBC: return CALG_AES_256; case schan_enc_DES_CBC: return CALG_DES; case schan_enc_DES40_CBC: return CALG_DES; case schan_enc_NULL: return 0; case schan_enc_RC2_CBC_40: return CALG_RC2; case schan_enc_RC2_CBC: return CALG_RC2; case schan_enc_RC4_128: return CALG_RC4; case schan_enc_RC4_40: return CALG_RC4; case schan_enc_AES_128_GCM: case schan_enc_AES_256_GCM: case schan_enc_FORTEZZA_CBC: case schan_enc_IDEA_CBC: FIXME("Don't know CALG for encryption algorithm %d, returning 0\n", c->enc_alg); return 0; default: FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite); return 0; } } static unsigned int schan_get_cipher_key_size(const struct cipher_suite* c) { TRACE("(%#x)\n", (unsigned int)c->suite); switch (c->enc_alg) { case schan_enc_3DES_EDE_CBC: return 168; case schan_enc_AES_128_CBC: return 128; case schan_enc_AES_128_GCM: return 128; case schan_enc_AES_256_CBC: return 256; case schan_enc_AES_256_GCM: return 256; case schan_enc_DES_CBC: return 56; case schan_enc_DES40_CBC: return 40; case schan_enc_NULL: return 0; case schan_enc_RC2_CBC_40: return 40; case schan_enc_RC2_CBC: return 128; case schan_enc_RC4_128: return 128; case schan_enc_RC4_40: return 40; case schan_enc_FORTEZZA_CBC: case schan_enc_IDEA_CBC: FIXME("Don't know key size for encryption algorithm %d, returning 0\n", c->enc_alg); return 0; default: FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite); return 0; } } static ALG_ID schan_get_mac_algid(const struct cipher_suite* c) { TRACE("(%#x)\n", (unsigned int)c->suite); switch (c->mac_alg) { case schan_mac_MD5: return CALG_MD5; case schan_mac_NULL: return 0; case schan_mac_SHA: return CALG_SHA; case schan_mac_SHA256: return CALG_SHA_256; case schan_mac_SHA384: return CALG_SHA_384; default: FIXME("Unknown hashing algorithm %d for cipher suite %#x, returning 0\n", c->mac_alg, (unsigned)c->suite); return 0; } } static unsigned int schan_get_mac_key_size(const struct cipher_suite* c) { TRACE("(%#x)\n", (unsigned int)c->suite); switch (c->mac_alg) { case schan_mac_MD5: return 128; case schan_mac_NULL: return 0; case schan_mac_SHA: return 160; case schan_mac_SHA256: return 256; case schan_mac_SHA384: return 384; default: FIXME("Unknown hashing algorithm %d for cipher suite %#x, returning 0\n", c->mac_alg, (unsigned)c->suite); return 0; } } static ALG_ID schan_get_kx_algid(const struct cipher_suite* c) { TRACE("(%#x)\n", (unsigned int)c->suite); switch (c->kx_alg) { case schan_kx_DHE_DSS_EXPORT: case schan_kx_DHE_DSS: case schan_kx_DHE_RSA_EXPORT: case schan_kx_DHE_RSA: return CALG_DH_EPHEM; case schan_kx_ECDH_anon: case schan_kx_ECDH_ECDSA: case schan_kx_ECDH_RSA: case schan_kx_ECDHE_ECDSA: case schan_kx_ECDHE_RSA: return CALG_ECDH; case schan_kx_NULL: return 0; case schan_kx_RSA: return CALG_RSA_KEYX; case schan_kx_DH_anon_EXPORT: case schan_kx_DH_anon: case schan_kx_DH_DSS_EXPORT: case schan_kx_DH_DSS: case schan_kx_DH_RSA_EXPORT: case schan_kx_DH_RSA: case schan_kx_FORTEZZA_DMS: case schan_kx_RSA_EXPORT: FIXME("Don't know CALG for key exchange algorithm %d for cipher suite %#x, returning 0\n", c->kx_alg, (unsigned)c->suite); return 0; default: FIXME("Unknown key exchange algorithm %d for cipher suite %#x, returning 0\n", c->kx_alg, (unsigned)c->suite); return 0; } } /* schan_pull_adapter * Callback registered with SSLSetIOFuncs as the read function for a * session. Reads data from the session connection. Conforms to the * SSLReadFunc type. * * transport - The session connection * buff - The buffer into which to store the read data. Must be at least * *buff_len bytes in length. * *buff_len - On input, the desired length to read. On successful return, * the number of bytes actually read. * * Returns: * noErr on complete success meaning the requested length was successfully * read. * errSSLWouldBlock when the requested length could not be read without * blocking. *buff_len indicates how much was actually read. The * caller should try again if/when they want to read more. * errSSLClosedGraceful when the connection has closed and there's no * more data to be read. * other error code for failure. */ static OSStatus schan_pull_adapter(SSLConnectionRef transport, void *buff, SIZE_T *buff_len) { struct mac_session *s = (struct mac_session*)transport; size_t requested = *buff_len; int status; OSStatus ret; TRACE("(%p/%p, %p, %p/%lu)\n", s, s->transport, buff, buff_len, *buff_len); status = schan_pull(s->transport, buff, buff_len); if (status == 0) { if (*buff_len == 0) { TRACE("Connection closed\n"); ret = errSSLClosedGraceful; } else if (*buff_len < requested) { TRACE("Pulled %lu bytes before would block\n", *buff_len); ret = errSSLWouldBlock; } else { TRACE("Pulled %lu bytes\n", *buff_len); ret = noErr; } } else if (status == EAGAIN) { TRACE("Would block before being able to pull anything\n"); ret = errSSLWouldBlock; } else { FIXME("Unknown status code from schan_pull: %d\n", status); ret = ioErr; } return ret; } /* schan_push_adapter * Callback registered with SSLSetIOFuncs as the write function for a * session. Writes data to the session connection. Conforms to the * SSLWriteFunc type. * * transport - The session connection * buff - The buffer of data to write. Must be at least *buff_len bytes in length. * *buff_len - On input, the desired length to write. On successful return, * the number of bytes actually written. * * Returns: * noErr on complete or partial success; *buff_len indicates how much data * was actually written, which may be less than requested. * errSSLWouldBlock when no data could be written without blocking. The * caller should try again. * other error code for failure. */ static OSStatus schan_push_adapter(SSLConnectionRef transport, const void *buff, SIZE_T *buff_len) { struct mac_session *s = (struct mac_session*)transport; int status; OSStatus ret; TRACE("(%p/%p, %p, %p/%lu)\n", s, s->transport, buff, buff_len, *buff_len); status = schan_push(s->transport, buff, buff_len); if (status == 0) { TRACE("Pushed %lu bytes\n", *buff_len); ret = noErr; } else if (status == EAGAIN) { TRACE("Would block before being able to push anything\n"); ret = errSSLWouldBlock; } else { FIXME("Unknown status code from schan_push: %d\n", status); ret = ioErr; } return ret; } static const struct { DWORD enable_flag; SSLProtocol mac_version; } protocol_priority_flags[] = { {SP_PROT_TLS1_2_CLIENT, kTLSProtocol12}, {SP_PROT_TLS1_1_CLIENT, kTLSProtocol11}, {SP_PROT_TLS1_0_CLIENT, kTLSProtocol1}, {SP_PROT_SSL3_CLIENT, kSSLProtocol3}, {SP_PROT_SSL2_CLIENT, kSSLProtocol2} }; static DWORD supported_protocols; DWORD schan_imp_enabled_protocols(void) { return supported_protocols; } BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred) { struct mac_session *s; unsigned i; int status; TRACE("(%p)\n", session); s = HeapAlloc(GetProcessHeap(), 0, sizeof(*s)); if (!s) return FALSE; status = SSLNewContext(cred->credential_use == SECPKG_CRED_INBOUND, &s->context); if (status != noErr) { ERR("Failed to create session context: %d\n", status); goto fail; } status = SSLSetConnection(s->context, s); if (status != noErr) { ERR("Failed to set session connection: %d\n", status); goto fail; } status = SSLSetEnableCertVerify(s->context, FALSE); if (status != noErr) { ERR("Failed to disable certificate verification: %d\n", status); goto fail; } for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) { if(!(protocol_priority_flags[i].enable_flag & supported_protocols)) continue; status = SSLSetProtocolVersionEnabled(s->context, protocol_priority_flags[i].mac_version, (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) != 0); if (status != noErr) { ERR("Failed to set SSL version %d: %d\n", protocol_priority_flags[i].mac_version, status); goto fail; } } status = SSLSetIOFuncs(s->context, schan_pull_adapter, schan_push_adapter); if (status != noErr) { ERR("Failed to set session I/O funcs: %d\n", status); goto fail; } TRACE(" -> %p/%p\n", s, s->context); *session = (schan_imp_session)s; return TRUE; fail: HeapFree(GetProcessHeap(), 0, s); return FALSE; } void schan_imp_dispose_session(schan_imp_session session) { struct mac_session *s = (struct mac_session*)session; int status; TRACE("(%p/%p)\n", s, s->context); status = SSLDisposeContext(s->context); if (status != noErr) ERR("Failed to dispose of session context: %d\n", status); HeapFree(GetProcessHeap(), 0, s); } void schan_imp_set_session_transport(schan_imp_session session, struct schan_transport *t) { struct mac_session *s = (struct mac_session*)session; TRACE("(%p/%p, %p)\n", s, s->context, t); s->transport = t; } void schan_imp_set_session_target(schan_imp_session session, const char *target) { struct mac_session *s = (struct mac_session*)session; TRACE("(%p/%p, %s)\n", s, s->context, debugstr_a(target)); SSLSetPeerDomainName( s->context, target, strlen(target) ); } SECURITY_STATUS schan_imp_handshake(schan_imp_session session) { struct mac_session *s = (struct mac_session*)session; int status; TRACE("(%p/%p)\n", s, s->context); status = SSLHandshake(s->context); if (status == noErr) { TRACE("Handshake completed\n"); return SEC_E_OK; } else if (status == errSSLWouldBlock) { TRACE("Continue...\n"); return SEC_I_CONTINUE_NEEDED; } else if (errSecErrnoBase <= status && status <= errSecErrnoLimit) { ERR("Handshake failed: %s\n", strerror(status)); return SEC_E_INTERNAL_ERROR; } else { ERR("Handshake failed: %d\n", status); cssmPerror("SSLHandshake", status); return SEC_E_INTERNAL_ERROR; } /* Never reached */ return SEC_E_OK; } unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session) { struct mac_session* s = (struct mac_session*)session; SSLCipherSuite cipherSuite; const struct cipher_suite* c; int status; TRACE("(%p/%p)\n", s, s->context); status = SSLGetNegotiatedCipher(s->context, &cipherSuite); if (status != noErr) { ERR("Failed to get session cipher suite: %d\n", status); return 0; } c = get_cipher_suite(cipherSuite); if (!c) { ERR("Unknown session cipher suite: %#x\n", (unsigned int)cipherSuite); return 0; } switch (c->enc_alg) { case schan_enc_3DES_EDE_CBC: return 64; case schan_enc_AES_128_CBC: return 128; case schan_enc_AES_128_GCM: return 128; case schan_enc_AES_256_CBC: return 128; case schan_enc_AES_256_GCM: return 128; case schan_enc_DES_CBC: return 64; case schan_enc_DES40_CBC: return 64; case schan_enc_NULL: return 0; case schan_enc_RC2_CBC_40: return 64; case schan_enc_RC2_CBC: return 64; case schan_enc_RC4_128: return 0; case schan_enc_RC4_40: return 0; case schan_enc_FORTEZZA_CBC: case schan_enc_IDEA_CBC: FIXME("Don't know block size for encryption algorithm %d, returning 0\n", c->enc_alg); return 0; default: FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite); return 0; } } unsigned int schan_imp_get_max_message_size(schan_imp_session session) { FIXME("Returning 1 << 14.\n"); return 1 << 14; } SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session, SecPkgContext_ConnectionInfo *info) { struct mac_session* s = (struct mac_session*)session; SSLCipherSuite cipherSuite; const struct cipher_suite* c; int status; TRACE("(%p/%p, %p)\n", s, s->context, info); status = SSLGetNegotiatedCipher(s->context, &cipherSuite); if (status != noErr) { ERR("Failed to get session cipher suite: %d\n", status); return SEC_E_INTERNAL_ERROR; } c = get_cipher_suite(cipherSuite); if (!c) { ERR("Unknown session cipher suite: %#x\n", (unsigned int)cipherSuite); return SEC_E_INTERNAL_ERROR; } info->dwProtocol = schan_get_session_protocol(s); info->aiCipher = schan_get_cipher_algid(c); info->dwCipherStrength = schan_get_cipher_key_size(c); info->aiHash = schan_get_mac_algid(c); info->dwHashStrength = schan_get_mac_key_size(c); info->aiExch = schan_get_kx_algid(c); /* FIXME: info->dwExchStrength? */ info->dwExchStrength = 0; return SEC_E_OK; } #ifndef HAVE_SSLCOPYPEERCERTIFICATES static void schan_imp_cf_release(const void *arg, void *ctx) { CFRelease(arg); } #endif SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store, PCCERT_CONTEXT *ret_cert) { struct mac_session* s = (struct mac_session*)session; SECURITY_STATUS ret = SEC_E_OK; PCCERT_CONTEXT cert = NULL; SecCertificateRef mac_cert; CFArrayRef cert_array; int status; CFIndex cnt, i; CFDataRef data; BOOL res; TRACE("(%p/%p, %p)\n", s, s->context, cert); #ifdef HAVE_SSLCOPYPEERCERTIFICATES status = SSLCopyPeerCertificates(s->context, &cert_array); #else status = SSLGetPeerCertificates(s->context, &cert_array); #endif if (status != noErr || !cert_array) { WARN("SSLCopyPeerCertificates failed: %d\n", status); return SEC_E_INTERNAL_ERROR; } cnt = CFArrayGetCount(cert_array); for (i=0; i < cnt; i++) { if (!(mac_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cert_array, i)) || (SecKeychainItemExport(mac_cert, kSecFormatX509Cert, 0, NULL, &data) != noErr)) { WARN("Couldn't extract certificate data\n"); ret = SEC_E_INTERNAL_ERROR; break; } res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, CFDataGetBytePtr(data), CFDataGetLength(data), CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert); CFRelease(data); if (!res) { ret = GetLastError(); WARN("CertAddEncodedCertificateToStore failed: %x\n", ret); break; } } #ifndef HAVE_SSLCOPYPEERCERTIFICATES /* This is why SSLGetPeerCertificates was deprecated */ CFArrayApplyFunction(cert_array, CFRangeMake(0, CFArrayGetCount(cert_array)), schan_imp_cf_release, NULL); #endif CFRelease(cert_array); if (ret != SEC_E_OK) { if(cert) CertFreeCertificateContext(cert); return ret; } *ret_cert = cert; return SEC_E_OK; } SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer, SIZE_T *length) { struct mac_session* s = (struct mac_session*)session; int status; TRACE("(%p/%p, %p, %p/%lu)\n", s, s->context, buffer, length, *length); status = SSLWrite(s->context, buffer, *length, length); if (status == noErr) TRACE("Wrote %lu bytes\n", *length); else if (status == errSSLWouldBlock) { if (!*length) { TRACE("Would block before being able to write anything\n"); return SEC_I_CONTINUE_NEEDED; } else TRACE("Wrote %lu bytes before would block\n", *length); } else { WARN("SSLWrite failed: %d\n", status); return SEC_E_INTERNAL_ERROR; } return SEC_E_OK; } SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer, SIZE_T *length) { struct mac_session* s = (struct mac_session*)session; int status; TRACE("(%p/%p, %p, %p/%lu)\n", s, s->context, buffer, length, *length); status = SSLRead(s->context, buffer, *length, length); if (status == noErr || status == errSSLClosedGraceful) TRACE("Read %lu bytes\n", *length); else if (status == errSSLWouldBlock) { if (!*length) { TRACE("Would block before being able to read anything\n"); return SEC_I_CONTINUE_NEEDED; } else TRACE("Read %lu bytes before would block\n", *length); } else { WARN("SSLRead failed: %d\n", status); return SEC_E_INTERNAL_ERROR; } return SEC_E_OK; } BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c) { /* The certificate is never really used for anything. */ c->credentials = NULL; return TRUE; } void schan_imp_free_certificate_credentials(schan_credentials *c) { } BOOL schan_imp_init(void) { TRACE("()\n"); supported_protocols = SP_PROT_SSL2_CLIENT | SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 if(SSLGetProtocolVersionMax != NULL) { SSLProtocol max_protocol; SSLContextRef ctx; OSStatus status; status = SSLNewContext(FALSE, &ctx); if(status == noErr) { status = SSLGetProtocolVersionMax(ctx, &max_protocol); if(status == noErr) { if(max_protocol >= kTLSProtocol11) supported_protocols |= SP_PROT_TLS1_1_CLIENT; if(max_protocol >= kTLSProtocol12) supported_protocols |= SP_PROT_TLS1_2_CLIENT; } SSLDisposeContext(ctx); }else { WARN("SSLNewContext failed\n"); } } #endif return TRUE; } void schan_imp_deinit(void) { TRACE("()\n"); } #endif /* HAVE_SECURITY_SECURITY_H */