/* * 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" WINE_DEFAULT_DEBUG_CHANNEL(secur32); #ifdef HAVE_SECURITY_SECURITY_H 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_256_CBC, 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, }; 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), #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 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), #endif 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; OSStatus status; TRACE("(%p/%p)\n", s, s->context); status = SSLGetNegotiatedProtocolVersion(s->context, &protocol); if (status != noErr) { ERR("Failed to get session protocol: %ld\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; 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_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_256_CBC: 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; 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; 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_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_ECDH_anon: case schan_kx_ECDH_ECDSA: case schan_kx_ECDH_RSA: case schan_kx_ECDHE_ECDSA: case schan_kx_ECDHE_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 requrested. * 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; } BOOL schan_imp_create_session(schan_imp_session *session, BOOL is_server, schan_imp_certificate_credentials cred) { struct mac_session *s; OSStatus status; TRACE("(%p, %d)\n", session, is_server); s = HeapAlloc(GetProcessHeap(), 0, sizeof(*s)); if (!s) return FALSE; status = SSLNewContext(is_server, &s->context); if (status != noErr) { ERR("Failed to create session context: %ld\n", (long)status); goto fail; } status = SSLSetConnection(s->context, s); if (status != noErr) { ERR("Failed to set session connection: %ld\n", (long)status); goto fail; } status = SSLSetEnableCertVerify(s->context, FALSE); if (status != noErr) { ERR("Failed to disable certificate verification: %ld\n", (long)status); goto fail; } status = SSLSetProtocolVersionEnabled(s->context, kSSLProtocol2, FALSE); if (status != noErr) { ERR("Failed to disable SSL version 2: %ld\n", (long)status); goto fail; } status = SSLSetIOFuncs(s->context, schan_pull_adapter, schan_push_adapter); if (status != noErr) { ERR("Failed to set session I/O funcs: %ld\n", (long)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; OSStatus status; TRACE("(%p/%p)\n", s, s->context); status = SSLDisposeContext(s->context); if (status != noErr) ERR("Failed to dispose of session context: %ld\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; } SECURITY_STATUS schan_imp_handshake(schan_imp_session session) { struct mac_session *s = (struct mac_session*)session; OSStatus 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: %ld\n", (long)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; OSStatus status; TRACE("(%p/%p)\n", s, s->context); status = SSLGetNegotiatedCipher(s->context, &cipherSuite); if (status != noErr) { ERR("Failed to get session cipher suite: %ld\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_256_CBC: 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; OSStatus 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: %ld\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, PCCERT_CONTEXT *cert) { struct mac_session* s = (struct mac_session*)session; SECURITY_STATUS ret = SEC_E_INTERNAL_ERROR; CFArrayRef certs; OSStatus status; TRACE("(%p/%p, %p)\n", s, s->context, cert); #ifdef HAVE_SSLCOPYPEERCERTIFICATES status = SSLCopyPeerCertificates(s->context, &certs); #else status = SSLGetPeerCertificates(s->context, &certs); #endif if (status == noErr && certs) { SecCertificateRef mac_cert; CFDataRef data; if (CFArrayGetCount(certs) && (mac_cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0)) && (SecKeychainItemExport(mac_cert, kSecFormatX509Cert, 0, NULL, &data) == noErr)) { *cert = CertCreateCertificateContext(X509_ASN_ENCODING, CFDataGetBytePtr(data), CFDataGetLength(data)); if (*cert) ret = SEC_E_OK; else { ret = GetLastError(); WARN("CertCreateCertificateContext failed: %x\n", ret); } CFRelease(data); } else WARN("Couldn't extract certificate data\n"); #ifndef HAVE_SSLCOPYPEERCERTIFICATES /* This is why SSLGetPeerCertificates was deprecated */ CFArrayApplyFunction(certs, CFRangeMake(0, CFArrayGetCount(certs)), schan_imp_cf_release, NULL); #endif CFRelease(certs); } else WARN("SSLCopyPeerCertificates failed: %ld\n", (long)status); return ret; } SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer, SIZE_T *length) { struct mac_session* s = (struct mac_session*)session; OSStatus 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: %ld\n", (long)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; OSStatus status; TRACE("(%p/%p, %p, %p/%lu)\n", s, s->context, buffer, length, *length); status = SSLRead(s->context, buffer, *length, length); if (status == noErr) 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: %ld\n", (long)status); return SEC_E_INTERNAL_ERROR; } return SEC_E_OK; } BOOL schan_imp_allocate_certificate_credentials(schan_imp_certificate_credentials *c) { /* The certificate is never really used for anything. */ *c = NULL; return TRUE; } void schan_imp_free_certificate_credentials(schan_imp_certificate_credentials c) { } BOOL schan_imp_init(void) { TRACE("()\n"); return TRUE; } void schan_imp_deinit(void) { TRACE("()\n"); } #endif /* HAVE_SECURITY_SECURITY_H */