1 /*
2  * Lightweight mbedTLS-based implementation of the schannel (SSL/TLS) provider.
3  *
4  * Copyright 2015 Peter Hater
5  * Copyright 2015 Ismael Ferreras Morezuelas <swyterzone+ros@gmail.com>
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 #ifdef __REACTOS__
26  #include "precomp.h"
27 #else
28  #include <stdarg.h>
29  #include <errno.h>
30 
31  #include "windef.h"
32  #include "winbase.h"
33  #include "sspi.h"
34  #include "schannel.h"
35  #include "wine/debug.h"
36  #include "wine/library.h"
37 #endif
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(schannel);
40 
41 #if defined(SONAME_LIBMBEDTLS) && !defined(HAVE_SECURITY_SECURITY_H) && !defined(SONAME_LIBGNUTLS)
42 
43 #include <mbedtls/ssl.h>
44 #include <mbedtls/net_sockets.h>
45 
46 #include <mbedtls/entropy.h>
47 #include <mbedtls/ctr_drbg.h>
48 #include <mbedtls/md_internal.h>
49 #include <mbedtls/ssl_internal.h>
50 
51 #define ROS_SCHAN_IS_BLOCKING(read_len)          ((read_len & 0xFFF00000) == 0xCCC00000)
52 #define ROS_SCHAN_IS_BLOCKING_MARSHALL(read_len) ((read_len & 0x000FFFFF) |  0xCCC00000)
53 #define ROS_SCHAN_IS_BLOCKING_RETRIEVE(read_len)  (read_len & 0x000FFFFF)
54 
55 #ifndef __REACTOS__
56  /* WINE defines the back-end glue in here */
57  #include "secur32_priv.h"
58 
59  /* in ReactOS we use schannel instead of secur32 */
60  WINE_DEFAULT_DEBUG_CHANNEL(secur32);
61 
62  /* WINE prefers to keep it optional, disable this to link explicitly */
63  #include "schannel_mbedtls_lazyload.h"
64 
65  /* WINE does not define this standard win32 macro for some reason */
66  #ifndef _countof
67   #define _countof(a) (sizeof(a)/sizeof(*(a)))
68  #endif
69 #endif
70 
71 typedef struct
72 {
73     mbedtls_ssl_context      ssl;
74     mbedtls_ssl_config       conf;
75     mbedtls_entropy_context  entropy;
76     mbedtls_ctr_drbg_context ctr_drbg;
77     struct schan_transport  *transport;
78 } MBEDTLS_SESSION, *PMBEDTLS_SESSION;
79 
80 /* custom `net_recv` callback adapter, mbedTLS uses it in mbedtls_ssl_read for
81    pulling data from the underlying win32 net stack */
82 static int schan_pull_adapter(void *session, unsigned char *buff, size_t buff_len)
83 {
84     MBEDTLS_SESSION *s = session;
85     size_t requested = buff_len;
86     int status;
87 
88     TRACE("MBEDTLS schan_pull_adapter: (%p/%p, %p, %u)\n", s, s->transport, buff, buff_len);
89 
90     status = schan_pull(s->transport, buff, &buff_len);
91 
92     TRACE("MBEDTLS schan_pull_adapter: (%p/%p, %p, %u) status: %#x\n", s, s->transport, buff, buff_len, status);
93 
94     if (status == NO_ERROR)
95     {
96         /* great, no more data left */
97         if (buff_len == 0)
98         {
99             TRACE("Connection closed\n");
100             return 0;
101         }
102         /* there's still some bytes that need pulling */
103         else if (buff_len < requested)
104         {
105             TRACE("Pulled %u bytes before would block\n", buff_len);
106             return ROS_SCHAN_IS_BLOCKING_MARSHALL(buff_len);
107         }
108         else
109         {
110             TRACE("Pulled %u bytes\n", buff_len);
111             return buff_len;
112         }
113     }
114     else if (status == EAGAIN)
115     {
116         TRACE("Would block before being able to pull anything, passing buff_len=%u\n", buff_len);
117         return ROS_SCHAN_IS_BLOCKING_MARSHALL(buff_len);
118     }
119     else
120     {
121         ERR("Unknown status code from schan_pull: %d\n", status);
122         return MBEDTLS_ERR_NET_RECV_FAILED;
123     }
124 
125     /* this should be unreachable */
126     return MBEDTLS_ERR_NET_CONNECT_FAILED;
127 }
128 
129 /* custom `net_send` callback adapter, mbedTLS uses it in mbedtls_ssl_write for
130    pushing data to the underlying win32 net stack */
131 static int schan_push_adapter(void *session, const unsigned char *buff, size_t buff_len)
132 {
133     MBEDTLS_SESSION *s = session;
134     int status;
135 
136     TRACE("MBEDTLS schan_push_adapter: (%p/%p, %p, %u)\n", s, s->transport, buff, buff_len);
137 
138     status = schan_push(s->transport, buff, &buff_len);
139 
140     TRACE("MBEDTLS schan_push_adapter: (%p/%p, %p, %u) status: %#x\n", s, s->transport, buff, buff_len, status);
141 
142     if (status == NO_ERROR)
143     {
144         TRACE("Pushed %u bytes\n", buff_len);
145         return buff_len;
146     }
147     else if (status == EAGAIN)
148     {
149         TRACE("Would block before being able to push anything. passing %u\n", buff_len);
150         return ROS_SCHAN_IS_BLOCKING_MARSHALL(buff_len);
151     }
152     else
153     {
154         ERR("Unknown status code from schan_push: %d\n", status);
155         return MBEDTLS_ERR_NET_SEND_FAILED;
156     }
157 
158     /* this should be unreachable */
159     return MBEDTLS_ERR_NET_CONNECT_FAILED;
160 }
161 
162 DWORD schan_imp_enabled_protocols(void)
163 {
164     /* NOTE: No support for SSL 2.0 */
165     TRACE("MBEDTLS schan_imp_enabled_protocols()\n");
166 
167     return 0
168 #ifdef MBEDTLS_SSL_PROTO_SSL3
169         | SP_PROT_SSL3_CLIENT | SP_PROT_SSL3_SERVER
170 #endif
171 #ifdef MBEDTLS_SSL_PROTO_TLS1
172         | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_0_SERVER
173 #endif
174 #ifdef MBEDTLS_SSL_PROTO_TLS1_1
175         | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_1_SERVER
176 #endif
177 #ifdef MBEDTLS_SSL_PROTO_TLS1_2
178         | SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_2_SERVER
179 #endif
180         ;
181 }
182 
183 static void schan_imp_debug(void *ctx, int level, const char *file, int line, const char *str)
184 {
185     WARN("MBEDTLS schan_imp_debug: %s:%04d: %s\n", file, line, str);
186 }
187 
188 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
189 {
190     MBEDTLS_SESSION *s = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MBEDTLS_SESSION));
191 
192     WARN("MBEDTLS schan_imp_create_session: %p %p %p\n", session, *session, cred);
193 
194     if (!(*session = (schan_imp_session)s))
195     {
196         ERR("Not enough memory to create session\n");
197         return FALSE;
198     }
199 
200     TRACE("MBEDTLS init entropy\n");
201     mbedtls_entropy_init(&s->entropy);
202 
203     TRACE("MBEDTLS init random - change static entropy private data\n");
204     mbedtls_ctr_drbg_init(&s->ctr_drbg);
205     mbedtls_ctr_drbg_seed(&s->ctr_drbg, mbedtls_entropy_func, &s->entropy, NULL, 0);
206 
207     WARN("MBEDTLS init ssl\n");
208     mbedtls_ssl_init(&s->ssl);
209 
210     WARN("MBEDTLS init conf\n");
211     mbedtls_ssl_config_init(&s->conf);
212     mbedtls_ssl_config_defaults(&s->conf, MBEDTLS_SSL_IS_CLIENT,
213                                           MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
214 
215     TRACE("MBEDTLS set BIO callbacks\n");
216     mbedtls_ssl_set_bio(&s->ssl, s, schan_push_adapter, schan_pull_adapter, NULL);
217 
218     TRACE("MBEDTLS set endpoint to %s\n", (cred->credential_use & SECPKG_CRED_INBOUND) ? "server" : "client");
219     mbedtls_ssl_conf_endpoint(&s->conf,   (cred->credential_use & SECPKG_CRED_INBOUND) ? MBEDTLS_SSL_IS_SERVER :
220                                                                                          MBEDTLS_SSL_IS_CLIENT);
221 
222     TRACE("MBEDTLS set authmode\n");
223     mbedtls_ssl_conf_authmode(&s->conf, MBEDTLS_SSL_VERIFY_NONE);
224 
225     TRACE("MBEDTLS set rng\n");
226     mbedtls_ssl_conf_rng(&s->conf, mbedtls_ctr_drbg_random, &s->ctr_drbg);
227 
228     TRACE("MBEDTLS set dbg\n");
229     mbedtls_ssl_conf_dbg(&s->conf, schan_imp_debug, stdout);
230 
231     TRACE("MBEDTLS setup\n");
232     mbedtls_ssl_setup(&s->ssl, &s->conf);
233 
234     TRACE("MBEDTLS schan_imp_create_session END!\n");
235     return TRUE;
236 }
237 
238 void schan_imp_dispose_session(schan_imp_session session)
239 {
240     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
241     WARN("MBEDTLS schan_imp_dispose_session: %p\n", session);
242 
243     /* tell the other peer (a server) that we are going away */
244     //ssl_close_notify(&s->ssl);
245 
246     mbedtls_ssl_free(&s->ssl);
247     mbedtls_ctr_drbg_free(&s->ctr_drbg);
248     mbedtls_entropy_free(&s->entropy);
249     mbedtls_ssl_config_free(&s->conf);
250 
251     /* safely overwrite the freed context with zeroes */
252     HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, s);
253 }
254 
255 void schan_imp_set_session_transport(schan_imp_session session,
256                                      struct schan_transport *t)
257 {
258     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
259 
260     TRACE("MBEDTLS schan_imp_set_session_transport: %p %p\n", session, t);
261 
262     s->transport = t;
263 }
264 
265 void schan_imp_set_session_target(schan_imp_session session, const char *target)
266 {
267     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
268 
269     TRACE("MBEDTLS schan_imp_set_session_target: sess: %p hostname: %s\n", session, target);
270 
271     /* FIXME: WINE tests do not pass when we set the hostname because in the test cases
272      * contacting 'www.winehq.org' the hostname is defined as 'localhost' so the server
273      * sends a non-fatal alert which preemptively forces mbedTLS to close connection. */
274 
275     mbedtls_ssl_set_hostname(&s->ssl, target);
276 }
277 
278 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
279 {
280     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
281 
282     int err = mbedtls_ssl_handshake(&s->ssl);
283 
284     TRACE("MBEDTLS schan_imp_handshake: %p  err: %#x \n", session, err);
285 
286     if (ROS_SCHAN_IS_BLOCKING(err))
287     {
288         TRACE("Received ERR_NET_WANT_READ/WRITE... let's try again!\n");
289         return SEC_I_CONTINUE_NEEDED;
290     }
291     else if (err == MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE)
292     {
293         ERR("schan_imp_handshake: SSL Feature unavailable...\n");
294         return SEC_E_UNSUPPORTED_FUNCTION;
295     }
296     else if (err != 0)
297     {
298         ERR("schan_imp_handshake: Oops! mbedtls_ssl_handshake returned the following error code: -%#x...\n", -err);
299         return SEC_E_INTERNAL_ERROR;
300     }
301 
302     WARN("schan_imp_handshake: Handshake completed!\n");
303     WARN("schan_imp_handshake: Protocol is %s, Cipher suite is %s\n", mbedtls_ssl_get_version(&s->ssl),
304                                                                       mbedtls_ssl_get_ciphersuite(&s->ssl));
305     return SEC_E_OK;
306 }
307 
308 static unsigned int schannel_get_cipher_key_size(int ciphersuite_id)
309 {
310     const mbedtls_ssl_ciphersuite_t *ssl_cipher_suite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
311     const mbedtls_cipher_info_t          *cipher_info = mbedtls_cipher_info_from_type(ssl_cipher_suite->cipher);
312 
313     unsigned int key_bitlen = cipher_info->key_bitlen;
314 
315     TRACE("MBEDTLS schannel_get_cipher_key_size: Unknown cipher %#x, returning %u\n", ciphersuite_id, key_bitlen);
316 
317     return key_bitlen;
318 }
319 
320 static unsigned int schannel_get_mac_key_size(int ciphersuite_id)
321 {
322     const mbedtls_ssl_ciphersuite_t *ssl_cipher_suite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
323     const mbedtls_md_info_t                  *md_info = mbedtls_md_info_from_type(ssl_cipher_suite->mac);
324 
325     int md_size = md_info->size * CHAR_BIT; /* return the size in bits, as the secur32:schannel winetest shows */
326 
327     TRACE("MBEDTLS schannel_get_mac_key_size: returning %i\n", md_size);
328 
329     return md_size;
330 }
331 
332 static unsigned int schannel_get_kx_key_size(const mbedtls_ssl_context *ssl, const mbedtls_ssl_config *conf, int ciphersuite_id)
333 {
334     const mbedtls_ssl_ciphersuite_t *ssl_ciphersuite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
335 
336     /* if we are the server take ca_chain, if we are the client take the proper x509 peer certificate */
337     const mbedtls_x509_crt *server_cert = (conf->endpoint == MBEDTLS_SSL_IS_SERVER) ? conf->ca_chain : mbedtls_ssl_get_peer_cert(ssl);
338 
339     if (ssl_ciphersuite->key_exchange != MBEDTLS_KEY_EXCHANGE_NONE)
340         return mbedtls_pk_get_len(&(server_cert->pk));
341 
342     TRACE("MBEDTLS schannel_get_kx_key_size: Unknown kx %#x, returning 0\n", ssl_ciphersuite->key_exchange);
343 
344     return 0;
345 }
346 
347 static DWORD schannel_get_protocol(const mbedtls_ssl_context *ssl, const mbedtls_ssl_config *conf)
348 {
349     /* FIXME: currently schannel only implements client connections, but
350      * there's no reason it couldn't be used for servers as well. The
351      * context doesn't tell us which it is, so decide based on ssl endpoint value. */
352 
353     switch (ssl->minor_ver)
354     {
355         case MBEDTLS_SSL_MINOR_VERSION_0:
356             return (conf->endpoint == MBEDTLS_SSL_IS_CLIENT) ? SP_PROT_SSL3_CLIENT :
357                                                                SP_PROT_SSL3_SERVER;
358 
359         case MBEDTLS_SSL_MINOR_VERSION_1:
360             return (conf->endpoint == MBEDTLS_SSL_IS_CLIENT) ? SP_PROT_TLS1_0_CLIENT :
361                                                                SP_PROT_TLS1_0_SERVER;
362 
363         case MBEDTLS_SSL_MINOR_VERSION_2:
364             return (conf->endpoint == MBEDTLS_SSL_IS_CLIENT) ? SP_PROT_TLS1_1_CLIENT :
365                                                                SP_PROT_TLS1_1_SERVER;
366 
367         case MBEDTLS_SSL_MINOR_VERSION_3:
368             return (conf->endpoint == MBEDTLS_SSL_IS_CLIENT) ? SP_PROT_TLS1_2_CLIENT :
369                                                                SP_PROT_TLS1_2_SERVER;
370 
371         default:
372         {
373             FIXME("MBEDTLS schannel_get_protocol: unknown protocol %d\n", ssl->minor_ver);
374             return 0;
375         }
376     }
377 }
378 
379 static ALG_ID schannel_get_cipher_algid(int ciphersuite_id)
380 {
381     const mbedtls_ssl_ciphersuite_t *cipher_suite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
382 
383     switch (cipher_suite->cipher)
384     {
385         case MBEDTLS_CIPHER_NONE:
386         case MBEDTLS_CIPHER_NULL:
387             return 0;
388 
389 #ifdef MBEDTLS_ARC4_C
390         /* ARC4 */
391         case MBEDTLS_CIPHER_ARC4_128:
392             return CALG_RC4;
393 #endif
394 
395 #ifdef MBEDTLS_DES_C
396         /* DES */
397         case MBEDTLS_CIPHER_DES_ECB:
398         case MBEDTLS_CIPHER_DES_CBC:
399         case MBEDTLS_CIPHER_DES_EDE_ECB:
400         case MBEDTLS_CIPHER_DES_EDE_CBC:
401             return CALG_DES;
402 
403         case MBEDTLS_CIPHER_DES_EDE3_ECB:
404         case MBEDTLS_CIPHER_DES_EDE3_CBC:
405             return CALG_3DES;
406 #endif
407 
408 #ifdef MBEDTLS_BLOWFISH_C
409         /* BLOWFISH */
410         case MBEDTLS_CIPHER_BLOWFISH_ECB:
411         case MBEDTLS_CIPHER_BLOWFISH_CBC:
412         case MBEDTLS_CIPHER_BLOWFISH_CFB64:
413         case MBEDTLS_CIPHER_BLOWFISH_CTR:
414             return CALG_RC4;  // (as schannel does not support it fake it as RC4, which has a
415                               //  similar profile of low footprint and medium-high security) CALG_BLOWFISH;
416 #endif
417 
418 #ifdef MBEDTLS_CAMELLIA_C
419         /* CAMELLIA */
420         case MBEDTLS_CIPHER_CAMELLIA_128_ECB:
421         case MBEDTLS_CIPHER_CAMELLIA_192_ECB:
422         case MBEDTLS_CIPHER_CAMELLIA_256_ECB:
423         case MBEDTLS_CIPHER_CAMELLIA_128_CBC:
424         case MBEDTLS_CIPHER_CAMELLIA_192_CBC:
425         case MBEDTLS_CIPHER_CAMELLIA_256_CBC:
426         case MBEDTLS_CIPHER_CAMELLIA_128_CFB128:
427         case MBEDTLS_CIPHER_CAMELLIA_192_CFB128:
428         case MBEDTLS_CIPHER_CAMELLIA_256_CFB128:
429         case MBEDTLS_CIPHER_CAMELLIA_128_CTR:
430         case MBEDTLS_CIPHER_CAMELLIA_192_CTR:
431         case MBEDTLS_CIPHER_CAMELLIA_256_CTR:
432         case MBEDTLS_CIPHER_CAMELLIA_128_GCM:
433         case MBEDTLS_CIPHER_CAMELLIA_192_GCM:
434         case MBEDTLS_CIPHER_CAMELLIA_256_GCM:
435             return CALG_AES_256;  // (as schannel does not support it fake it as AES, which has a
436                                   //  similar profile, offering modern high security) CALG_CAMELLIA;
437 #endif
438 
439 #ifdef MBEDTLS_AES_C
440         /* AES 128 */
441         case MBEDTLS_CIPHER_AES_128_ECB:
442         case MBEDTLS_CIPHER_AES_128_CBC:
443         case MBEDTLS_CIPHER_AES_128_CFB128:
444         case MBEDTLS_CIPHER_AES_128_CTR:
445         case MBEDTLS_CIPHER_AES_128_GCM:
446     #ifdef MBEDTLS_CCM_C
447         case MBEDTLS_CIPHER_AES_128_CCM:
448     #endif
449             return CALG_AES_128;
450 
451         case MBEDTLS_CIPHER_AES_192_ECB:
452         case MBEDTLS_CIPHER_AES_192_CBC:
453         case MBEDTLS_CIPHER_AES_192_CFB128:
454         case MBEDTLS_CIPHER_AES_192_CTR:
455         case MBEDTLS_CIPHER_AES_192_GCM:
456     #ifdef MBEDTLS_CCM_C
457         case MBEDTLS_CIPHER_AES_192_CCM:
458     #endif
459             return CALG_AES_192;
460 
461         case MBEDTLS_CIPHER_AES_256_ECB:
462         case MBEDTLS_CIPHER_AES_256_CBC:
463         case MBEDTLS_CIPHER_AES_256_CFB128:
464         case MBEDTLS_CIPHER_AES_256_CTR:
465         case MBEDTLS_CIPHER_AES_256_GCM:
466     #ifdef MBEDTLS_CCM_C
467         case MBEDTLS_CIPHER_AES_256_CCM:
468     #endif
469             return CALG_AES_256;
470 #endif
471 
472         /* nothing to show? fall through */
473         default:
474         {
475             FIXME("MBEDTLS schannel_get_cipher_algid: unknown algorithm %d\n", ciphersuite_id);
476             return 0;
477         }
478     }
479 }
480 
481 static ALG_ID schannel_get_mac_algid(int ciphersuite_id)
482 {
483     const mbedtls_ssl_ciphersuite_t *cipher_suite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
484 
485     switch (cipher_suite->mac)
486     {
487         case MBEDTLS_MD_NONE:      return 0;
488         case MBEDTLS_MD_MD2:       return CALG_MD2;
489         case MBEDTLS_MD_MD4:       return CALG_MD4;
490         case MBEDTLS_MD_MD5:       return CALG_MD5;
491         case MBEDTLS_MD_SHA1:      return CALG_SHA1;
492         case MBEDTLS_MD_SHA224:    return CALG_SHA;
493         case MBEDTLS_MD_SHA256:    return CALG_SHA_256;
494         case MBEDTLS_MD_SHA384:    return CALG_SHA_384;
495         case MBEDTLS_MD_SHA512:    return CALG_SHA_512;
496         case MBEDTLS_MD_RIPEMD160: return (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_RIPEMD160); /* there's no CALG_RIPEMD or CALG_RIPEMD160 defined in <wincrypt.h> yet */
497 
498         default:
499         {
500             FIXME("MBEDTLS schannel_get_mac_algid: unknown algorithm %d\n", cipher_suite->mac);
501             return 0;
502         }
503     }
504 }
505 
506 static ALG_ID schannel_get_kx_algid(int ciphersuite_id)
507 {
508     const mbedtls_ssl_ciphersuite_t *cipher_suite = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id);
509 
510     switch (cipher_suite->key_exchange)
511     {
512         case MBEDTLS_KEY_EXCHANGE_NONE:
513         case MBEDTLS_KEY_EXCHANGE_PSK: /* the original implementation does not support    */
514             return 0;                  /* any PSK, and does not define any `CALG_PSK` :)  */
515 
516         case MBEDTLS_KEY_EXCHANGE_RSA:
517         case MBEDTLS_KEY_EXCHANGE_RSA_PSK:
518             return CALG_RSA_KEYX;
519 
520         case MBEDTLS_KEY_EXCHANGE_DHE_RSA:
521         case MBEDTLS_KEY_EXCHANGE_DHE_PSK:
522             return CALG_DH_EPHEM;
523 
524         case MBEDTLS_KEY_EXCHANGE_ECDH_RSA:
525         case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA:
526             return CALG_ECDH;
527 
528         case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA:
529         case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA:
530         case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK:
531             return CALG_ECDH_EPHEM;
532 
533         default:
534         {
535             FIXME("MBEDTLS schannel_get_kx_algid: unknown algorithm %d\n", cipher_suite->key_exchange);
536             return 0;
537         }
538     }
539 }
540 
541 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
542 {
543     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
544 
545     unsigned int cipher_block_size = mbedtls_cipher_get_block_size(&s->ssl.transform->cipher_ctx_enc);
546 
547     TRACE("MBEDTLS schan_imp_get_session_cipher_block_size %p returning %u.\n", session, cipher_block_size);
548 
549     return cipher_block_size;
550 }
551 
552 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
553 {
554     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
555 
556     unsigned int max_frag_len = mbedtls_ssl_get_max_frag_len(&s->ssl);
557 
558     TRACE("MBEDTLS schan_imp_get_max_message_size %p returning %u.\n", session, max_frag_len);
559 
560     return max_frag_len;
561 }
562 
563 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
564                                               SecPkgContext_ConnectionInfo *info)
565 {
566     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
567 
568     int ciphersuite_id = mbedtls_ssl_get_ciphersuite_id(mbedtls_ssl_get_ciphersuite(&s->ssl));
569 
570     TRACE("MBEDTLS schan_imp_get_connection_info %p %p.\n", session, info);
571 
572     info->dwProtocol       = schannel_get_protocol(&s->ssl, &s->conf);
573     info->aiCipher         = schannel_get_cipher_algid(ciphersuite_id);
574     info->dwCipherStrength = schannel_get_cipher_key_size(ciphersuite_id);
575     info->aiHash           = schannel_get_mac_algid(ciphersuite_id);
576     info->dwHashStrength   = schannel_get_mac_key_size(ciphersuite_id);
577     info->aiExch           = schannel_get_kx_algid(ciphersuite_id);
578     info->dwExchStrength   = schannel_get_kx_key_size(&s->ssl, &s->conf, ciphersuite_id);
579 
580     return SEC_E_OK;
581 }
582 
583 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
584                                                        PCCERT_CONTEXT *ret)
585 {
586     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
587     PCCERT_CONTEXT cert_context = NULL;
588 
589     const mbedtls_x509_crt *next_cert;
590     const mbedtls_x509_crt *peer_cert = mbedtls_ssl_get_peer_cert(&s->ssl);
591 
592     TRACE("MBEDTLS schan_imp_get_session_peer_certificate %p %p %p %p.\n", session, store, ret, ret != NULL ? *ret : NULL);
593 
594     if (!peer_cert)
595         return SEC_E_INTERNAL_ERROR;
596 
597     for (next_cert = peer_cert; next_cert != NULL; next_cert = next_cert->next)
598     {
599         if (!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, next_cert->raw.p, next_cert->raw.len,
600             CERT_STORE_ADD_REPLACE_EXISTING, (next_cert != peer_cert) ? NULL : &cert_context))
601         {
602             if (next_cert != peer_cert)
603                 CertFreeCertificateContext(cert_context);
604             return GetLastError();
605         }
606     }
607 
608     *ret = cert_context;
609     return SEC_E_OK;
610 }
611 
612 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
613                                SIZE_T *length)
614 {
615     MBEDTLS_SESSION *s = (MBEDTLS_SESSION *)session;
616     int ret;
617 
618     ret = mbedtls_ssl_write(&s->ssl, (unsigned char *)buffer, *length);
619 
620     TRACE("MBEDTLS schan_imp_send: (%p, %p, %p/%lu)\n", s, buffer, length, *length);
621 
622     if (ret >= 0)
623     {
624         TRACE("MBEDTLS schan_imp_send: ret=%i.\n", ret);
625 
626         *length = ret;
627     }
628     else if (ROS_SCHAN_IS_BLOCKING(ret))
629     {
630         *length = ROS_SCHAN_IS_BLOCKING_RETRIEVE(ret);
631 
632         if (!*length)
633         {
634             TRACE("MBEDTLS schan_imp_send: ret=MBEDTLS_ERR_NET_WANT_WRITE -> SEC_I_CONTINUE_NEEDED; len=%lu", *length);
635             return SEC_I_CONTINUE_NEEDED;
636         }
637         else
638         {
639             TRACE("MBEDTLS schan_imp_send: ret=MBEDTLS_ERR_NET_WANT_WRITE -> SEC_E_OK; len=%lu", *length);
640             return SEC_E_OK;
641         }
642     }
643     else
644     {
645         ERR("MBEDTLS schan_imp_send: mbedtls_ssl_write failed with -%x\n", -ret);
646         return SEC_E_INTERNAL_ERROR;
647     }
648 
649     return SEC_E_OK;
650 }
651 
652 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
653                                SIZE_T *length)
654 {
655     PMBEDTLS_SESSION s = (PMBEDTLS_SESSION)session;
656     int ret;
657 
658     TRACE("MBEDTLS schan_imp_recv: (%p, %p, %p/%lu)\n", s, buffer, length, *length);
659 
660     ret = mbedtls_ssl_read(&s->ssl, (unsigned char *)buffer, *length);
661 
662     TRACE("MBEDTLS schan_imp_recv: (%p, %p, %p/%lu) ret= %#x\n", s, buffer, length, *length, ret);
663 
664     if (ret >= 0)
665     {
666         TRACE("MBEDTLS schan_imp_recv: ret == %i.\n", ret);
667 
668         *length = ret;
669     }
670     else if (ROS_SCHAN_IS_BLOCKING(ret))
671     {
672         *length = ROS_SCHAN_IS_BLOCKING_RETRIEVE(ret);
673 
674         if (!*length)
675         {
676             TRACE("MBEDTLS schan_imp_recv: ret=MBEDTLS_ERR_NET_WANT_WRITE -> SEC_I_CONTINUE_NEEDED; len=%lu", *length);
677             return SEC_I_CONTINUE_NEEDED;
678         }
679         else
680         {
681             TRACE("MBEDTLS schan_imp_recv: ret=MBEDTLS_ERR_NET_WANT_WRITE -> SEC_E_OK; len=%lu", *length);
682             return SEC_E_OK;
683         }
684     }
685     else if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
686     {
687         *length = 0;
688         TRACE("MBEDTLS schan_imp_recv: ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY -> SEC_E_OK\n");
689         return SEC_E_OK;
690     }
691     else
692     {
693         ERR("MBEDTLS schan_imp_recv: mbedtls_ssl_read failed with -%x\n", -ret);
694         return SEC_E_INTERNAL_ERROR;
695     }
696 
697     return SEC_E_OK;
698 }
699 
700 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
701 {
702     TRACE("MBEDTLS schan_imp_allocate_certificate_credentials %p %p %d\n", c, c->credentials, c->credential_use);
703 
704     /* in our case credentials aren't really used for anything, so just stub them */
705     c->credentials = NULL;
706     return TRUE;
707 }
708 
709 void schan_imp_free_certificate_credentials(schan_credentials *c)
710 {
711     TRACE("MBEDTLS schan_imp_free_certificate_credentials %p %p %d\n", c, c->credentials, c->credential_use);
712 }
713 
714 BOOL schan_imp_init(void)
715 {
716     TRACE("Schannel MBEDTLS schan_imp_init\n");
717     return TRUE;
718 }
719 
720 void schan_imp_deinit(void)
721 {
722     WARN("Schannel MBEDTLS schan_imp_deinit\n");
723 }
724 
725 #endif /* SONAME_LIBMBEDTLS && !HAVE_SECURITY_SECURITY_H && !SONAME_LIBGNUTLS */