reg/tests: Test import with non-standard registry file headers.
[wine.git] / dlls / secur32 / schannel_gnutls.c
blobda834820623e196398146478fdb2c73eed25b8fa
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 static int (*pgnutls_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_key_size);
56 MAKE_FUNCPTR(gnutls_credentials_set);
57 MAKE_FUNCPTR(gnutls_deinit);
58 MAKE_FUNCPTR(gnutls_global_deinit);
59 MAKE_FUNCPTR(gnutls_global_init);
60 MAKE_FUNCPTR(gnutls_global_set_log_function);
61 MAKE_FUNCPTR(gnutls_global_set_log_level);
62 MAKE_FUNCPTR(gnutls_handshake);
63 MAKE_FUNCPTR(gnutls_init);
64 MAKE_FUNCPTR(gnutls_kx_get);
65 MAKE_FUNCPTR(gnutls_mac_get);
66 MAKE_FUNCPTR(gnutls_mac_get_key_size);
67 MAKE_FUNCPTR(gnutls_perror);
68 MAKE_FUNCPTR(gnutls_protocol_get_version);
69 MAKE_FUNCPTR(gnutls_priority_set_direct);
70 MAKE_FUNCPTR(gnutls_record_get_max_size);
71 MAKE_FUNCPTR(gnutls_record_recv);
72 MAKE_FUNCPTR(gnutls_record_send);
73 MAKE_FUNCPTR(gnutls_server_name_set);
74 MAKE_FUNCPTR(gnutls_transport_get_ptr);
75 MAKE_FUNCPTR(gnutls_transport_set_errno);
76 MAKE_FUNCPTR(gnutls_transport_set_ptr);
77 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
78 MAKE_FUNCPTR(gnutls_transport_set_push_function);
79 #undef MAKE_FUNCPTR
81 #if GNUTLS_VERSION_MAJOR < 3
82 #define GNUTLS_CIPHER_AES_192_CBC 92
83 #define GNUTLS_CIPHER_AES_128_GCM 93
84 #define GNUTLS_CIPHER_AES_256_GCM 94
86 #define GNUTLS_KX_ANON_ECDH 11
87 #define GNUTLS_KX_ECDHE_RSA 12
88 #define GNUTLS_KX_ECDHE_ECDSA 13
89 #define GNUTLS_KX_ECDHE_PSK 14
90 #endif
92 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
94 switch(cipher) {
95 case GNUTLS_CIPHER_3DES_CBC:
96 return 8;
97 case GNUTLS_CIPHER_AES_128_CBC:
98 case GNUTLS_CIPHER_AES_256_CBC:
99 return 16;
100 case GNUTLS_CIPHER_ARCFOUR_128:
101 case GNUTLS_CIPHER_ARCFOUR_40:
102 return 1;
103 case GNUTLS_CIPHER_DES_CBC:
104 return 8;
105 case GNUTLS_CIPHER_NULL:
106 return 1;
107 case GNUTLS_CIPHER_RC2_40_CBC:
108 return 8;
109 default:
110 FIXME("Unknown cipher %#x, returning 1\n", cipher);
111 return 1;
115 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
116 void *buff, size_t buff_len)
118 struct schan_transport *t = (struct schan_transport*)transport;
119 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
121 int ret = schan_pull(transport, buff, &buff_len);
122 if (ret)
124 pgnutls_transport_set_errno(s, ret);
125 return -1;
128 return buff_len;
131 static ssize_t schan_push_adapter(gnutls_transport_ptr_t transport,
132 const void *buff, size_t buff_len)
134 struct schan_transport *t = (struct schan_transport*)transport;
135 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
137 int ret = schan_push(transport, buff, &buff_len);
138 if (ret)
140 pgnutls_transport_set_errno(s, ret);
141 return -1;
144 return buff_len;
147 static const struct {
148 DWORD enable_flag;
149 const char *gnutls_flag;
150 } protocol_priority_flags[] = {
151 {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
152 {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
153 {SP_PROT_TLS1_0_CLIENT, "VERS-TLS1.0"},
154 {SP_PROT_SSL3_CLIENT, "VERS-SSL3.0"}
155 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
158 DWORD schan_imp_enabled_protocols(void)
160 /* NOTE: No support for SSL 2.0 */
161 return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
164 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
166 gnutls_session_t *s = (gnutls_session_t*)session;
167 char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p;
168 unsigned i;
170 int err = pgnutls_init(s, cred->credential_use == SECPKG_CRED_INBOUND ? GNUTLS_SERVER : GNUTLS_CLIENT);
171 if (err != GNUTLS_E_SUCCESS)
173 pgnutls_perror(err);
174 return FALSE;
177 p = priority + strlen(priority);
178 for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) {
179 *p++ = ':';
180 *p++ = (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) ? '+' : '-';
181 strcpy(p, protocol_priority_flags[i].gnutls_flag);
182 p += strlen(p);
185 TRACE("Using %s priority\n", debugstr_a(priority));
186 err = pgnutls_priority_set_direct(*s, priority, NULL);
187 if (err != GNUTLS_E_SUCCESS)
189 pgnutls_perror(err);
190 pgnutls_deinit(*s);
191 return FALSE;
194 err = pgnutls_credentials_set(*s, GNUTLS_CRD_CERTIFICATE,
195 (gnutls_certificate_credentials_t)cred->credentials);
196 if (err != GNUTLS_E_SUCCESS)
198 pgnutls_perror(err);
199 pgnutls_deinit(*s);
200 return FALSE;
203 pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
204 pgnutls_transport_set_push_function(*s, schan_push_adapter);
206 return TRUE;
209 void schan_imp_dispose_session(schan_imp_session session)
211 gnutls_session_t s = (gnutls_session_t)session;
212 pgnutls_deinit(s);
215 void schan_imp_set_session_transport(schan_imp_session session,
216 struct schan_transport *t)
218 gnutls_session_t s = (gnutls_session_t)session;
219 pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)t);
222 void schan_imp_set_session_target(schan_imp_session session, const char *target)
224 gnutls_session_t s = (gnutls_session_t)session;
226 pgnutls_server_name_set( s, GNUTLS_NAME_DNS, target, strlen(target) );
229 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
231 gnutls_session_t s = (gnutls_session_t)session;
232 int err;
234 while(1) {
235 err = pgnutls_handshake(s);
236 switch(err) {
237 case GNUTLS_E_SUCCESS:
238 TRACE("Handshake completed\n");
239 return SEC_E_OK;
241 case GNUTLS_E_AGAIN:
242 TRACE("Continue...\n");
243 return SEC_I_CONTINUE_NEEDED;
245 case GNUTLS_E_WARNING_ALERT_RECEIVED:
247 gnutls_alert_description_t alert = pgnutls_alert_get(s);
249 WARN("WARNING ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
251 switch(alert) {
252 case GNUTLS_A_UNRECOGNIZED_NAME:
253 TRACE("Ignoring\n");
254 continue;
255 default:
256 return SEC_E_INTERNAL_ERROR;
260 case GNUTLS_E_FATAL_ALERT_RECEIVED:
262 gnutls_alert_description_t alert = pgnutls_alert_get(s);
263 WARN("FATAL ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
264 return SEC_E_INTERNAL_ERROR;
267 default:
268 pgnutls_perror(err);
269 return SEC_E_INTERNAL_ERROR;
273 /* Never reached */
274 return SEC_E_OK;
277 static DWORD schannel_get_protocol(gnutls_protocol_t proto)
279 /* FIXME: currently schannel only implements client connections, but
280 * there's no reason it couldn't be used for servers as well. The
281 * context doesn't tell us which it is, so assume client for now.
283 switch (proto)
285 case GNUTLS_SSL3: return SP_PROT_SSL3_CLIENT;
286 case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
287 case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
288 case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
289 default:
290 FIXME("unknown protocol %d\n", proto);
291 return 0;
295 static ALG_ID schannel_get_cipher_algid(int cipher)
297 switch (cipher)
299 case GNUTLS_CIPHER_UNKNOWN:
300 case GNUTLS_CIPHER_NULL: return 0;
301 case GNUTLS_CIPHER_ARCFOUR_40:
302 case GNUTLS_CIPHER_ARCFOUR_128: return CALG_RC4;
303 case GNUTLS_CIPHER_DES_CBC:
304 case GNUTLS_CIPHER_3DES_CBC: return CALG_DES;
305 case GNUTLS_CIPHER_AES_128_CBC:
306 case GNUTLS_CIPHER_AES_128_GCM: return CALG_AES_128;
307 case GNUTLS_CIPHER_AES_192_CBC: return CALG_AES_192;
308 case GNUTLS_CIPHER_AES_256_GCM:
309 case GNUTLS_CIPHER_AES_256_CBC: return CALG_AES_256;
310 case GNUTLS_CIPHER_RC2_40_CBC: return CALG_RC2;
311 default:
312 FIXME("unknown algorithm %d\n", cipher);
313 return 0;
317 static ALG_ID schannel_get_mac_algid(gnutls_mac_algorithm_t mac)
319 switch (mac)
321 case GNUTLS_MAC_UNKNOWN:
322 case GNUTLS_MAC_NULL: return 0;
323 case GNUTLS_MAC_MD2: return CALG_MD2;
324 case GNUTLS_MAC_MD5: return CALG_MD5;
325 case GNUTLS_MAC_SHA1: return CALG_SHA1;
326 case GNUTLS_MAC_SHA256: return CALG_SHA_256;
327 case GNUTLS_MAC_SHA384: return CALG_SHA_384;
328 case GNUTLS_MAC_SHA512: return CALG_SHA_512;
329 default:
330 FIXME("unknown algorithm %d\n", mac);
331 return 0;
335 static ALG_ID schannel_get_kx_algid(int kx)
337 switch (kx)
339 case GNUTLS_KX_UNKNOWN: return 0;
340 case GNUTLS_KX_RSA:
341 case GNUTLS_KX_RSA_EXPORT: return CALG_RSA_KEYX;
342 case GNUTLS_KX_DHE_PSK:
343 case GNUTLS_KX_DHE_DSS:
344 case GNUTLS_KX_DHE_RSA: return CALG_DH_EPHEM;
345 case GNUTLS_KX_ANON_ECDH: return CALG_ECDH;
346 /* MSDN mentions CALG_ECDH_EPHEM, but doesn't appear in the Windows SDK. */
347 case GNUTLS_KX_ECDHE_RSA:
348 case GNUTLS_KX_ECDHE_PSK: return CALG_ECDH;
349 case GNUTLS_KX_ECDHE_ECDSA: return CALG_ECDSA;
350 default:
351 FIXME("unknown algorithm %d\n", kx);
352 return 0;
356 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
358 gnutls_session_t s = (gnutls_session_t)session;
359 return pgnutls_cipher_get_block_size(pgnutls_cipher_get(s));
362 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
364 return pgnutls_record_get_max_size((gnutls_session_t)session);
367 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
368 SecPkgContext_ConnectionInfo *info)
370 gnutls_session_t s = (gnutls_session_t)session;
371 gnutls_protocol_t proto = pgnutls_protocol_get_version(s);
372 gnutls_cipher_algorithm_t alg = pgnutls_cipher_get(s);
373 gnutls_mac_algorithm_t mac = pgnutls_mac_get(s);
374 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
376 info->dwProtocol = schannel_get_protocol(proto);
377 info->aiCipher = schannel_get_cipher_algid(alg);
378 info->dwCipherStrength = pgnutls_cipher_get_key_size(alg) * 8;
379 info->aiHash = schannel_get_mac_algid(mac);
380 info->dwHashStrength = pgnutls_mac_get_key_size(mac) * 8;
381 info->aiExch = schannel_get_kx_algid(kx);
382 /* FIXME: info->dwExchStrength? */
383 info->dwExchStrength = 0;
384 return SEC_E_OK;
387 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
388 PCCERT_CONTEXT *ret)
390 gnutls_session_t s = (gnutls_session_t)session;
391 PCCERT_CONTEXT cert = NULL;
392 const gnutls_datum_t *datum;
393 unsigned list_size, i;
394 BOOL res;
396 datum = pgnutls_certificate_get_peers(s, &list_size);
397 if(!datum)
398 return SEC_E_INTERNAL_ERROR;
400 for(i = 0; i < list_size; i++) {
401 res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, datum[i].data, datum[i].size,
402 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert);
403 if(!res) {
404 if(i)
405 CertFreeCertificateContext(cert);
406 return GetLastError();
410 *ret = cert;
411 return SEC_E_OK;
414 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
415 SIZE_T *length)
417 gnutls_session_t s = (gnutls_session_t)session;
418 SSIZE_T ret, total = 0;
420 for (;;)
422 ret = pgnutls_record_send(s, (const char *)buffer + total, *length - total);
423 if (ret >= 0)
425 total += ret;
426 TRACE( "sent %ld now %ld/%ld\n", ret, total, *length );
427 if (total == *length) return SEC_E_OK;
429 else if (ret == GNUTLS_E_AGAIN)
431 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
432 SIZE_T count = 0;
434 if (schan_get_buffer(t, &t->out, &count)) continue;
435 return SEC_I_CONTINUE_NEEDED;
437 else
439 pgnutls_perror(ret);
440 return SEC_E_INTERNAL_ERROR;
445 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
446 SIZE_T *length)
448 gnutls_session_t s = (gnutls_session_t)session;
449 ssize_t ret;
451 again:
452 ret = pgnutls_record_recv(s, buffer, *length);
454 if (ret >= 0)
455 *length = ret;
456 else if (ret == GNUTLS_E_AGAIN)
458 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
459 SIZE_T count = 0;
461 if (schan_get_buffer(t, &t->in, &count))
462 goto again;
464 return SEC_I_CONTINUE_NEEDED;
466 else
468 pgnutls_perror(ret);
469 return SEC_E_INTERNAL_ERROR;
472 return SEC_E_OK;
475 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
477 int ret = pgnutls_certificate_allocate_credentials((gnutls_certificate_credentials_t*)&c->credentials);
478 if (ret != GNUTLS_E_SUCCESS)
479 pgnutls_perror(ret);
480 return (ret == GNUTLS_E_SUCCESS);
483 void schan_imp_free_certificate_credentials(schan_credentials *c)
485 pgnutls_certificate_free_credentials(c->credentials);
488 static void schan_gnutls_log(int level, const char *msg)
490 TRACE("<%d> %s", level, msg);
493 BOOL schan_imp_init(void)
495 int ret;
497 libgnutls_handle = wine_dlopen(SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0);
498 if (!libgnutls_handle)
500 ERR_(winediag)("Failed to load libgnutls, secure connections will not be available.\n");
501 return FALSE;
504 #define LOAD_FUNCPTR(f) \
505 if (!(p##f = wine_dlsym(libgnutls_handle, #f, NULL, 0))) \
507 ERR("Failed to load %s\n", #f); \
508 goto fail; \
511 LOAD_FUNCPTR(gnutls_alert_get)
512 LOAD_FUNCPTR(gnutls_alert_get_name)
513 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
514 LOAD_FUNCPTR(gnutls_certificate_free_credentials)
515 LOAD_FUNCPTR(gnutls_certificate_get_peers)
516 LOAD_FUNCPTR(gnutls_cipher_get)
517 LOAD_FUNCPTR(gnutls_cipher_get_key_size)
518 LOAD_FUNCPTR(gnutls_credentials_set)
519 LOAD_FUNCPTR(gnutls_deinit)
520 LOAD_FUNCPTR(gnutls_global_deinit)
521 LOAD_FUNCPTR(gnutls_global_init)
522 LOAD_FUNCPTR(gnutls_global_set_log_function)
523 LOAD_FUNCPTR(gnutls_global_set_log_level)
524 LOAD_FUNCPTR(gnutls_handshake)
525 LOAD_FUNCPTR(gnutls_init)
526 LOAD_FUNCPTR(gnutls_kx_get)
527 LOAD_FUNCPTR(gnutls_mac_get)
528 LOAD_FUNCPTR(gnutls_mac_get_key_size)
529 LOAD_FUNCPTR(gnutls_perror)
530 LOAD_FUNCPTR(gnutls_protocol_get_version)
531 LOAD_FUNCPTR(gnutls_priority_set_direct)
532 LOAD_FUNCPTR(gnutls_record_get_max_size);
533 LOAD_FUNCPTR(gnutls_record_recv);
534 LOAD_FUNCPTR(gnutls_record_send);
535 LOAD_FUNCPTR(gnutls_server_name_set)
536 LOAD_FUNCPTR(gnutls_transport_get_ptr)
537 LOAD_FUNCPTR(gnutls_transport_set_errno)
538 LOAD_FUNCPTR(gnutls_transport_set_ptr)
539 LOAD_FUNCPTR(gnutls_transport_set_pull_function)
540 LOAD_FUNCPTR(gnutls_transport_set_push_function)
541 #undef LOAD_FUNCPTR
543 if (!(pgnutls_cipher_get_block_size = wine_dlsym(libgnutls_handle, "gnutls_cipher_get_block_size", NULL, 0)))
545 WARN("gnutls_cipher_get_block_size not found\n");
546 pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
549 ret = pgnutls_global_init();
550 if (ret != GNUTLS_E_SUCCESS)
552 pgnutls_perror(ret);
553 goto fail;
556 if (TRACE_ON(secur32))
558 pgnutls_global_set_log_level(4);
559 pgnutls_global_set_log_function(schan_gnutls_log);
562 return TRUE;
564 fail:
565 wine_dlclose(libgnutls_handle, NULL, 0);
566 libgnutls_handle = NULL;
567 return FALSE;
570 void schan_imp_deinit(void)
572 pgnutls_global_deinit();
573 wine_dlclose(libgnutls_handle, NULL, 0);
574 libgnutls_handle = NULL;
577 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */