TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / secur32 / schannel_gnutls.c
blob95c9bdab3fd159fecb413abeb9ce2fcc66c6ea5f
1 /*
2 * GnuTLS-based implementation of the schannel (SSL/TLS) provider.
4 * Copyright 2005 Juan Lang
5 * Copyright 2008 Henri Verbeet
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
26 #ifdef SONAME_LIBGNUTLS
27 #include <gnutls/gnutls.h>
28 #include <gnutls/crypto.h>
29 #endif
31 #include "windef.h"
32 #include "winbase.h"
33 #include "sspi.h"
34 #include "schannel.h"
35 #include "secur32_priv.h"
36 #include "wine/debug.h"
37 #include "wine/library.h"
39 #if defined(SONAME_LIBGNUTLS) && !defined(HAVE_SECURITY_SECURITY_H)
41 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
42 WINE_DECLARE_DEBUG_CHANNEL(winediag);
44 /* Not present in gnutls version < 2.9.10. */
45 extern int gnutls_cipher_get_block_size(gnutls_cipher_algorithm_t algorithm);
47 static void *libgnutls_handle;
48 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
49 MAKE_FUNCPTR(gnutls_alert_get);
50 MAKE_FUNCPTR(gnutls_alert_get_name);
51 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
52 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
53 MAKE_FUNCPTR(gnutls_certificate_get_peers);
54 MAKE_FUNCPTR(gnutls_cipher_get);
55 MAKE_FUNCPTR(gnutls_cipher_get_block_size);
56 MAKE_FUNCPTR(gnutls_cipher_get_key_size);
57 MAKE_FUNCPTR(gnutls_credentials_set);
58 MAKE_FUNCPTR(gnutls_deinit);
59 MAKE_FUNCPTR(gnutls_global_deinit);
60 MAKE_FUNCPTR(gnutls_global_init);
61 MAKE_FUNCPTR(gnutls_global_set_log_function);
62 MAKE_FUNCPTR(gnutls_global_set_log_level);
63 MAKE_FUNCPTR(gnutls_handshake);
64 MAKE_FUNCPTR(gnutls_init);
65 MAKE_FUNCPTR(gnutls_kx_get);
66 MAKE_FUNCPTR(gnutls_mac_get);
67 MAKE_FUNCPTR(gnutls_mac_get_key_size);
68 MAKE_FUNCPTR(gnutls_perror);
69 MAKE_FUNCPTR(gnutls_protocol_get_version);
70 MAKE_FUNCPTR(gnutls_priority_set_direct);
71 MAKE_FUNCPTR(gnutls_record_get_max_size);
72 MAKE_FUNCPTR(gnutls_record_recv);
73 MAKE_FUNCPTR(gnutls_record_send);
74 MAKE_FUNCPTR(gnutls_server_name_set);
75 MAKE_FUNCPTR(gnutls_transport_get_ptr);
76 MAKE_FUNCPTR(gnutls_transport_set_errno);
77 MAKE_FUNCPTR(gnutls_transport_set_ptr);
78 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
79 MAKE_FUNCPTR(gnutls_transport_set_push_function);
80 #undef MAKE_FUNCPTR
82 #if GNUTLS_VERSION_MAJOR < 3
83 #define GNUTLS_CIPHER_AES_192_CBC 92
84 #define GNUTLS_CIPHER_AES_128_GCM 93
85 #define GNUTLS_CIPHER_AES_256_GCM 94
86 #endif
88 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
90 switch(cipher) {
91 case GNUTLS_CIPHER_3DES_CBC:
92 return 8;
93 case GNUTLS_CIPHER_AES_128_CBC:
94 case GNUTLS_CIPHER_AES_256_CBC:
95 return 16;
96 case GNUTLS_CIPHER_ARCFOUR_128:
97 case GNUTLS_CIPHER_ARCFOUR_40:
98 return 1;
99 case GNUTLS_CIPHER_DES_CBC:
100 return 8;
101 case GNUTLS_CIPHER_NULL:
102 return 1;
103 case GNUTLS_CIPHER_RC2_40_CBC:
104 return 8;
105 default:
106 FIXME("Unknown cipher %#x, returning 1\n", cipher);
107 return 1;
111 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
112 void *buff, size_t buff_len)
114 struct schan_transport *t = (struct schan_transport*)transport;
115 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
117 int ret = schan_pull(transport, buff, &buff_len);
118 if (ret)
120 pgnutls_transport_set_errno(s, ret);
121 return -1;
124 return buff_len;
127 static ssize_t schan_push_adapter(gnutls_transport_ptr_t transport,
128 const void *buff, size_t buff_len)
130 struct schan_transport *t = (struct schan_transport*)transport;
131 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
133 int ret = schan_push(transport, buff, &buff_len);
134 if (ret)
136 pgnutls_transport_set_errno(s, ret);
137 return -1;
140 return buff_len;
143 static const struct {
144 DWORD enable_flag;
145 const char *gnutls_flag;
146 } protocol_priority_flags[] = {
147 {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
148 {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
149 {SP_PROT_TLS1_0_CLIENT, "VERS-TLS1.0"},
150 {SP_PROT_SSL3_CLIENT, "VERS-SSL3.0"}
151 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
154 DWORD schan_imp_enabled_protocols(void)
156 /* NOTE: No support for SSL 2.0 */
157 return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
160 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
162 gnutls_session_t *s = (gnutls_session_t*)session;
163 char priority[64] = "NORMAL", *p;
164 unsigned i;
166 int err = pgnutls_init(s, cred->credential_use == SECPKG_CRED_INBOUND ? GNUTLS_SERVER : GNUTLS_CLIENT);
167 if (err != GNUTLS_E_SUCCESS)
169 pgnutls_perror(err);
170 return FALSE;
173 p = priority + strlen(priority);
174 for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) {
175 *p++ = ':';
176 *p++ = (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) ? '+' : '-';
177 strcpy(p, protocol_priority_flags[i].gnutls_flag);
178 p += strlen(p);
181 TRACE("Using %s priority\n", debugstr_a(priority));
182 err = pgnutls_priority_set_direct(*s, priority, NULL);
183 if (err != GNUTLS_E_SUCCESS)
185 pgnutls_perror(err);
186 pgnutls_deinit(*s);
187 return FALSE;
190 err = pgnutls_credentials_set(*s, GNUTLS_CRD_CERTIFICATE,
191 (gnutls_certificate_credentials_t)cred->credentials);
192 if (err != GNUTLS_E_SUCCESS)
194 pgnutls_perror(err);
195 pgnutls_deinit(*s);
196 return FALSE;
199 pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
200 pgnutls_transport_set_push_function(*s, schan_push_adapter);
202 return TRUE;
205 void schan_imp_dispose_session(schan_imp_session session)
207 gnutls_session_t s = (gnutls_session_t)session;
208 pgnutls_deinit(s);
211 void schan_imp_set_session_transport(schan_imp_session session,
212 struct schan_transport *t)
214 gnutls_session_t s = (gnutls_session_t)session;
215 pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)t);
218 void schan_imp_set_session_target(schan_imp_session session, const char *target)
220 gnutls_session_t s = (gnutls_session_t)session;
222 pgnutls_server_name_set( s, GNUTLS_NAME_DNS, target, strlen(target) );
225 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
227 gnutls_session_t s = (gnutls_session_t)session;
228 int err;
230 while(1) {
231 err = pgnutls_handshake(s);
232 switch(err) {
233 case GNUTLS_E_SUCCESS:
234 TRACE("Handshake completed\n");
235 return SEC_E_OK;
237 case GNUTLS_E_AGAIN:
238 TRACE("Continue...\n");
239 return SEC_I_CONTINUE_NEEDED;
241 case GNUTLS_E_WARNING_ALERT_RECEIVED:
243 gnutls_alert_description_t alert = pgnutls_alert_get(s);
245 WARN("WARNING ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
247 switch(alert) {
248 case GNUTLS_A_UNRECOGNIZED_NAME:
249 TRACE("Ignoring\n");
250 continue;
251 default:
252 return SEC_E_INTERNAL_ERROR;
256 case GNUTLS_E_FATAL_ALERT_RECEIVED:
258 gnutls_alert_description_t alert = pgnutls_alert_get(s);
259 WARN("FATAL ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
260 return SEC_E_INTERNAL_ERROR;
263 default:
264 pgnutls_perror(err);
265 return SEC_E_INTERNAL_ERROR;
269 /* Never reached */
270 return SEC_E_OK;
273 static DWORD schannel_get_protocol(gnutls_protocol_t proto)
275 /* FIXME: currently schannel only implements client connections, but
276 * there's no reason it couldn't be used for servers as well. The
277 * context doesn't tell us which it is, so assume client for now.
279 switch (proto)
281 case GNUTLS_SSL3: return SP_PROT_SSL3_CLIENT;
282 case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
283 case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
284 case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
285 default:
286 FIXME("unknown protocol %d\n", proto);
287 return 0;
291 static ALG_ID schannel_get_cipher_algid(int cipher)
293 switch (cipher)
295 case GNUTLS_CIPHER_UNKNOWN:
296 case GNUTLS_CIPHER_NULL: return 0;
297 case GNUTLS_CIPHER_ARCFOUR_40:
298 case GNUTLS_CIPHER_ARCFOUR_128: return CALG_RC4;
299 case GNUTLS_CIPHER_DES_CBC:
300 case GNUTLS_CIPHER_3DES_CBC: return CALG_DES;
301 case GNUTLS_CIPHER_AES_128_CBC:
302 case GNUTLS_CIPHER_AES_128_GCM: return CALG_AES_128;
303 case GNUTLS_CIPHER_AES_192_CBC: return CALG_AES_192;
304 case GNUTLS_CIPHER_AES_256_GCM:
305 case GNUTLS_CIPHER_AES_256_CBC: return CALG_AES_256;
306 case GNUTLS_CIPHER_RC2_40_CBC: return CALG_RC2;
307 default:
308 FIXME("unknown algorithm %d\n", cipher);
309 return 0;
313 static ALG_ID schannel_get_mac_algid(gnutls_mac_algorithm_t mac)
315 switch (mac)
317 case GNUTLS_MAC_UNKNOWN:
318 case GNUTLS_MAC_NULL: return 0;
319 case GNUTLS_MAC_MD5: return CALG_MD5;
320 case GNUTLS_MAC_SHA1:
321 case GNUTLS_MAC_SHA256:
322 case GNUTLS_MAC_SHA384:
323 case GNUTLS_MAC_SHA512: return CALG_SHA;
324 default:
325 FIXME("unknown algorithm %d\n", mac);
326 return 0;
330 static ALG_ID schannel_get_kx_algid(gnutls_kx_algorithm_t kx)
332 switch (kx)
334 case GNUTLS_KX_RSA: return CALG_RSA_KEYX;
335 case GNUTLS_KX_DHE_DSS:
336 case GNUTLS_KX_DHE_RSA: return CALG_DH_EPHEM;
337 default:
338 FIXME("unknown algorithm %d\n", kx);
339 return 0;
343 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
345 gnutls_session_t s = (gnutls_session_t)session;
346 return pgnutls_cipher_get_block_size(pgnutls_cipher_get(s));
349 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
351 return pgnutls_record_get_max_size((gnutls_session_t)session);
354 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
355 SecPkgContext_ConnectionInfo *info)
357 gnutls_session_t s = (gnutls_session_t)session;
358 gnutls_protocol_t proto = pgnutls_protocol_get_version(s);
359 gnutls_cipher_algorithm_t alg = pgnutls_cipher_get(s);
360 gnutls_mac_algorithm_t mac = pgnutls_mac_get(s);
361 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
363 info->dwProtocol = schannel_get_protocol(proto);
364 info->aiCipher = schannel_get_cipher_algid(alg);
365 info->dwCipherStrength = pgnutls_cipher_get_key_size(alg) * 8;
366 info->aiHash = schannel_get_mac_algid(mac);
367 info->dwHashStrength = pgnutls_mac_get_key_size(mac) * 8;
368 info->aiExch = schannel_get_kx_algid(kx);
369 /* FIXME: info->dwExchStrength? */
370 info->dwExchStrength = 0;
371 return SEC_E_OK;
374 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
375 PCCERT_CONTEXT *ret)
377 gnutls_session_t s = (gnutls_session_t)session;
378 PCCERT_CONTEXT cert = NULL;
379 const gnutls_datum_t *datum;
380 unsigned list_size, i;
381 BOOL res;
383 datum = pgnutls_certificate_get_peers(s, &list_size);
384 if(!datum)
385 return SEC_E_INTERNAL_ERROR;
387 for(i = 0; i < list_size; i++) {
388 res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, datum[i].data, datum[i].size,
389 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert);
390 if(!res) {
391 if(i)
392 CertFreeCertificateContext(cert);
393 return GetLastError();
397 *ret = cert;
398 return SEC_E_OK;
401 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
402 SIZE_T *length)
404 gnutls_session_t s = (gnutls_session_t)session;
405 SSIZE_T ret, total = 0;
407 for (;;)
409 ret = pgnutls_record_send(s, (const char *)buffer + total, *length - total);
410 if (ret >= 0)
412 total += ret;
413 TRACE( "sent %ld now %ld/%ld\n", ret, total, *length );
414 if (total == *length) return SEC_E_OK;
416 else if (ret == GNUTLS_E_AGAIN)
418 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
419 SIZE_T count = 0;
421 if (schan_get_buffer(t, &t->out, &count)) continue;
422 return SEC_I_CONTINUE_NEEDED;
424 else
426 pgnutls_perror(ret);
427 return SEC_E_INTERNAL_ERROR;
432 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
433 SIZE_T *length)
435 gnutls_session_t s = (gnutls_session_t)session;
436 ssize_t ret;
438 again:
439 ret = pgnutls_record_recv(s, buffer, *length);
441 if (ret >= 0)
442 *length = ret;
443 else if (ret == GNUTLS_E_AGAIN)
445 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
446 SIZE_T count = 0;
448 if (schan_get_buffer(t, &t->in, &count))
449 goto again;
451 return SEC_I_CONTINUE_NEEDED;
453 else
455 pgnutls_perror(ret);
456 return SEC_E_INTERNAL_ERROR;
459 return SEC_E_OK;
462 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
464 int ret = pgnutls_certificate_allocate_credentials((gnutls_certificate_credentials_t*)&c->credentials);
465 if (ret != GNUTLS_E_SUCCESS)
466 pgnutls_perror(ret);
467 return (ret == GNUTLS_E_SUCCESS);
470 void schan_imp_free_certificate_credentials(schan_credentials *c)
472 pgnutls_certificate_free_credentials(c->credentials);
475 static void schan_gnutls_log(int level, const char *msg)
477 TRACE("<%d> %s", level, msg);
480 BOOL schan_imp_init(void)
482 int ret;
484 libgnutls_handle = wine_dlopen(SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0);
485 if (!libgnutls_handle)
487 ERR_(winediag)("Failed to load libgnutls, secure connections will not be available.\n");
488 return FALSE;
491 #define LOAD_FUNCPTR(f) \
492 if (!(p##f = wine_dlsym(libgnutls_handle, #f, NULL, 0))) \
494 ERR("Failed to load %s\n", #f); \
495 goto fail; \
498 LOAD_FUNCPTR(gnutls_alert_get)
499 LOAD_FUNCPTR(gnutls_alert_get_name)
500 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
501 LOAD_FUNCPTR(gnutls_certificate_free_credentials)
502 LOAD_FUNCPTR(gnutls_certificate_get_peers)
503 LOAD_FUNCPTR(gnutls_cipher_get)
504 LOAD_FUNCPTR(gnutls_cipher_get_key_size)
505 LOAD_FUNCPTR(gnutls_credentials_set)
506 LOAD_FUNCPTR(gnutls_deinit)
507 LOAD_FUNCPTR(gnutls_global_deinit)
508 LOAD_FUNCPTR(gnutls_global_init)
509 LOAD_FUNCPTR(gnutls_global_set_log_function)
510 LOAD_FUNCPTR(gnutls_global_set_log_level)
511 LOAD_FUNCPTR(gnutls_handshake)
512 LOAD_FUNCPTR(gnutls_init)
513 LOAD_FUNCPTR(gnutls_kx_get)
514 LOAD_FUNCPTR(gnutls_mac_get)
515 LOAD_FUNCPTR(gnutls_mac_get_key_size)
516 LOAD_FUNCPTR(gnutls_perror)
517 LOAD_FUNCPTR(gnutls_protocol_get_version)
518 LOAD_FUNCPTR(gnutls_priority_set_direct)
519 LOAD_FUNCPTR(gnutls_record_get_max_size);
520 LOAD_FUNCPTR(gnutls_record_recv);
521 LOAD_FUNCPTR(gnutls_record_send);
522 LOAD_FUNCPTR(gnutls_server_name_set)
523 LOAD_FUNCPTR(gnutls_transport_get_ptr)
524 LOAD_FUNCPTR(gnutls_transport_set_errno)
525 LOAD_FUNCPTR(gnutls_transport_set_ptr)
526 LOAD_FUNCPTR(gnutls_transport_set_pull_function)
527 LOAD_FUNCPTR(gnutls_transport_set_push_function)
528 #undef LOAD_FUNCPTR
530 if (!(pgnutls_cipher_get_block_size = wine_dlsym(libgnutls_handle, "gnutls_cipher_get_block_size", NULL, 0)))
532 WARN("gnutls_cipher_get_block_size not found\n");
533 pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
536 ret = pgnutls_global_init();
537 if (ret != GNUTLS_E_SUCCESS)
539 pgnutls_perror(ret);
540 goto fail;
543 if (TRACE_ON(secur32))
545 pgnutls_global_set_log_level(4);
546 pgnutls_global_set_log_function(schan_gnutls_log);
549 return TRUE;
551 fail:
552 wine_dlclose(libgnutls_handle, NULL, 0);
553 libgnutls_handle = NULL;
554 return FALSE;
557 void schan_imp_deinit(void)
559 pgnutls_global_deinit();
560 wine_dlclose(libgnutls_handle, NULL, 0);
561 libgnutls_handle = NULL;
564 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */