[SCHANNEL]
[reactos.git] / reactos / dll / win32 / schannel / schannel_gnutls.c
1 /*
2 * GnuTLS-based 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 "precomp.h"
23
24 #include <wine/config.h>
25 #include <wine/port.h>
26 #include <strsafe.h>
27
28 #ifdef SONAME_LIBGNUTLS
29 #include <gnutls/gnutls.h>
30 #include <gnutls/crypto.h>
31 #endif
32
33 #define __wine_dbch_secur32 __wine_dbch_schannel
34
35 #if defined(SONAME_LIBGNUTLS) && !defined(HAVE_SECURITY_SECURITY_H)
36
37 static void *libgnutls_handle;
38 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
39 MAKE_FUNCPTR(gnutls_alert_get);
40 MAKE_FUNCPTR(gnutls_alert_get_name);
41 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
42 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
43 MAKE_FUNCPTR(gnutls_certificate_get_peers);
44 MAKE_FUNCPTR(gnutls_cipher_get);
45 MAKE_FUNCPTR(gnutls_cipher_get_key_size);
46 MAKE_FUNCPTR(gnutls_credentials_set);
47 MAKE_FUNCPTR(gnutls_deinit);
48 MAKE_FUNCPTR(gnutls_global_deinit);
49 MAKE_FUNCPTR(gnutls_global_init);
50 MAKE_FUNCPTR(gnutls_global_set_log_function);
51 MAKE_FUNCPTR(gnutls_global_set_log_level);
52 MAKE_FUNCPTR(gnutls_handshake);
53 MAKE_FUNCPTR(gnutls_init);
54 MAKE_FUNCPTR(gnutls_kx_get);
55 MAKE_FUNCPTR(gnutls_mac_get);
56 MAKE_FUNCPTR(gnutls_mac_get_key_size);
57 MAKE_FUNCPTR(gnutls_perror);
58 MAKE_FUNCPTR(gnutls_protocol_get_version);
59 MAKE_FUNCPTR(gnutls_priority_set_direct);
60 MAKE_FUNCPTR(gnutls_record_get_max_size);
61 MAKE_FUNCPTR(gnutls_record_recv);
62 MAKE_FUNCPTR(gnutls_record_send);
63 MAKE_FUNCPTR(gnutls_server_name_set);
64 MAKE_FUNCPTR(gnutls_transport_get_ptr);
65 MAKE_FUNCPTR(gnutls_transport_set_errno);
66 MAKE_FUNCPTR(gnutls_transport_set_ptr);
67 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
68 MAKE_FUNCPTR(gnutls_transport_set_push_function);
69 #undef MAKE_FUNCPTR
70
71
72
73 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
74 void *buff, size_t buff_len)
75 {
76 struct schan_transport *t = (struct schan_transport*)transport;
77 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
78
79 int ret = schan_pull(transport, buff, &buff_len);
80 if (ret)
81 {
82 pgnutls_transport_set_errno(s, ret);
83 return -1;
84 }
85
86 return buff_len;
87 }
88
89 static ssize_t schan_push_adapter(gnutls_transport_ptr_t transport,
90 const void *buff, size_t buff_len)
91 {
92 struct schan_transport *t = (struct schan_transport*)transport;
93 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
94
95 int ret = schan_push(transport, buff, &buff_len);
96 if (ret)
97 {
98 pgnutls_transport_set_errno(s, ret);
99 return -1;
100 }
101
102 return buff_len;
103 }
104
105 static const struct {
106 DWORD enable_flag;
107 const char *gnutls_flag;
108 } protocol_priority_flags[] = {
109 {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
110 {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
111 {SP_PROT_TLS1_0_CLIENT, "VERS-TLS1.0"},
112 {SP_PROT_SSL3_CLIENT, "VERS-SSL3.0"}
113 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
114 };
115
116 DWORD schan_imp_enabled_protocols(void)
117 {
118 /* NOTE: No support for SSL 2.0 */
119 return SP_PROT_SSL3_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_2_CLIENT;
120 }
121
122 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
123 {
124 gnutls_session_t *s = (gnutls_session_t*)session;
125 char priority[64] = "NORMAL", *p;
126 unsigned i;
127
128 int err = pgnutls_init(s, cred->credential_use == SECPKG_CRED_INBOUND ? GNUTLS_SERVER : GNUTLS_CLIENT);
129 if (err != GNUTLS_E_SUCCESS)
130 {
131 pgnutls_perror(err);
132 return FALSE;
133 }
134
135 p = priority + strlen(priority);
136 for(i=0; i < sizeof(protocol_priority_flags)/sizeof(*protocol_priority_flags); i++) {
137 *p++ = ':';
138 *p++ = (cred->enabled_protocols & protocol_priority_flags[i].enable_flag) ? '+' : '-';
139 strcpy(p, protocol_priority_flags[i].gnutls_flag);
140 p += strlen(p);
141 }
142
143 TRACE("Using %s priority\n", debugstr_a(priority));
144 err = pgnutls_priority_set_direct(*s, priority, NULL);
145 if (err != GNUTLS_E_SUCCESS)
146 {
147 pgnutls_perror(err);
148 pgnutls_deinit(*s);
149 return FALSE;
150 }
151
152 err = pgnutls_credentials_set(*s, GNUTLS_CRD_CERTIFICATE,
153 (gnutls_certificate_credentials_t)cred->credentials);
154 if (err != GNUTLS_E_SUCCESS)
155 {
156 pgnutls_perror(err);
157 pgnutls_deinit(*s);
158 return FALSE;
159 }
160
161 pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
162 pgnutls_transport_set_push_function(*s, schan_push_adapter);
163
164 return TRUE;
165 }
166
167 void schan_imp_dispose_session(schan_imp_session session)
168 {
169 gnutls_session_t s = (gnutls_session_t)session;
170 pgnutls_deinit(s);
171 }
172
173 void schan_imp_set_session_transport(schan_imp_session session,
174 struct schan_transport *t)
175 {
176 gnutls_session_t s = (gnutls_session_t)session;
177 pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)t);
178 }
179
180 void schan_imp_set_session_target(schan_imp_session session, const char *target)
181 {
182 gnutls_session_t s = (gnutls_session_t)session;
183
184 pgnutls_server_name_set( s, GNUTLS_NAME_DNS, target, strlen(target) );
185 }
186
187 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
188 {
189 gnutls_session_t s = (gnutls_session_t)session;
190 int err;
191
192 while(1) {
193 err = pgnutls_handshake(s);
194 switch(err) {
195 case GNUTLS_E_SUCCESS:
196 TRACE("Handshake completed\n");
197 return SEC_E_OK;
198
199 case GNUTLS_E_AGAIN:
200 TRACE("Continue...\n");
201 return SEC_I_CONTINUE_NEEDED;
202
203 case GNUTLS_E_WARNING_ALERT_RECEIVED:
204 {
205 gnutls_alert_description_t alert = pgnutls_alert_get(s);
206
207 WARN("WARNING ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
208
209 switch(alert) {
210 case GNUTLS_A_UNRECOGNIZED_NAME:
211 TRACE("Ignoring\n");
212 continue;
213 default:
214 return SEC_E_INTERNAL_ERROR;
215 }
216 }
217
218 case GNUTLS_E_FATAL_ALERT_RECEIVED:
219 {
220 gnutls_alert_description_t alert = pgnutls_alert_get(s);
221 WARN("FATAL ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
222 return SEC_E_INTERNAL_ERROR;
223 }
224
225 default:
226 pgnutls_perror(err);
227 return SEC_E_INTERNAL_ERROR;
228 }
229 }
230
231 /* Never reached */
232 return SEC_E_OK;
233 }
234
235 static unsigned int schannel_get_cipher_block_size(gnutls_cipher_algorithm_t cipher)
236 {
237 const struct
238 {
239 gnutls_cipher_algorithm_t cipher;
240 unsigned int block_size;
241 }
242 algorithms[] =
243 {
244 {GNUTLS_CIPHER_3DES_CBC, 8},
245 {GNUTLS_CIPHER_AES_128_CBC, 16},
246 {GNUTLS_CIPHER_AES_256_CBC, 16},
247 {GNUTLS_CIPHER_ARCFOUR_128, 1},
248 {GNUTLS_CIPHER_ARCFOUR_40, 1},
249 {GNUTLS_CIPHER_DES_CBC, 8},
250 {GNUTLS_CIPHER_NULL, 1},
251 {GNUTLS_CIPHER_RC2_40_CBC, 8},
252 };
253 unsigned int i;
254
255 for (i = 0; i < sizeof(algorithms) / sizeof(*algorithms); ++i)
256 {
257 if (algorithms[i].cipher == cipher)
258 return algorithms[i].block_size;
259 }
260
261 FIXME("Unknown cipher %#x, returning 1\n", cipher);
262
263 return 1;
264 }
265
266 static DWORD schannel_get_protocol(gnutls_protocol_t proto)
267 {
268 /* FIXME: currently schannel only implements client connections, but
269 * there's no reason it couldn't be used for servers as well. The
270 * context doesn't tell us which it is, so assume client for now.
271 */
272 switch (proto)
273 {
274 case GNUTLS_SSL3: return SP_PROT_SSL3_CLIENT;
275 case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
276 case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
277 case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
278 default:
279 FIXME("unknown protocol %d\n", proto);
280 return 0;
281 }
282 }
283
284 static ALG_ID schannel_get_cipher_algid(gnutls_cipher_algorithm_t cipher)
285 {
286 switch (cipher)
287 {
288 case GNUTLS_CIPHER_UNKNOWN:
289 case GNUTLS_CIPHER_NULL: return 0;
290 case GNUTLS_CIPHER_ARCFOUR_40:
291 case GNUTLS_CIPHER_ARCFOUR_128: return CALG_RC4;
292 case GNUTLS_CIPHER_DES_CBC:
293 case GNUTLS_CIPHER_3DES_CBC: return CALG_DES;
294 case GNUTLS_CIPHER_AES_128_CBC:
295 case GNUTLS_CIPHER_AES_256_CBC: return CALG_AES;
296 case GNUTLS_CIPHER_RC2_40_CBC: return CALG_RC2;
297 default:
298 FIXME("unknown algorithm %d\n", cipher);
299 return 0;
300 }
301 }
302
303 static ALG_ID schannel_get_mac_algid(gnutls_mac_algorithm_t mac)
304 {
305 switch (mac)
306 {
307 case GNUTLS_MAC_UNKNOWN:
308 case GNUTLS_MAC_NULL: return 0;
309 case GNUTLS_MAC_MD5: return CALG_MD5;
310 case GNUTLS_MAC_SHA1:
311 case GNUTLS_MAC_SHA256:
312 case GNUTLS_MAC_SHA384:
313 case GNUTLS_MAC_SHA512: return CALG_SHA;
314 default:
315 FIXME("unknown algorithm %d\n", mac);
316 return 0;
317 }
318 }
319
320 static ALG_ID schannel_get_kx_algid(gnutls_kx_algorithm_t kx)
321 {
322 switch (kx)
323 {
324 case GNUTLS_KX_RSA: return CALG_RSA_KEYX;
325 case GNUTLS_KX_DHE_DSS:
326 case GNUTLS_KX_DHE_RSA: return CALG_DH_EPHEM;
327 default:
328 FIXME("unknown algorithm %d\n", kx);
329 return 0;
330 }
331 }
332
333 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
334 {
335 gnutls_session_t s = (gnutls_session_t)session;
336 gnutls_cipher_algorithm_t cipher = pgnutls_cipher_get(s);
337 return schannel_get_cipher_block_size(cipher);
338 }
339
340 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
341 {
342 return pgnutls_record_get_max_size((gnutls_session_t)session);
343 }
344
345 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
346 SecPkgContext_ConnectionInfo *info)
347 {
348 gnutls_session_t s = (gnutls_session_t)session;
349 gnutls_protocol_t proto = pgnutls_protocol_get_version(s);
350 gnutls_cipher_algorithm_t alg = pgnutls_cipher_get(s);
351 gnutls_mac_algorithm_t mac = pgnutls_mac_get(s);
352 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
353
354 info->dwProtocol = schannel_get_protocol(proto);
355 info->aiCipher = schannel_get_cipher_algid(alg);
356 info->dwCipherStrength = pgnutls_cipher_get_key_size(alg) * 8;
357 info->aiHash = schannel_get_mac_algid(mac);
358 info->dwHashStrength = pgnutls_mac_get_key_size(mac) * 8;
359 info->aiExch = schannel_get_kx_algid(kx);
360 /* FIXME: info->dwExchStrength? */
361 info->dwExchStrength = 0;
362 return SEC_E_OK;
363 }
364
365 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
366 PCCERT_CONTEXT *ret)
367 {
368 gnutls_session_t s = (gnutls_session_t)session;
369 PCCERT_CONTEXT cert = NULL;
370 const gnutls_datum_t *datum;
371 unsigned list_size, i;
372 BOOL res;
373
374 datum = pgnutls_certificate_get_peers(s, &list_size);
375 if(!datum)
376 return SEC_E_INTERNAL_ERROR;
377
378 for(i = 0; i < list_size; i++) {
379 res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, datum[i].data, datum[i].size,
380 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert);
381 if(!res) {
382 if(i)
383 CertFreeCertificateContext(cert);
384 return GetLastError();
385 }
386 }
387
388 *ret = cert;
389 return SEC_E_OK;
390 }
391
392 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
393 SIZE_T *length)
394 {
395 gnutls_session_t s = (gnutls_session_t)session;
396 ssize_t ret;
397
398 again:
399 ret = pgnutls_record_send(s, buffer, *length);
400
401 if (ret >= 0)
402 *length = ret;
403 else if (ret == GNUTLS_E_AGAIN)
404 {
405 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
406 SIZE_T count = 0;
407
408 if (schan_get_buffer(t, &t->out, &count))
409 goto again;
410
411 return SEC_I_CONTINUE_NEEDED;
412 }
413 else
414 {
415 pgnutls_perror(ret);
416 return SEC_E_INTERNAL_ERROR;
417 }
418
419 return SEC_E_OK;
420 }
421
422 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
423 SIZE_T *length)
424 {
425 gnutls_session_t s = (gnutls_session_t)session;
426 ssize_t ret;
427
428 again:
429 ret = pgnutls_record_recv(s, buffer, *length);
430
431 if (ret >= 0)
432 *length = ret;
433 else if (ret == GNUTLS_E_AGAIN)
434 {
435 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
436 SIZE_T count = 0;
437
438 if (schan_get_buffer(t, &t->in, &count))
439 goto again;
440
441 return SEC_I_CONTINUE_NEEDED;
442 }
443 else
444 {
445 pgnutls_perror(ret);
446 return SEC_E_INTERNAL_ERROR;
447 }
448
449 return SEC_E_OK;
450 }
451
452 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
453 {
454 int ret = pgnutls_certificate_allocate_credentials((gnutls_certificate_credentials_t*)&c->credentials);
455 if (ret != GNUTLS_E_SUCCESS)
456 pgnutls_perror(ret);
457 return (ret == GNUTLS_E_SUCCESS);
458 }
459
460 void schan_imp_free_certificate_credentials(schan_credentials *c)
461 {
462 pgnutls_certificate_free_credentials(c->credentials);
463 }
464
465 static void schan_gnutls_log(int level, const char *msg)
466 {
467 TRACE("<%d> %s", level, msg);
468 }
469
470 BOOL schan_imp_init(void)
471 {
472 int ret;
473
474 #ifndef __REACTOS__
475 libgnutls_handle = wine_dlopen(SONAME_LIBGNUTLS, RTLD_NOW, NULL, 0);
476 if (!libgnutls_handle)
477 {
478 WARN("Failed to load libgnutls.\n");
479 return FALSE;
480 }
481
482 #define LOAD_FUNCPTR(f) \
483 if (!(p##f = wine_dlsym(libgnutls_handle, #f, NULL, 0))) \
484 { \
485 ERR("Failed to load %s\n", #f); \
486 goto fail; \
487 }
488 #else
489 /*
490 static const WCHAR RosSchannelKey[] = L"Software\\ReactOS\\Schannel";
491 static const WCHAR PathValue[] = L"GnuTLSPath";
492 WCHAR Path[MAX_PATH];
493 DWORD PathSize = sizeof(Path), ValueType;
494 HKEY Key;
495 DWORD Error;
496
497 Error = RegOpenKeyW(HKEY_LOCAL_MACHINE, RosSchannelKey, &Key);
498 if(Error != ERROR_SUCCESS)
499 return FALSE;
500
501 Error = RegQueryValueExW(Key, PathValue, NULL, &ValueType, (LPBYTE)Path, &PathSize);
502 RegCloseKey(Key);
503 if ((Error != ERROR_SUCCESS) || (ValueType != REG_SZ))
504 return FALSE;
505 wcscat(Path, L"\\");
506 wcscat(Path, SONAME_LIBGNUTLS);
507 */
508 static const WCHAR Path[] = L"C:\\Reactos\\system32\\gnutls\\libgnutls-28.dll";
509
510 libgnutls_handle = LoadLibraryExW(Path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
511 if (!libgnutls_handle)
512 {
513 ERR("Could not load %S.\n", Path);
514 return FALSE;
515 }
516
517 #define LOAD_FUNCPTR(f) \
518 if (!(p##f = (void*)GetProcAddress(libgnutls_handle, #f))) \
519 { \
520 ERR("Failed to load %s\n", #f); \
521 goto fail; \
522 }
523 #endif // __REACTOS__
524
525 LOAD_FUNCPTR(gnutls_alert_get)
526 LOAD_FUNCPTR(gnutls_alert_get_name)
527 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
528 LOAD_FUNCPTR(gnutls_certificate_free_credentials)
529 LOAD_FUNCPTR(gnutls_certificate_get_peers)
530 LOAD_FUNCPTR(gnutls_cipher_get)
531 LOAD_FUNCPTR(gnutls_cipher_get_key_size)
532 LOAD_FUNCPTR(gnutls_credentials_set)
533 LOAD_FUNCPTR(gnutls_deinit)
534 LOAD_FUNCPTR(gnutls_global_deinit)
535 LOAD_FUNCPTR(gnutls_global_init)
536 LOAD_FUNCPTR(gnutls_global_set_log_function)
537 LOAD_FUNCPTR(gnutls_global_set_log_level)
538 LOAD_FUNCPTR(gnutls_handshake)
539 LOAD_FUNCPTR(gnutls_init)
540 LOAD_FUNCPTR(gnutls_kx_get)
541 LOAD_FUNCPTR(gnutls_mac_get)
542 LOAD_FUNCPTR(gnutls_mac_get_key_size)
543 LOAD_FUNCPTR(gnutls_perror)
544 LOAD_FUNCPTR(gnutls_protocol_get_version)
545 LOAD_FUNCPTR(gnutls_priority_set_direct)
546 LOAD_FUNCPTR(gnutls_record_get_max_size);
547 LOAD_FUNCPTR(gnutls_record_recv);
548 LOAD_FUNCPTR(gnutls_record_send);
549 LOAD_FUNCPTR(gnutls_server_name_set)
550 LOAD_FUNCPTR(gnutls_transport_get_ptr)
551 LOAD_FUNCPTR(gnutls_transport_set_errno)
552 LOAD_FUNCPTR(gnutls_transport_set_ptr)
553 LOAD_FUNCPTR(gnutls_transport_set_pull_function)
554 LOAD_FUNCPTR(gnutls_transport_set_push_function)
555 #undef LOAD_FUNCPTR
556
557 ret = pgnutls_global_init();
558 if (ret != GNUTLS_E_SUCCESS)
559 {
560 pgnutls_perror(ret);
561 goto fail;
562 }
563
564 if (TRACE_ON(secur32))
565 {
566 pgnutls_global_set_log_level(4);
567 pgnutls_global_set_log_function(schan_gnutls_log);
568 }
569
570 return TRUE;
571
572 fail:
573 #ifndef __REACTOS__
574 wine_dlclose(libgnutls_handle, NULL, 0);
575 #else
576 FreeLibrary(libgnutls_handle);
577 #endif
578 libgnutls_handle = NULL;
579 return FALSE;
580 }
581
582 void schan_imp_deinit(void)
583 {
584 pgnutls_global_deinit();
585 #ifndef __REACTOS__
586 wine_dlclose(libgnutls_handle, NULL, 0);
587 #else
588 FreeLibrary(libgnutls_handle);
589 #endif
590 libgnutls_handle = NULL;
591 }
592
593 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */