oledb32: Improve fixme in GetConversionSize.
[wine/multimedia.git] / dlls / secur32 / schannel_gnutls.c
blob70b827660f8e216532c9bec91f96b6b1886c42dd
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 #endif
30 #include "windef.h"
31 #include "winbase.h"
32 #include "sspi.h"
33 #include "schannel.h"
34 #include "secur32_priv.h"
35 #include "wine/debug.h"
36 #include "wine/library.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
40 #if defined(SONAME_LIBGNUTLS) && !defined(HAVE_SECURITY_SECURITY_H)
42 static void *libgnutls_handle;
43 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
44 MAKE_FUNCPTR(gnutls_alert_get);
45 MAKE_FUNCPTR(gnutls_alert_get_name);
46 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
47 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
48 MAKE_FUNCPTR(gnutls_certificate_get_peers);
49 MAKE_FUNCPTR(gnutls_cipher_get);
50 MAKE_FUNCPTR(gnutls_cipher_get_key_size);
51 MAKE_FUNCPTR(gnutls_credentials_set);
52 MAKE_FUNCPTR(gnutls_deinit);
53 MAKE_FUNCPTR(gnutls_global_deinit);
54 MAKE_FUNCPTR(gnutls_global_init);
55 MAKE_FUNCPTR(gnutls_global_set_log_function);
56 MAKE_FUNCPTR(gnutls_global_set_log_level);
57 MAKE_FUNCPTR(gnutls_handshake);
58 MAKE_FUNCPTR(gnutls_init);
59 MAKE_FUNCPTR(gnutls_kx_get);
60 MAKE_FUNCPTR(gnutls_mac_get);
61 MAKE_FUNCPTR(gnutls_mac_get_key_size);
62 MAKE_FUNCPTR(gnutls_perror);
63 MAKE_FUNCPTR(gnutls_protocol_get_version);
64 MAKE_FUNCPTR(gnutls_priority_set_direct);
65 MAKE_FUNCPTR(gnutls_record_get_max_size);
66 MAKE_FUNCPTR(gnutls_record_recv);
67 MAKE_FUNCPTR(gnutls_record_send);
68 MAKE_FUNCPTR(gnutls_transport_get_ptr);
69 MAKE_FUNCPTR(gnutls_transport_set_errno);
70 MAKE_FUNCPTR(gnutls_transport_set_ptr);
71 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
72 MAKE_FUNCPTR(gnutls_transport_set_push_function);
73 #undef MAKE_FUNCPTR
77 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
78 void *buff, size_t buff_len)
80 struct schan_transport *t = (struct schan_transport*)transport;
81 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
83 int ret = schan_pull(transport, buff, &buff_len);
84 if (ret)
86 pgnutls_transport_set_errno(s, ret);
87 return -1;
90 return buff_len;
93 static ssize_t schan_push_adapter(gnutls_transport_ptr_t transport,
94 const void *buff, size_t buff_len)
96 struct schan_transport *t = (struct schan_transport*)transport;
97 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
99 int ret = schan_push(transport, buff, &buff_len);
100 if (ret)
102 pgnutls_transport_set_errno(s, ret);
103 return -1;
106 return buff_len;
109 static const struct {
110 DWORD enable_flag;
111 const char *gnutls_flag;
112 } protocol_priority_flags[] = {
113 {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
114 {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
115 {SP_PROT_TLS1_0_CLIENT, "VERS-TLS1.0"},
116 {SP_PROT_SSL3_CLIENT, "VERS-SSL3.0"}
117 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
120 DWORD schan_imp_enabled_protocols(void)
122 /* NOTE: No support for SSL 2.0 */
123 return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
126 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
128 gnutls_session_t *s = (gnutls_session_t*)session;
129 char priority[64] = "NORMAL", *p;
130 unsigned i;
132 int err = pgnutls_init(s, cred->credential_use == SECPKG_CRED_INBOUND ? GNUTLS_SERVER : GNUTLS_CLIENT);
133 if (err != GNUTLS_E_SUCCESS)
135 pgnutls_perror(err);
136 return FALSE;
139 p = priority + strlen(priority);
140 for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) {
141 *p++ = ':';
142 *p++ = (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) ? '+' : '-';
143 strcpy(p, protocol_priority_flags[i].gnutls_flag);
144 p += strlen(p);
147 TRACE("Using %s priority\n", debugstr_a(priority));
148 err = pgnutls_priority_set_direct(*s, priority, NULL);
149 if (err != GNUTLS_E_SUCCESS)
151 pgnutls_perror(err);
152 pgnutls_deinit(*s);
153 return FALSE;
156 err = pgnutls_credentials_set(*s, GNUTLS_CRD_CERTIFICATE,
157 (gnutls_certificate_credentials_t)cred->credentials);
158 if (err != GNUTLS_E_SUCCESS)
160 pgnutls_perror(err);
161 pgnutls_deinit(*s);
162 return FALSE;
165 pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
166 pgnutls_transport_set_push_function(*s, schan_push_adapter);
168 return TRUE;
171 void schan_imp_dispose_session(schan_imp_session session)
173 gnutls_session_t s = (gnutls_session_t)session;
174 pgnutls_deinit(s);
177 void schan_imp_set_session_transport(schan_imp_session session,
178 struct schan_transport *t)
180 gnutls_session_t s = (gnutls_session_t)session;
181 pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)t);
184 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
186 gnutls_session_t s = (gnutls_session_t)session;
187 int err = pgnutls_handshake(s);
188 switch(err)
190 case GNUTLS_E_SUCCESS:
191 TRACE("Handshake completed\n");
192 return SEC_E_OK;
194 case GNUTLS_E_AGAIN:
195 TRACE("Continue...\n");
196 return SEC_I_CONTINUE_NEEDED;
198 case GNUTLS_E_WARNING_ALERT_RECEIVED:
199 case GNUTLS_E_FATAL_ALERT_RECEIVED:
201 gnutls_alert_description_t alert = pgnutls_alert_get(s);
202 const char *alert_name = pgnutls_alert_get_name(alert);
203 WARN("ALERT: %d %s\n", alert, alert_name);
204 return SEC_E_INTERNAL_ERROR;
207 default:
208 pgnutls_perror(err);
209 return SEC_E_INTERNAL_ERROR;
212 /* Never reached */
213 return SEC_E_OK;
216 static unsigned int schannel_get_cipher_block_size(gnutls_cipher_algorithm_t cipher)
218 const struct
220 gnutls_cipher_algorithm_t cipher;
221 unsigned int block_size;
223 algorithms[] =
225 {GNUTLS_CIPHER_3DES_CBC, 8},
226 {GNUTLS_CIPHER_AES_128_CBC, 16},
227 {GNUTLS_CIPHER_AES_256_CBC, 16},
228 {GNUTLS_CIPHER_ARCFOUR_128, 1},
229 {GNUTLS_CIPHER_ARCFOUR_40, 1},
230 {GNUTLS_CIPHER_DES_CBC, 8},
231 {GNUTLS_CIPHER_NULL, 1},
232 {GNUTLS_CIPHER_RC2_40_CBC, 8},
234 unsigned int i;
236 for (i = 0; i < sizeof(algorithms) / sizeof(*algorithms); ++i)
238 if (algorithms[i].cipher == cipher)
239 return algorithms[i].block_size;
242 FIXME("Unknown cipher %#x, returning 1\n", cipher);
244 return 1;
247 static DWORD schannel_get_protocol(gnutls_protocol_t proto)
249 /* FIXME: currently schannel only implements client connections, but
250 * there's no reason it couldn't be used for servers as well. The
251 * context doesn't tell us which it is, so assume client for now.
253 switch (proto)
255 case GNUTLS_SSL3: return SP_PROT_SSL3_CLIENT;
256 case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
257 case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
258 case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
259 default:
260 FIXME("unknown protocol %d\n", proto);
261 return 0;
265 static ALG_ID schannel_get_cipher_algid(gnutls_cipher_algorithm_t cipher)
267 switch (cipher)
269 case GNUTLS_CIPHER_UNKNOWN:
270 case GNUTLS_CIPHER_NULL: return 0;
271 case GNUTLS_CIPHER_ARCFOUR_40:
272 case GNUTLS_CIPHER_ARCFOUR_128: return CALG_RC4;
273 case GNUTLS_CIPHER_DES_CBC:
274 case GNUTLS_CIPHER_3DES_CBC: return CALG_DES;
275 case GNUTLS_CIPHER_AES_128_CBC:
276 case GNUTLS_CIPHER_AES_256_CBC: return CALG_AES;
277 case GNUTLS_CIPHER_RC2_40_CBC: return CALG_RC2;
278 default:
279 FIXME("unknown algorithm %d\n", cipher);
280 return 0;
284 static ALG_ID schannel_get_mac_algid(gnutls_mac_algorithm_t mac)
286 switch (mac)
288 case GNUTLS_MAC_UNKNOWN:
289 case GNUTLS_MAC_NULL: return 0;
290 case GNUTLS_MAC_MD5: return CALG_MD5;
291 case GNUTLS_MAC_SHA1:
292 case GNUTLS_MAC_SHA256:
293 case GNUTLS_MAC_SHA384:
294 case GNUTLS_MAC_SHA512: return CALG_SHA;
295 default:
296 FIXME("unknown algorithm %d\n", mac);
297 return 0;
301 static ALG_ID schannel_get_kx_algid(gnutls_kx_algorithm_t kx)
303 switch (kx)
305 case GNUTLS_KX_RSA: return CALG_RSA_KEYX;
306 case GNUTLS_KX_DHE_DSS:
307 case GNUTLS_KX_DHE_RSA: return CALG_DH_EPHEM;
308 default:
309 FIXME("unknown algorithm %d\n", kx);
310 return 0;
314 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
316 gnutls_session_t s = (gnutls_session_t)session;
317 gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get(s);
318 return schannel_get_cipher_block_size(cipher);
321 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
323 return pgnutls_record_get_max_size((gnutls_session_t)session);
326 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
327 SecPkgContext_ConnectionInfo *info)
329 gnutls_session_t s = (gnutls_session_t)session;
330 gnutls_protocol_t proto = pgnutls_protocol_get_version(s);
331 gnutls_cipher_algorithm_t alg = pgnutls_cipher_get(s);
332 gnutls_mac_algorithm_t mac = pgnutls_mac_get(s);
333 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
335 info->dwProtocol = schannel_get_protocol(proto);
336 info->aiCipher = schannel_get_cipher_algid(alg);
337 info->dwCipherStrength = pgnutls_cipher_get_key_size(alg) * 8;
338 info->aiHash = schannel_get_mac_algid(mac);
339 info->dwHashStrength = pgnutls_mac_get_key_size(mac) * 8;
340 info->aiExch = schannel_get_kx_algid(kx);
341 /* FIXME: info->dwExchStrength? */
342 info->dwExchStrength = 0;
343 return SEC_E_OK;
346 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
347 PCCERT_CONTEXT *ret)
349 gnutls_session_t s = (gnutls_session_t)session;
350 PCCERT_CONTEXT cert = NULL;
351 const gnutls_datum_t *datum;
352 unsigned list_size, i;
353 BOOL res;
355 datum = pgnutls_certificate_get_peers(s, &list_size);
356 if(!datum)
357 return SEC_E_INTERNAL_ERROR;
359 for(i = 0; i < list_size; i++) {
360 res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, datum[i].data, datum[i].size,
361 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert);
362 if(!res) {
363 if(i)
364 CertFreeCertificateContext(cert);
365 return GetLastError();
369 *ret = cert;
370 return SEC_E_OK;
373 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
374 SIZE_T *length)
376 gnutls_session_t s = (gnutls_session_t)session;
377 ssize_t ret;
379 again:
380 ret = pgnutls_record_send(s, buffer, *length);
382 if (ret >= 0)
383 *length = ret;
384 else if (ret == GNUTLS_E_AGAIN)
386 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
387 SIZE_T count = 0;
389 if (schan_get_buffer(t, &t->out, &count))
390 goto again;
392 return SEC_I_CONTINUE_NEEDED;
394 else
396 pgnutls_perror(ret);
397 return SEC_E_INTERNAL_ERROR;
400 return SEC_E_OK;
403 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
404 SIZE_T *length)
406 gnutls_session_t s = (gnutls_session_t)session;
407 ssize_t ret;
409 again:
410 ret = pgnutls_record_recv(s, buffer, *length);
412 if (ret >= 0)
413 *length = ret;
414 else if (ret == GNUTLS_E_AGAIN)
416 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
417 SIZE_T count = 0;
419 if (schan_get_buffer(t, &t->in, &count))
420 goto again;
422 return SEC_I_CONTINUE_NEEDED;
424 else
426 pgnutls_perror(ret);
427 return SEC_E_INTERNAL_ERROR;
430 return SEC_E_OK;
433 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
435 int ret = pgnutls_certificate_allocate_credentials((gnutls_certificate_credentials*)&c->credentials);
436 if (ret != GNUTLS_E_SUCCESS)
437 pgnutls_perror(ret);
438 return (ret == GNUTLS_E_SUCCESS);
441 void schan_imp_free_certificate_credentials(schan_credentials *c)
443 pgnutls_certificate_free_credentials(c->credentials);
446 static void schan_gnutls_log(int level, const char *msg)
448 TRACE("<%d> %s", level, msg);
451 BOOL schan_imp_init(void)
453 int ret;
455 libgnutls_handle = wine_dlopen(SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0);
456 if (!libgnutls_handle)
458 WARN("Failed to load libgnutls.\n");
459 return FALSE;
462 #define LOAD_FUNCPTR(f) \
463 if (!(p##f = wine_dlsym(libgnutls_handle, #f, NULL, 0))) \
465 ERR("Failed to load %s\n", #f); \
466 goto fail; \
469 LOAD_FUNCPTR(gnutls_alert_get)
470 LOAD_FUNCPTR(gnutls_alert_get_name)
471 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
472 LOAD_FUNCPTR(gnutls_certificate_free_credentials)
473 LOAD_FUNCPTR(gnutls_certificate_get_peers)
474 LOAD_FUNCPTR(gnutls_cipher_get)
475 LOAD_FUNCPTR(gnutls_cipher_get_key_size)
476 LOAD_FUNCPTR(gnutls_credentials_set)
477 LOAD_FUNCPTR(gnutls_deinit)
478 LOAD_FUNCPTR(gnutls_global_deinit)
479 LOAD_FUNCPTR(gnutls_global_init)
480 LOAD_FUNCPTR(gnutls_global_set_log_function)
481 LOAD_FUNCPTR(gnutls_global_set_log_level)
482 LOAD_FUNCPTR(gnutls_handshake)
483 LOAD_FUNCPTR(gnutls_init)
484 LOAD_FUNCPTR(gnutls_kx_get)
485 LOAD_FUNCPTR(gnutls_mac_get)
486 LOAD_FUNCPTR(gnutls_mac_get_key_size)
487 LOAD_FUNCPTR(gnutls_perror)
488 LOAD_FUNCPTR(gnutls_protocol_get_version)
489 LOAD_FUNCPTR(gnutls_priority_set_direct)
490 LOAD_FUNCPTR(gnutls_record_get_max_size);
491 LOAD_FUNCPTR(gnutls_record_recv);
492 LOAD_FUNCPTR(gnutls_record_send);
493 LOAD_FUNCPTR(gnutls_transport_get_ptr)
494 LOAD_FUNCPTR(gnutls_transport_set_errno)
495 LOAD_FUNCPTR(gnutls_transport_set_ptr)
496 LOAD_FUNCPTR(gnutls_transport_set_pull_function)
497 LOAD_FUNCPTR(gnutls_transport_set_push_function)
498 #undef LOAD_FUNCPTR
500 ret = pgnutls_global_init();
501 if (ret != GNUTLS_E_SUCCESS)
503 pgnutls_perror(ret);
504 goto fail;
507 if (TRACE_ON(secur32))
509 pgnutls_global_set_log_level(4);
510 pgnutls_global_set_log_function(schan_gnutls_log);
513 return TRUE;
515 fail:
516 wine_dlclose(libgnutls_handle, NULL, 0);
517 libgnutls_handle = NULL;
518 return FALSE;
521 void schan_imp_deinit(void)
523 pgnutls_global_deinit();
524 wine_dlclose(libgnutls_handle, NULL, 0);
525 libgnutls_handle = NULL;
528 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */