secur32: Avoid size_t and fix some printf formats.
[wine] / dlls / secur32 / schannel_macosx.c
1 /*
2  * Mac OS X Secure Transport implementation of the schannel (SSL/TLS) provider.
3  *
4  * Copyright 2005 Juan Lang
5  * Copyright 2008 Henri Verbeet
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #ifdef HAVE_SECURITY_SECURITY_H
27 #include <Security/Security.h>
28 #define GetCurrentThread GetCurrentThread_Mac
29 #define LoadResource LoadResource_Mac
30 #include <CoreServices/CoreServices.h>
31 #undef GetCurrentThread
32 #undef LoadResource
33 #undef DPRINTF
34 #endif
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "sspi.h"
39 #include "schannel.h"
40 #include "secur32_priv.h"
41 #include "wine/debug.h"
42 #include "wine/library.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
45
46 #ifdef HAVE_SECURITY_SECURITY_H
47
48 struct mac_session {
49     SSLContextRef context;
50     struct schan_transport *transport;
51 };
52
53
54 enum {
55     schan_proto_SSL,
56     schan_proto_TLS,
57 };
58
59 enum {
60     schan_kx_DH_anon_EXPORT,
61     schan_kx_DH_anon,
62     schan_kx_DH_DSS_EXPORT,
63     schan_kx_DH_DSS,
64     schan_kx_DH_RSA_EXPORT,
65     schan_kx_DH_RSA,
66     schan_kx_DHE_DSS_EXPORT,
67     schan_kx_DHE_DSS,
68     schan_kx_DHE_RSA_EXPORT,
69     schan_kx_DHE_RSA,
70     schan_kx_ECDH_anon,
71     schan_kx_ECDH_ECDSA,
72     schan_kx_ECDH_RSA,
73     schan_kx_ECDHE_ECDSA,
74     schan_kx_ECDHE_RSA,
75     schan_kx_FORTEZZA_DMS,
76     schan_kx_NULL,
77     schan_kx_RSA_EXPORT,
78     schan_kx_RSA,
79 };
80
81 enum {
82     schan_enc_3DES_EDE_CBC,
83     schan_enc_AES_128_CBC,
84     schan_enc_AES_256_CBC,
85     schan_enc_DES_CBC,
86     schan_enc_DES40_CBC,
87     schan_enc_FORTEZZA_CBC,
88     schan_enc_IDEA_CBC,
89     schan_enc_NULL,
90     schan_enc_RC2_CBC,
91     schan_enc_RC2_CBC_40,
92     schan_enc_RC4_128,
93     schan_enc_RC4_40,
94 };
95
96 enum {
97     schan_mac_MD5,
98     schan_mac_NULL,
99     schan_mac_SHA,
100 };
101
102
103 struct cipher_suite {
104     SSLCipherSuite suite;
105     int protocol;
106     int kx_alg;
107     int enc_alg;
108     int mac_alg;
109 };
110
111 /* This table corresponds to the enum in <Security/CipherSuite.h>. */
112 static const struct cipher_suite cipher_suites[] = {
113 #define CIPHER_SUITE(p, kx, enc, mac) { p##_##kx##_WITH_##enc##_##mac, schan_proto_##p, \
114                                         schan_kx_##kx, schan_enc_##enc, schan_mac_##mac }
115     CIPHER_SUITE(SSL, RSA, NULL, MD5),
116     CIPHER_SUITE(SSL, RSA, NULL, MD5),
117     CIPHER_SUITE(SSL, RSA, NULL, SHA),
118     CIPHER_SUITE(SSL, RSA_EXPORT, RC4_40, MD5),
119     CIPHER_SUITE(SSL, RSA, RC4_128, MD5),
120     CIPHER_SUITE(SSL, RSA, RC4_128, SHA),
121     CIPHER_SUITE(SSL, RSA_EXPORT, RC2_CBC_40, MD5),
122     CIPHER_SUITE(SSL, RSA, IDEA_CBC, SHA),
123     CIPHER_SUITE(SSL, RSA_EXPORT, DES40_CBC, SHA),
124     CIPHER_SUITE(SSL, RSA, DES_CBC, SHA),
125     CIPHER_SUITE(SSL, RSA, 3DES_EDE_CBC, SHA),
126     CIPHER_SUITE(SSL, DH_DSS_EXPORT, DES40_CBC, SHA),
127     CIPHER_SUITE(SSL, DH_DSS, DES_CBC, SHA),
128     CIPHER_SUITE(SSL, DH_DSS, 3DES_EDE_CBC, SHA),
129     CIPHER_SUITE(SSL, DH_RSA_EXPORT, DES40_CBC, SHA),
130     CIPHER_SUITE(SSL, DH_RSA, DES_CBC, SHA),
131     CIPHER_SUITE(SSL, DH_RSA, 3DES_EDE_CBC, SHA),
132     CIPHER_SUITE(SSL, DHE_DSS_EXPORT, DES40_CBC, SHA),
133     CIPHER_SUITE(SSL, DHE_DSS, DES_CBC, SHA),
134     CIPHER_SUITE(SSL, DHE_DSS, 3DES_EDE_CBC, SHA),
135     CIPHER_SUITE(SSL, DHE_RSA_EXPORT, DES40_CBC, SHA),
136     CIPHER_SUITE(SSL, DHE_RSA, DES_CBC, SHA),
137     CIPHER_SUITE(SSL, DHE_RSA, 3DES_EDE_CBC, SHA),
138     CIPHER_SUITE(SSL, DH_anon_EXPORT, RC4_40, MD5),
139     CIPHER_SUITE(SSL, DH_anon, RC4_128, MD5),
140     CIPHER_SUITE(SSL, DH_anon_EXPORT, DES40_CBC, SHA),
141     CIPHER_SUITE(SSL, DH_anon, DES_CBC, SHA),
142     CIPHER_SUITE(SSL, DH_anon, 3DES_EDE_CBC, SHA),
143     CIPHER_SUITE(SSL, FORTEZZA_DMS, NULL, SHA),
144     CIPHER_SUITE(SSL, FORTEZZA_DMS, FORTEZZA_CBC, SHA),
145
146     CIPHER_SUITE(TLS, RSA, AES_128_CBC, SHA),
147     CIPHER_SUITE(TLS, DH_DSS, AES_128_CBC, SHA),
148     CIPHER_SUITE(TLS, DH_RSA, AES_128_CBC, SHA),
149     CIPHER_SUITE(TLS, DHE_DSS, AES_128_CBC, SHA),
150     CIPHER_SUITE(TLS, DHE_RSA, AES_128_CBC, SHA),
151     CIPHER_SUITE(TLS, DH_anon, AES_128_CBC, SHA),
152     CIPHER_SUITE(TLS, RSA, AES_256_CBC, SHA),
153     CIPHER_SUITE(TLS, DH_DSS, AES_256_CBC, SHA),
154     CIPHER_SUITE(TLS, DH_RSA, AES_256_CBC, SHA),
155     CIPHER_SUITE(TLS, DHE_DSS, AES_256_CBC, SHA),
156     CIPHER_SUITE(TLS, DHE_RSA, AES_256_CBC, SHA),
157     CIPHER_SUITE(TLS, DH_anon, AES_256_CBC, SHA),
158
159 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
160     CIPHER_SUITE(TLS, ECDH_ECDSA, NULL, SHA),
161     CIPHER_SUITE(TLS, ECDH_ECDSA, RC4_128, SHA),
162     CIPHER_SUITE(TLS, ECDH_ECDSA, 3DES_EDE_CBC, SHA),
163     CIPHER_SUITE(TLS, ECDH_ECDSA, AES_128_CBC, SHA),
164     CIPHER_SUITE(TLS, ECDH_ECDSA, AES_256_CBC, SHA),
165     CIPHER_SUITE(TLS, ECDHE_ECDSA, NULL, SHA),
166     CIPHER_SUITE(TLS, ECDHE_ECDSA, RC4_128, SHA),
167     CIPHER_SUITE(TLS, ECDHE_ECDSA, 3DES_EDE_CBC, SHA),
168     CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_128_CBC, SHA),
169     CIPHER_SUITE(TLS, ECDHE_ECDSA, AES_256_CBC, SHA),
170     CIPHER_SUITE(TLS, ECDH_RSA, NULL, SHA),
171     CIPHER_SUITE(TLS, ECDH_RSA, RC4_128, SHA),
172     CIPHER_SUITE(TLS, ECDH_RSA, 3DES_EDE_CBC, SHA),
173     CIPHER_SUITE(TLS, ECDH_RSA, AES_128_CBC, SHA),
174     CIPHER_SUITE(TLS, ECDH_RSA, AES_256_CBC, SHA),
175     CIPHER_SUITE(TLS, ECDHE_RSA, NULL, SHA),
176     CIPHER_SUITE(TLS, ECDHE_RSA, RC4_128, SHA),
177     CIPHER_SUITE(TLS, ECDHE_RSA, 3DES_EDE_CBC, SHA),
178     CIPHER_SUITE(TLS, ECDHE_RSA, AES_128_CBC, SHA),
179     CIPHER_SUITE(TLS, ECDHE_RSA, AES_256_CBC, SHA),
180     CIPHER_SUITE(TLS, ECDH_anon, NULL, SHA),
181     CIPHER_SUITE(TLS, ECDH_anon, RC4_128, SHA),
182     CIPHER_SUITE(TLS, ECDH_anon, 3DES_EDE_CBC, SHA),
183     CIPHER_SUITE(TLS, ECDH_anon, AES_128_CBC, SHA),
184     CIPHER_SUITE(TLS, ECDH_anon, AES_256_CBC, SHA),
185 #endif
186
187     CIPHER_SUITE(SSL, RSA, RC2_CBC, MD5),
188     CIPHER_SUITE(SSL, RSA, IDEA_CBC, MD5),
189     CIPHER_SUITE(SSL, RSA, DES_CBC, MD5),
190     CIPHER_SUITE(SSL, RSA, 3DES_EDE_CBC, MD5),
191 #undef CIPHER_SUITE
192 };
193
194
195 static const struct cipher_suite* get_cipher_suite(SSLCipherSuite cipher_suite)
196 {
197     int i;
198     for (i = 0; i < sizeof(cipher_suites)/sizeof(cipher_suites[0]); i++)
199     {
200         if (cipher_suites[i].suite == cipher_suite)
201             return &cipher_suites[i];
202     }
203
204     return NULL;
205 }
206
207
208 static DWORD schan_get_session_protocol(struct mac_session* s)
209 {
210     SSLProtocol protocol;
211     OSStatus status;
212
213     TRACE("(%p/%p)\n", s, s->context);
214
215     status = SSLGetNegotiatedProtocolVersion(s->context, &protocol);
216     if (status != noErr)
217     {
218         ERR("Failed to get session protocol: %ld\n", status);
219         return 0;
220     }
221
222     TRACE("protocol %d\n", protocol);
223
224     switch (protocol)
225     {
226     case kSSLProtocol2: return SP_PROT_SSL2_CLIENT;
227     case kSSLProtocol3: return SP_PROT_SSL3_CLIENT;
228     case kTLSProtocol1: return SP_PROT_TLS1_CLIENT;
229     default:
230         FIXME("unknown protocol %d\n", protocol);
231         return 0;
232     }
233 }
234
235 static ALG_ID schan_get_cipher_algid(const struct cipher_suite* c)
236 {
237     TRACE("(%#x)\n", (unsigned int)c->suite);
238
239     switch (c->enc_alg)
240     {
241     case schan_enc_3DES_EDE_CBC:    return CALG_3DES;
242     case schan_enc_AES_128_CBC:     return CALG_AES_128;
243     case schan_enc_AES_256_CBC:     return CALG_AES_256;
244     case schan_enc_DES_CBC:         return CALG_DES;
245     case schan_enc_DES40_CBC:       return CALG_DES;
246     case schan_enc_NULL:            return 0;
247     case schan_enc_RC2_CBC_40:      return CALG_RC2;
248     case schan_enc_RC2_CBC:         return CALG_RC2;
249     case schan_enc_RC4_128:         return CALG_RC4;
250     case schan_enc_RC4_40:          return CALG_RC4;
251
252     case schan_enc_FORTEZZA_CBC:
253     case schan_enc_IDEA_CBC:
254         FIXME("Don't know CALG for encryption algorithm %d, returning 0\n", c->enc_alg);
255         return 0;
256
257     default:
258         FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite);
259         return 0;
260     }
261 }
262
263 static unsigned int schan_get_cipher_key_size(const struct cipher_suite* c)
264 {
265     TRACE("(%#x)\n", (unsigned int)c->suite);
266
267     switch (c->enc_alg)
268     {
269     case schan_enc_3DES_EDE_CBC:    return 168;
270     case schan_enc_AES_128_CBC:     return 128;
271     case schan_enc_AES_256_CBC:     return 256;
272     case schan_enc_DES_CBC:         return 56;
273     case schan_enc_DES40_CBC:       return 40;
274     case schan_enc_NULL:            return 0;
275     case schan_enc_RC2_CBC_40:      return 40;
276     case schan_enc_RC2_CBC:         return 128;
277     case schan_enc_RC4_128:         return 128;
278     case schan_enc_RC4_40:          return 40;
279
280     case schan_enc_FORTEZZA_CBC:
281     case schan_enc_IDEA_CBC:
282         FIXME("Don't know key size for encryption algorithm %d, returning 0\n", c->enc_alg);
283         return 0;
284
285     default:
286         FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite);
287         return 0;
288     }
289 }
290
291 static ALG_ID schan_get_mac_algid(const struct cipher_suite* c)
292 {
293     TRACE("(%#x)\n", (unsigned int)c->suite);
294
295     switch (c->mac_alg)
296     {
297     case schan_mac_MD5:     return CALG_MD5;
298     case schan_mac_NULL:    return 0;
299     case schan_mac_SHA:     return CALG_SHA;
300
301     default:
302         FIXME("Unknown hashing algorithm %d for cipher suite %#x, returning 0\n", c->mac_alg, (unsigned)c->suite);
303         return 0;
304     }
305 }
306
307 static unsigned int schan_get_mac_key_size(const struct cipher_suite* c)
308 {
309     TRACE("(%#x)\n", (unsigned int)c->suite);
310
311     switch (c->mac_alg)
312     {
313     case schan_mac_MD5:     return 128;
314     case schan_mac_NULL:    return 0;
315     case schan_mac_SHA:     return 160;
316
317     default:
318         FIXME("Unknown hashing algorithm %d for cipher suite %#x, returning 0\n", c->mac_alg, (unsigned)c->suite);
319         return 0;
320     }
321 }
322
323 static ALG_ID schan_get_kx_algid(const struct cipher_suite* c)
324 {
325     TRACE("(%#x)\n", (unsigned int)c->suite);
326
327     switch (c->kx_alg)
328     {
329     case schan_kx_DHE_DSS_EXPORT:
330     case schan_kx_DHE_DSS:
331     case schan_kx_DHE_RSA_EXPORT:
332     case schan_kx_DHE_RSA:          return CALG_DH_EPHEM;
333     case schan_kx_NULL:             return 0;
334     case schan_kx_RSA:              return CALG_RSA_KEYX;
335
336     case schan_kx_DH_anon_EXPORT:
337     case schan_kx_DH_anon:
338     case schan_kx_DH_DSS_EXPORT:
339     case schan_kx_DH_DSS:
340     case schan_kx_DH_RSA_EXPORT:
341     case schan_kx_DH_RSA:
342     case schan_kx_ECDH_anon:
343     case schan_kx_ECDH_ECDSA:
344     case schan_kx_ECDH_RSA:
345     case schan_kx_ECDHE_ECDSA:
346     case schan_kx_ECDHE_RSA:
347     case schan_kx_FORTEZZA_DMS:
348     case schan_kx_RSA_EXPORT:
349         FIXME("Don't know CALG for key exchange algorithm %d for cipher suite %#x, returning 0\n", c->kx_alg, (unsigned)c->suite);
350         return 0;
351
352     default:
353         FIXME("Unknown key exchange algorithm %d for cipher suite %#x, returning 0\n", c->kx_alg, (unsigned)c->suite);
354         return 0;
355     }
356 }
357
358
359 /* schan_pull_adapter
360  *      Callback registered with SSLSetIOFuncs as the read function for a
361  *      session.  Reads data from the session connection.  Conforms to the
362  *      SSLReadFunc type.
363  *
364  *  transport - The session connection
365  *  buff - The buffer into which to store the read data.  Must be at least
366  *         *buff_len bytes in length.
367  *  *buff_len - On input, the desired length to read.  On successful return,
368  *              the number of bytes actually read.
369  *
370  *  Returns:
371  *      noErr on complete success meaning the requested length was successfully
372  *          read.
373  *      errSSLWouldBlock when the requested length could not be read without
374  *          blocking.  *buff_len indicates how much was actually read.  The
375  *          caller should try again if/when they want to read more.
376  *      errSSLClosedGraceful when the connection has closed and there's no
377  *          more data to be read.
378  *      other error code for failure.
379  */
380 static OSStatus schan_pull_adapter(SSLConnectionRef transport, void *buff,
381                                    SIZE_T *buff_len)
382 {
383     struct mac_session *s = (struct mac_session*)transport;
384     size_t requested = *buff_len;
385     int status;
386     OSStatus ret;
387
388     TRACE("(%p/%p, %p, %p/%lu)\n", s, s->transport, buff, buff_len, *buff_len);
389
390     status = schan_pull(s->transport, buff, buff_len);
391     if (status == 0)
392     {
393         if (*buff_len == 0)
394         {
395             TRACE("Connection closed\n");
396             ret = errSSLClosedGraceful;
397         }
398         else if (*buff_len < requested)
399         {
400             TRACE("Pulled %lu bytes before would block\n", *buff_len);
401             ret = errSSLWouldBlock;
402         }
403         else
404         {
405             TRACE("Pulled %lu bytes\n", *buff_len);
406             ret = noErr;
407         }
408     }
409     else if (status == EAGAIN)
410     {
411         TRACE("Would block before being able to pull anything\n");
412         ret = errSSLWouldBlock;
413     }
414     else
415     {
416         FIXME("Unknown status code from schan_pull: %d\n", status);
417         ret = ioErr;
418     }
419
420     return ret;
421 }
422
423 /* schan_push_adapter
424  *      Callback registered with SSLSetIOFuncs as the write function for a
425  *      session.  Writes data to the session connection.  Conforms to the
426  *      SSLWriteFunc type.
427  *
428  *  transport - The session connection
429  *  buff - The buffer of data to write.  Must be at least *buff_len bytes in length.
430  *  *buff_len - On input, the desired length to write.  On successful return,
431  *              the number of bytes actually written.
432  *
433  *  Returns:
434  *      noErr on complete or partial success; *buff_len indicates how much data
435  *          was actually written, which may be less than requrested.
436  *      errSSLWouldBlock when no data could be written without blocking.  The
437  *          caller should try again.
438  *      other error code for failure.
439  */
440 static OSStatus schan_push_adapter(SSLConnectionRef transport, const void *buff,
441                                        SIZE_T *buff_len)
442 {
443     struct mac_session *s = (struct mac_session*)transport;
444     int status;
445     OSStatus ret;
446
447     TRACE("(%p/%p, %p, %p/%lu)\n", s, s->transport, buff, buff_len, *buff_len);
448
449     status = schan_push(s->transport, buff, buff_len);
450     if (status == 0)
451     {
452         TRACE("Pushed %lu bytes\n", *buff_len);
453         ret = noErr;
454     }
455     else if (status == EAGAIN)
456     {
457         TRACE("Would block before being able to push anything\n");
458         ret = errSSLWouldBlock;
459     }
460     else
461     {
462         FIXME("Unknown status code from schan_push: %d\n", status);
463         ret = ioErr;
464     }
465
466     return ret;
467 }
468
469
470 BOOL schan_imp_create_session(schan_imp_session *session, BOOL is_server,
471                               schan_imp_certificate_credentials cred)
472 {
473     struct mac_session *s;
474     OSStatus status;
475
476     TRACE("(%p, %d)\n", session, is_server);
477
478     s = HeapAlloc(GetProcessHeap(), 0, sizeof(*s));
479     if (!s)
480         return FALSE;
481
482     status = SSLNewContext(is_server, &s->context);
483     if (status != noErr)
484     {
485         ERR("Failed to create session context: %ld\n", (long)status);
486         goto fail;
487     }
488
489     status = SSLSetConnection(s->context, s);
490     if (status != noErr)
491     {
492         ERR("Failed to set session connection: %ld\n", (long)status);
493         goto fail;
494     }
495
496     status = SSLSetEnableCertVerify(s->context, FALSE);
497     if (status != noErr)
498     {
499         ERR("Failed to disable certificate verification: %ld\n", (long)status);
500         goto fail;
501     }
502
503     status = SSLSetProtocolVersionEnabled(s->context, kSSLProtocol2, FALSE);
504     if (status != noErr)
505     {
506         ERR("Failed to disable SSL version 2: %ld\n", (long)status);
507         goto fail;
508     }
509
510     status = SSLSetIOFuncs(s->context, schan_pull_adapter, schan_push_adapter);
511     if (status != noErr)
512     {
513         ERR("Failed to set session I/O funcs: %ld\n", (long)status);
514         goto fail;
515     }
516
517     TRACE("    -> %p/%p\n", s, s->context);
518
519     *session = (schan_imp_session)s;
520     return TRUE;
521
522 fail:
523     HeapFree(GetProcessHeap(), 0, s);
524     return FALSE;
525 }
526
527 void schan_imp_dispose_session(schan_imp_session session)
528 {
529     struct mac_session *s = (struct mac_session*)session;
530     OSStatus status;
531
532     TRACE("(%p/%p)\n", s, s->context);
533
534     status = SSLDisposeContext(s->context);
535     if (status != noErr)
536         ERR("Failed to dispose of session context: %ld\n", status);
537     HeapFree(GetProcessHeap(), 0, s);
538 }
539
540 void schan_imp_set_session_transport(schan_imp_session session,
541                                      struct schan_transport *t)
542 {
543     struct mac_session *s = (struct mac_session*)session;
544
545     TRACE("(%p/%p, %p)\n", s, s->context, t);
546
547     s->transport = t;
548 }
549
550 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
551 {
552     struct mac_session *s = (struct mac_session*)session;
553     OSStatus status;
554
555     TRACE("(%p/%p)\n", s, s->context);
556
557     status = SSLHandshake(s->context);
558     if (status == noErr)
559     {
560         TRACE("Handshake completed\n");
561         return SEC_E_OK;
562     }
563     else if (status == errSSLWouldBlock)
564     {
565         TRACE("Continue...\n");
566         return SEC_I_CONTINUE_NEEDED;
567     }
568     else if (errSecErrnoBase <= status && status <= errSecErrnoLimit)
569     {
570         ERR("Handshake failed: %s\n", strerror(status));
571         return SEC_E_INTERNAL_ERROR;
572     }
573     else
574     {
575         ERR("Handshake failed: %ld\n", (long)status);
576         cssmPerror("SSLHandshake", status);
577         return SEC_E_INTERNAL_ERROR;
578     }
579
580     /* Never reached */
581     return SEC_E_OK;
582 }
583
584 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
585 {
586     struct mac_session* s = (struct mac_session*)session;
587     SSLCipherSuite cipherSuite;
588     const struct cipher_suite* c;
589     OSStatus status;
590
591     TRACE("(%p/%p)\n", s, s->context);
592
593     status = SSLGetNegotiatedCipher(s->context, &cipherSuite);
594     if (status != noErr)
595     {
596         ERR("Failed to get session cipher suite: %ld\n", status);
597         return 0;
598     }
599
600     c = get_cipher_suite(cipherSuite);
601     if (!c)
602     {
603         ERR("Unknown session cipher suite: %#x\n", (unsigned int)cipherSuite);
604         return 0;
605     }
606
607     switch (c->enc_alg)
608     {
609     case schan_enc_3DES_EDE_CBC:    return 64;
610     case schan_enc_AES_128_CBC:     return 128;
611     case schan_enc_AES_256_CBC:     return 128;
612     case schan_enc_DES_CBC:         return 64;
613     case schan_enc_DES40_CBC:       return 64;
614     case schan_enc_NULL:            return 0;
615     case schan_enc_RC2_CBC_40:      return 64;
616     case schan_enc_RC2_CBC:         return 64;
617     case schan_enc_RC4_128:         return 0;
618     case schan_enc_RC4_40:          return 0;
619
620     case schan_enc_FORTEZZA_CBC:
621     case schan_enc_IDEA_CBC:
622         FIXME("Don't know block size for encryption algorithm %d, returning 0\n", c->enc_alg);
623         return 0;
624
625     default:
626         FIXME("Unknown encryption algorithm %d for cipher suite %#x, returning 0\n", c->enc_alg, (unsigned int)c->suite);
627         return 0;
628     }
629 }
630
631 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
632                                               SecPkgContext_ConnectionInfo *info)
633 {
634     struct mac_session* s = (struct mac_session*)session;
635     SSLCipherSuite cipherSuite;
636     const struct cipher_suite* c;
637     OSStatus status;
638
639     TRACE("(%p/%p, %p)\n", s, s->context, info);
640
641     status = SSLGetNegotiatedCipher(s->context, &cipherSuite);
642     if (status != noErr)
643     {
644         ERR("Failed to get session cipher suite: %ld\n", status);
645         return SEC_E_INTERNAL_ERROR;
646     }
647
648     c = get_cipher_suite(cipherSuite);
649     if (!c)
650     {
651         ERR("Unknown session cipher suite: %#x\n", (unsigned int)cipherSuite);
652         return SEC_E_INTERNAL_ERROR;
653     }
654
655     info->dwProtocol = schan_get_session_protocol(s);
656     info->aiCipher = schan_get_cipher_algid(c);
657     info->dwCipherStrength = schan_get_cipher_key_size(c);
658     info->aiHash = schan_get_mac_algid(c);
659     info->dwHashStrength = schan_get_mac_key_size(c);
660     info->aiExch = schan_get_kx_algid(c);
661     /* FIXME: info->dwExchStrength? */
662     info->dwExchStrength = 0;
663
664     return SEC_E_OK;
665 }
666
667 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session,
668                                                        PCCERT_CONTEXT *cert)
669 {
670     struct mac_session* s = (struct mac_session*)session;
671     SECURITY_STATUS ret = SEC_E_INTERNAL_ERROR;
672     CFArrayRef certs;
673     OSStatus status;
674
675     TRACE("(%p/%p, %p)\n", s, s->context, cert);
676
677     status = SSLCopyPeerCertificates(s->context, &certs);
678     if (status == noErr && certs)
679     {
680         SecCertificateRef mac_cert;
681         CFDataRef data;
682         if (CFArrayGetCount(certs) &&
683             (mac_cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, 0)) &&
684             (SecKeychainItemExport(mac_cert, kSecFormatX509Cert, 0, NULL, &data) == noErr))
685         {
686             *cert = CertCreateCertificateContext(X509_ASN_ENCODING,
687                     CFDataGetBytePtr(data), CFDataGetLength(data));
688             if (*cert)
689                 ret = SEC_E_OK;
690             else
691             {
692                 ret = GetLastError();
693                 WARN("CertCreateCertificateContext failed: %x\n", ret);
694             }
695             CFRelease(data);
696         }
697         else
698             WARN("Couldn't extract certificate data\n");
699         CFRelease(certs);
700     }
701     else
702         WARN("SSLCopyPeerCertificates failed: %ld\n", (long)status);
703
704     return ret;
705 }
706
707 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
708                                SIZE_T *length)
709 {
710     struct mac_session* s = (struct mac_session*)session;
711     OSStatus status;
712
713     TRACE("(%p/%p, %p, %p/%lu)\n", s, s->context, buffer, length, *length);
714
715     status = SSLWrite(s->context, buffer, *length, length);
716     if (status == noErr)
717         TRACE("Wrote %lu bytes\n", *length);
718     else if (status == errSSLWouldBlock)
719     {
720         if (!*length)
721         {
722             TRACE("Would block before being able to write anything\n");
723             return SEC_I_CONTINUE_NEEDED;
724         }
725         else
726             TRACE("Wrote %lu bytes before would block\n", *length);
727     }
728     else
729     {
730         WARN("SSLWrite failed: %ld\n", (long)status);
731         return SEC_E_INTERNAL_ERROR;
732     }
733
734     return SEC_E_OK;
735 }
736
737 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
738                                SIZE_T *length)
739 {
740     struct mac_session* s = (struct mac_session*)session;
741     OSStatus status;
742
743     TRACE("(%p/%p, %p, %p/%lu)\n", s, s->context, buffer, length, *length);
744
745     status = SSLRead(s->context, buffer, *length, length);
746     if (status == noErr)
747         TRACE("Read %lu bytes\n", *length);
748     else if (status == errSSLWouldBlock)
749     {
750         if (!*length)
751         {
752             TRACE("Would block before being able to read anything\n");
753             return SEC_I_CONTINUE_NEEDED;
754         }
755         else
756             TRACE("Read %lu bytes before would block\n", *length);
757     }
758     else
759     {
760         WARN("SSLRead failed: %ld\n", (long)status);
761         return SEC_E_INTERNAL_ERROR;
762     }
763
764     return SEC_E_OK;
765 }
766
767 BOOL schan_imp_allocate_certificate_credentials(schan_imp_certificate_credentials *c)
768 {
769     /* The certificate is never really used for anything. */
770     *c = NULL;
771     return TRUE;
772 }
773
774 void schan_imp_free_certificate_credentials(schan_imp_certificate_credentials c)
775 {
776 }
777
778 BOOL schan_imp_init(void)
779 {
780     TRACE("()\n");
781     return TRUE;
782 }
783
784 void schan_imp_deinit(void)
785 {
786     TRACE("()\n");
787 }
788
789 #endif /* HAVE_SECURITY_SECURITY_H */