winex11.drv: Map coordinates before calling send_mouse_input.
[wine/zf.git] / dlls / secur32 / schannel_gnutls.c
blob785430ddf581b16e98f254e673fdb8702a4544a2
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 #include <stdio.h>
27 #include <assert.h>
28 #ifdef SONAME_LIBGNUTLS
29 #include <gnutls/gnutls.h>
30 #include <gnutls/crypto.h>
31 #include <gnutls/abstract.h>
32 #endif
34 #include "windef.h"
35 #include "winbase.h"
36 #include "sspi.h"
37 #include "schannel.h"
38 #include "lmcons.h"
39 #include "winreg.h"
40 #include "secur32_priv.h"
42 #include "wine/debug.h"
43 #include "wine/unicode.h"
45 #if defined(SONAME_LIBGNUTLS) && !defined(HAVE_SECURITY_SECURITY_H)
47 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
48 WINE_DECLARE_DEBUG_CHANNEL(winediag);
50 /* Not present in gnutls version < 2.9.10. */
51 static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t);
53 /* Not present in gnutls version < 3.0. */
54 static void (*pgnutls_transport_set_pull_timeout_function)(gnutls_session_t,
55 int (*)(gnutls_transport_ptr_t, unsigned int));
56 static void (*pgnutls_dtls_set_mtu)(gnutls_session_t, unsigned int);
58 /* Not present in gnutls version < 3.2.0. */
59 static int (*pgnutls_alpn_get_selected_protocol)(gnutls_session_t, gnutls_datum_t *);
60 static int (*pgnutls_alpn_set_protocols)(gnutls_session_t, const gnutls_datum_t *,
61 unsigned, unsigned int);
63 /* Not present in gnutls version < 3.3.0. */
64 static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *,
65 const gnutls_datum_t *, const gnutls_datum_t *,
66 const gnutls_datum_t *, const gnutls_datum_t *,
67 const gnutls_datum_t *, const gnutls_datum_t *,
68 const gnutls_datum_t *);
70 /* Not present in gnutls version < 3.4.0. */
71 static int (*pgnutls_privkey_export_x509)(gnutls_privkey_t, gnutls_x509_privkey_t *);
73 static void *libgnutls_handle;
74 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
75 MAKE_FUNCPTR(gnutls_alert_get);
76 MAKE_FUNCPTR(gnutls_alert_get_name);
77 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
78 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
79 MAKE_FUNCPTR(gnutls_certificate_get_peers);
80 MAKE_FUNCPTR(gnutls_certificate_set_x509_key);
81 MAKE_FUNCPTR(gnutls_cipher_get);
82 MAKE_FUNCPTR(gnutls_cipher_get_key_size);
83 MAKE_FUNCPTR(gnutls_credentials_set);
84 MAKE_FUNCPTR(gnutls_deinit);
85 MAKE_FUNCPTR(gnutls_global_deinit);
86 MAKE_FUNCPTR(gnutls_global_init);
87 MAKE_FUNCPTR(gnutls_global_set_log_function);
88 MAKE_FUNCPTR(gnutls_global_set_log_level);
89 MAKE_FUNCPTR(gnutls_handshake);
90 MAKE_FUNCPTR(gnutls_init);
91 MAKE_FUNCPTR(gnutls_kx_get);
92 MAKE_FUNCPTR(gnutls_mac_get);
93 MAKE_FUNCPTR(gnutls_mac_get_key_size);
94 MAKE_FUNCPTR(gnutls_perror);
95 MAKE_FUNCPTR(gnutls_protocol_get_version);
96 MAKE_FUNCPTR(gnutls_priority_set_direct);
97 MAKE_FUNCPTR(gnutls_privkey_deinit);
98 MAKE_FUNCPTR(gnutls_privkey_init);
99 MAKE_FUNCPTR(gnutls_record_get_max_size);
100 MAKE_FUNCPTR(gnutls_record_recv);
101 MAKE_FUNCPTR(gnutls_record_send);
102 MAKE_FUNCPTR(gnutls_server_name_set);
103 MAKE_FUNCPTR(gnutls_session_channel_binding);
104 MAKE_FUNCPTR(gnutls_transport_get_ptr);
105 MAKE_FUNCPTR(gnutls_transport_set_errno);
106 MAKE_FUNCPTR(gnutls_transport_set_ptr);
107 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
108 MAKE_FUNCPTR(gnutls_transport_set_push_function);
109 MAKE_FUNCPTR(gnutls_x509_crt_deinit);
110 MAKE_FUNCPTR(gnutls_x509_crt_import);
111 MAKE_FUNCPTR(gnutls_x509_crt_init);
112 MAKE_FUNCPTR(gnutls_x509_privkey_deinit);
113 #undef MAKE_FUNCPTR
115 #if GNUTLS_VERSION_MAJOR < 3
116 #define GNUTLS_CIPHER_AES_192_CBC 92
117 #define GNUTLS_CIPHER_AES_128_GCM 93
118 #define GNUTLS_CIPHER_AES_256_GCM 94
120 #define GNUTLS_MAC_AEAD 200
122 #define GNUTLS_KX_ANON_ECDH 11
123 #define GNUTLS_KX_ECDHE_RSA 12
124 #define GNUTLS_KX_ECDHE_ECDSA 13
125 #define GNUTLS_KX_ECDHE_PSK 14
126 #endif
128 #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 5)
129 #define GNUTLS_ALPN_SERVER_PRECEDENCE (1<<1)
130 #endif
132 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
134 switch(cipher) {
135 case GNUTLS_CIPHER_3DES_CBC:
136 return 8;
137 case GNUTLS_CIPHER_AES_128_CBC:
138 case GNUTLS_CIPHER_AES_256_CBC:
139 return 16;
140 case GNUTLS_CIPHER_ARCFOUR_128:
141 case GNUTLS_CIPHER_ARCFOUR_40:
142 return 1;
143 case GNUTLS_CIPHER_DES_CBC:
144 return 8;
145 case GNUTLS_CIPHER_NULL:
146 return 1;
147 case GNUTLS_CIPHER_RC2_40_CBC:
148 return 8;
149 default:
150 FIXME("Unknown cipher %#x, returning 1\n", cipher);
151 return 1;
155 static void compat_gnutls_transport_set_pull_timeout_function(gnutls_session_t session,
156 int (*func)(gnutls_transport_ptr_t, unsigned int))
158 FIXME("\n");
161 static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey, gnutls_x509_privkey_t *key)
163 FIXME("\n");
164 return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
167 static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key, const gnutls_datum_t *p1,
168 const gnutls_datum_t *p2, const gnutls_datum_t *p3,
169 const gnutls_datum_t *p4, const gnutls_datum_t *p5,
170 const gnutls_datum_t *p6, const gnutls_datum_t *p7,
171 const gnutls_datum_t *p8)
173 FIXME("\n");
174 return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
177 static int compat_gnutls_alpn_get_selected_protocol(gnutls_session_t session, gnutls_datum_t *protocol)
179 FIXME("\n");
180 return GNUTLS_E_INVALID_REQUEST;
183 static int compat_gnutls_alpn_set_protocols(gnutls_session_t session, const gnutls_datum_t *protocols,
184 unsigned size, unsigned int flags)
186 FIXME("\n");
187 return GNUTLS_E_INVALID_REQUEST;
190 static void compat_gnutls_dtls_set_mtu(gnutls_session_t session, unsigned int mtu)
192 FIXME("\n");
195 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
196 void *buff, size_t buff_len)
198 struct schan_transport *t = (struct schan_transport*)transport;
199 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
201 int ret = schan_pull(transport, buff, &buff_len);
202 if (ret)
204 pgnutls_transport_set_errno(s, ret);
205 return -1;
208 return buff_len;
211 static ssize_t schan_push_adapter(gnutls_transport_ptr_t transport,
212 const void *buff, size_t buff_len)
214 struct schan_transport *t = (struct schan_transport*)transport;
215 gnutls_session_t s = (gnutls_session_t)schan_session_for_transport(t);
217 int ret = schan_push(transport, buff, &buff_len);
218 if (ret)
220 pgnutls_transport_set_errno(s, ret);
221 return -1;
224 return buff_len;
227 static const struct {
228 DWORD enable_flag;
229 const char *gnutls_flag;
230 } protocol_priority_flags[] = {
231 {SP_PROT_DTLS1_2_CLIENT, "VERS-DTLS1.2"},
232 {SP_PROT_DTLS1_0_CLIENT, "VERS-DTLS1.0"},
233 {SP_PROT_TLS1_3_CLIENT, "VERS-TLS1.3"},
234 {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
235 {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
236 {SP_PROT_TLS1_0_CLIENT, "VERS-TLS1.0"},
237 {SP_PROT_SSL3_CLIENT, "VERS-SSL3.0"}
238 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
241 static DWORD supported_protocols;
243 static void check_supported_protocols(void)
245 gnutls_session_t session;
246 char priority[64];
247 unsigned i;
248 int err;
250 err = pgnutls_init(&session, GNUTLS_CLIENT);
251 if (err != GNUTLS_E_SUCCESS)
253 pgnutls_perror(err);
254 return;
257 for(i = 0; i < ARRAY_SIZE(protocol_priority_flags); i++)
259 sprintf(priority, "NORMAL:-%s", protocol_priority_flags[i].gnutls_flag);
260 err = pgnutls_priority_set_direct(session, priority, NULL);
261 if (err == GNUTLS_E_SUCCESS)
263 TRACE("%s is supported\n", protocol_priority_flags[i].gnutls_flag);
264 supported_protocols |= protocol_priority_flags[i].enable_flag;
266 else
267 TRACE("%s is not supported\n", protocol_priority_flags[i].gnutls_flag);
270 pgnutls_deinit(session);
273 DWORD schan_imp_enabled_protocols(void)
275 return supported_protocols;
278 static int schan_pull_timeout(gnutls_transport_ptr_t transport, unsigned int timeout)
280 struct schan_transport *t = (struct schan_transport *)transport;
281 SIZE_T count = 0;
283 if (schan_get_buffer(t, &t->in, &count)) return 1;
284 return 0;
287 BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
289 gnutls_session_t *s = (gnutls_session_t*)session;
290 char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p;
291 BOOL using_vers_all = FALSE, disabled;
292 unsigned int i, flags = (cred->credential_use == SECPKG_CRED_INBOUND) ? GNUTLS_SERVER : GNUTLS_CLIENT;
293 int err;
295 if (cred->enabled_protocols & (SP_PROT_DTLS1_0_CLIENT | SP_PROT_DTLS1_2_CLIENT))
297 flags |= GNUTLS_DATAGRAM | GNUTLS_NONBLOCK;
300 err = pgnutls_init(s, flags);
301 if (err != GNUTLS_E_SUCCESS)
303 pgnutls_perror(err);
304 return FALSE;
307 p = priority + strlen(priority);
309 /* VERS-ALL is nice to use for forward compatibility. It was introduced before support for TLS1.3,
310 * so if TLS1.3 is supported, we may safely use it. Otherwise explicitly disable all known
311 * disabled protocols. */
312 if (supported_protocols & SP_PROT_TLS1_3_CLIENT)
314 strcpy(p, ":-VERS-ALL");
315 p += strlen(p);
316 using_vers_all = TRUE;
319 for (i = 0; i < ARRAY_SIZE(protocol_priority_flags); i++)
321 if (!(supported_protocols & protocol_priority_flags[i].enable_flag)) continue;
323 disabled = !(cred->enabled_protocols & protocol_priority_flags[i].enable_flag);
324 if (using_vers_all && disabled) continue;
326 *p++ = ':';
327 *p++ = disabled ? '-' : '+';
328 strcpy(p, protocol_priority_flags[i].gnutls_flag);
329 p += strlen(p);
332 TRACE("Using %s priority\n", debugstr_a(priority));
333 err = pgnutls_priority_set_direct(*s, priority, NULL);
334 if (err != GNUTLS_E_SUCCESS)
336 pgnutls_perror(err);
337 pgnutls_deinit(*s);
338 return FALSE;
341 err = pgnutls_credentials_set(*s, GNUTLS_CRD_CERTIFICATE,
342 (gnutls_certificate_credentials_t)cred->credentials);
343 if (err != GNUTLS_E_SUCCESS)
345 pgnutls_perror(err);
346 pgnutls_deinit(*s);
347 return FALSE;
350 pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
351 if (flags & GNUTLS_DATAGRAM) pgnutls_transport_set_pull_timeout_function(*s, schan_pull_timeout);
352 pgnutls_transport_set_push_function(*s, schan_push_adapter);
354 return TRUE;
357 void schan_imp_dispose_session(schan_imp_session session)
359 gnutls_session_t s = (gnutls_session_t)session;
360 pgnutls_deinit(s);
363 void schan_imp_set_session_transport(schan_imp_session session,
364 struct schan_transport *t)
366 gnutls_session_t s = (gnutls_session_t)session;
367 pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)t);
370 void schan_imp_set_session_target(schan_imp_session session, const char *target)
372 gnutls_session_t s = (gnutls_session_t)session;
374 pgnutls_server_name_set( s, GNUTLS_NAME_DNS, target, strlen(target) );
377 SECURITY_STATUS schan_imp_handshake(schan_imp_session session)
379 gnutls_session_t s = (gnutls_session_t)session;
380 int err;
382 while(1) {
383 err = pgnutls_handshake(s);
384 switch(err) {
385 case GNUTLS_E_SUCCESS:
386 TRACE("Handshake completed\n");
387 return SEC_E_OK;
389 case GNUTLS_E_AGAIN:
390 TRACE("Continue...\n");
391 return SEC_I_CONTINUE_NEEDED;
393 case GNUTLS_E_WARNING_ALERT_RECEIVED:
395 gnutls_alert_description_t alert = pgnutls_alert_get(s);
397 WARN("WARNING ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
399 switch(alert) {
400 case GNUTLS_A_UNRECOGNIZED_NAME:
401 TRACE("Ignoring\n");
402 continue;
403 default:
404 return SEC_E_INTERNAL_ERROR;
408 case GNUTLS_E_FATAL_ALERT_RECEIVED:
410 gnutls_alert_description_t alert = pgnutls_alert_get(s);
411 WARN("FATAL ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
412 return SEC_E_INTERNAL_ERROR;
415 default:
416 pgnutls_perror(err);
417 return SEC_E_INTERNAL_ERROR;
421 /* Never reached */
422 return SEC_E_OK;
425 static DWORD schannel_get_protocol(gnutls_protocol_t proto)
427 /* FIXME: currently schannel only implements client connections, but
428 * there's no reason it couldn't be used for servers as well. The
429 * context doesn't tell us which it is, so assume client for now.
431 switch (proto)
433 case GNUTLS_SSL3: return SP_PROT_SSL3_CLIENT;
434 case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
435 case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
436 case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
437 case GNUTLS_DTLS1_0: return SP_PROT_DTLS1_0_CLIENT;
438 case GNUTLS_DTLS1_2: return SP_PROT_DTLS1_2_CLIENT;
439 default:
440 FIXME("unknown protocol %d\n", proto);
441 return 0;
445 static ALG_ID schannel_get_cipher_algid(gnutls_cipher_algorithm_t cipher)
447 switch (cipher)
449 case GNUTLS_CIPHER_UNKNOWN:
450 case GNUTLS_CIPHER_NULL: return 0;
451 case GNUTLS_CIPHER_ARCFOUR_40:
452 case GNUTLS_CIPHER_ARCFOUR_128: return CALG_RC4;
453 case GNUTLS_CIPHER_DES_CBC: return CALG_DES;
454 case GNUTLS_CIPHER_3DES_CBC: return CALG_3DES;
455 case GNUTLS_CIPHER_AES_128_CBC:
456 case GNUTLS_CIPHER_AES_128_GCM: return CALG_AES_128;
457 case GNUTLS_CIPHER_AES_192_CBC: return CALG_AES_192;
458 case GNUTLS_CIPHER_AES_256_GCM:
459 case GNUTLS_CIPHER_AES_256_CBC: return CALG_AES_256;
460 case GNUTLS_CIPHER_RC2_40_CBC: return CALG_RC2;
461 default:
462 FIXME("unknown algorithm %d\n", cipher);
463 return 0;
467 static ALG_ID schannel_get_mac_algid(gnutls_mac_algorithm_t mac, gnutls_cipher_algorithm_t cipher)
469 switch (mac)
471 case GNUTLS_MAC_UNKNOWN:
472 case GNUTLS_MAC_NULL: return 0;
473 case GNUTLS_MAC_MD2: return CALG_MD2;
474 case GNUTLS_MAC_MD5: return CALG_MD5;
475 case GNUTLS_MAC_SHA1: return CALG_SHA1;
476 case GNUTLS_MAC_SHA256: return CALG_SHA_256;
477 case GNUTLS_MAC_SHA384: return CALG_SHA_384;
478 case GNUTLS_MAC_SHA512: return CALG_SHA_512;
479 case GNUTLS_MAC_AEAD:
480 /* When using AEAD (such as GCM), we return PRF algorithm instead
481 which is defined in RFC 5289. */
482 switch (cipher)
484 case GNUTLS_CIPHER_AES_128_GCM: return CALG_SHA_256;
485 case GNUTLS_CIPHER_AES_256_GCM: return CALG_SHA_384;
486 default:
487 break;
489 /* fall through */
490 default:
491 FIXME("unknown algorithm %d, cipher %d\n", mac, cipher);
492 return 0;
496 static ALG_ID schannel_get_kx_algid(int kx)
498 switch (kx)
500 case GNUTLS_KX_UNKNOWN: return 0;
501 case GNUTLS_KX_RSA:
502 case GNUTLS_KX_RSA_EXPORT: return CALG_RSA_KEYX;
503 case GNUTLS_KX_DHE_PSK:
504 case GNUTLS_KX_DHE_DSS:
505 case GNUTLS_KX_DHE_RSA: return CALG_DH_EPHEM;
506 case GNUTLS_KX_ANON_ECDH: return CALG_ECDH;
507 case GNUTLS_KX_ECDHE_RSA:
508 case GNUTLS_KX_ECDHE_PSK:
509 case GNUTLS_KX_ECDHE_ECDSA: return CALG_ECDH_EPHEM;
510 default:
511 FIXME("unknown algorithm %d\n", kx);
512 return 0;
516 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session)
518 gnutls_session_t s = (gnutls_session_t)session;
519 return pgnutls_cipher_get_block_size(pgnutls_cipher_get(s));
522 unsigned int schan_imp_get_max_message_size(schan_imp_session session)
524 return pgnutls_record_get_max_size((gnutls_session_t)session);
527 SECURITY_STATUS schan_imp_get_connection_info(schan_imp_session session,
528 SecPkgContext_ConnectionInfo *info)
530 gnutls_session_t s = (gnutls_session_t)session;
531 gnutls_protocol_t proto = pgnutls_protocol_get_version(s);
532 gnutls_cipher_algorithm_t alg = pgnutls_cipher_get(s);
533 gnutls_mac_algorithm_t mac = pgnutls_mac_get(s);
534 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
536 info->dwProtocol = schannel_get_protocol(proto);
537 info->aiCipher = schannel_get_cipher_algid(alg);
538 info->dwCipherStrength = pgnutls_cipher_get_key_size(alg) * 8;
539 info->aiHash = schannel_get_mac_algid(mac, alg);
540 info->dwHashStrength = pgnutls_mac_get_key_size(mac) * 8;
541 info->aiExch = schannel_get_kx_algid(kx);
542 /* FIXME: info->dwExchStrength? */
543 info->dwExchStrength = 0;
544 return SEC_E_OK;
547 SECURITY_STATUS schan_imp_get_unique_channel_binding(schan_imp_session session,
548 SecPkgContext_Bindings *bindings)
550 static const char prefix[] = "tls-unique:";
551 gnutls_datum_t datum;
552 int rc;
553 SECURITY_STATUS ret;
554 char *p;
555 gnutls_session_t s = (gnutls_session_t)session;
557 rc = pgnutls_session_channel_binding(s, GNUTLS_CB_TLS_UNIQUE, &datum);
558 if (rc)
560 pgnutls_perror(rc);
561 return SEC_E_INTERNAL_ERROR;
564 bindings->BindingsLength = sizeof(SEC_CHANNEL_BINDINGS) + sizeof(prefix)-1 + datum.size;
565 bindings->Bindings = heap_alloc_zero(bindings->BindingsLength);
566 if (!bindings->Bindings)
567 ret = SEC_E_INSUFFICIENT_MEMORY;
568 else
570 bindings->Bindings->cbApplicationDataLength = sizeof(prefix)-1 + datum.size;
571 bindings->Bindings->dwApplicationDataOffset = sizeof(SEC_CHANNEL_BINDINGS);
572 p = (char*)(bindings->Bindings+1);
573 memcpy(p, prefix, sizeof(prefix)-1);
574 p += sizeof(prefix)-1;
575 memcpy(p, datum.data, datum.size);
576 ret = SEC_E_OK;
578 free(datum.data);
579 return ret;
582 ALG_ID schan_imp_get_key_signature_algorithm(schan_imp_session session)
584 gnutls_session_t s = (gnutls_session_t)session;
585 gnutls_kx_algorithm_t kx = pgnutls_kx_get(s);
587 TRACE("(%p)\n", session);
589 switch (kx)
591 case GNUTLS_KX_UNKNOWN: return 0;
592 case GNUTLS_KX_RSA:
593 case GNUTLS_KX_RSA_EXPORT:
594 case GNUTLS_KX_DHE_RSA:
595 case GNUTLS_KX_ECDHE_RSA: return CALG_RSA_SIGN;
596 case GNUTLS_KX_ECDHE_ECDSA: return CALG_ECDSA;
597 default:
598 FIXME("unknown algorithm %d\n", kx);
599 return 0;
603 SECURITY_STATUS schan_imp_get_session_peer_certificate(schan_imp_session session, HCERTSTORE store,
604 PCCERT_CONTEXT *ret)
606 gnutls_session_t s = (gnutls_session_t)session;
607 PCCERT_CONTEXT cert = NULL;
608 const gnutls_datum_t *datum;
609 unsigned list_size, i;
610 BOOL res;
612 datum = pgnutls_certificate_get_peers(s, &list_size);
613 if(!datum)
614 return SEC_E_INTERNAL_ERROR;
616 for(i = 0; i < list_size; i++) {
617 res = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, datum[i].data, datum[i].size,
618 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert);
619 if(!res) {
620 if(i)
621 CertFreeCertificateContext(cert);
622 return GetLastError();
626 *ret = cert;
627 return SEC_E_OK;
630 SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buffer,
631 SIZE_T *length)
633 gnutls_session_t s = (gnutls_session_t)session;
634 SSIZE_T ret, total = 0;
636 for (;;)
638 ret = pgnutls_record_send(s, (const char *)buffer + total, *length - total);
639 if (ret >= 0)
641 total += ret;
642 TRACE( "sent %ld now %ld/%ld\n", ret, total, *length );
643 if (total == *length) return SEC_E_OK;
645 else if (ret == GNUTLS_E_AGAIN)
647 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
648 SIZE_T count = 0;
650 if (schan_get_buffer(t, &t->out, &count)) continue;
651 return SEC_I_CONTINUE_NEEDED;
653 else
655 pgnutls_perror(ret);
656 return SEC_E_INTERNAL_ERROR;
661 SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
662 SIZE_T *length)
664 gnutls_session_t s = (gnutls_session_t)session;
665 ssize_t ret;
667 again:
668 ret = pgnutls_record_recv(s, buffer, *length);
670 if (ret >= 0)
671 *length = ret;
672 else if (ret == GNUTLS_E_AGAIN)
674 struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
675 SIZE_T count = 0;
677 if (schan_get_buffer(t, &t->in, &count))
678 goto again;
680 return SEC_I_CONTINUE_NEEDED;
682 else if (ret == GNUTLS_E_REHANDSHAKE)
684 TRACE("Rehandshake requested\n");
685 return SEC_I_RENEGOTIATE;
687 else
689 pgnutls_perror(ret);
690 return SEC_E_INTERNAL_ERROR;
693 return SEC_E_OK;
696 static unsigned int parse_alpn_protocol_list(unsigned char *buffer, unsigned int buflen, gnutls_datum_t *list)
698 unsigned int len, offset = 0, count = 0;
700 while (buflen)
702 len = buffer[offset++];
703 buflen--;
704 if (!len || len > buflen) return 0;
705 if (list)
707 list[count].data = &buffer[offset];
708 list[count].size = len;
710 buflen -= len;
711 offset += len;
712 count++;
715 return count;
718 void schan_imp_set_application_protocols(schan_imp_session session, unsigned char *buffer, unsigned int buflen)
720 gnutls_session_t s = (gnutls_session_t)session;
721 unsigned int extension_len, extension, count = 0, offset = 0;
722 unsigned short list_len;
723 gnutls_datum_t *protocols;
724 int ret;
726 if (sizeof(extension_len) > buflen) return;
727 extension_len = *(unsigned int *)&buffer[offset];
728 offset += sizeof(extension_len);
730 if (offset + sizeof(extension) > buflen) return;
731 extension = *(unsigned int *)&buffer[offset];
732 if (extension != SecApplicationProtocolNegotiationExt_ALPN)
734 FIXME("extension %u not supported\n", extension);
735 return;
737 offset += sizeof(extension);
739 if (offset + sizeof(list_len) > buflen) return;
740 list_len = *(unsigned short *)&buffer[offset];
741 offset += sizeof(list_len);
743 if (offset + list_len > buflen) return;
744 count = parse_alpn_protocol_list(&buffer[offset], list_len, NULL);
745 if (!count || !(protocols = heap_alloc(count * sizeof(*protocols)))) return;
747 parse_alpn_protocol_list(&buffer[offset], list_len, protocols);
748 if ((ret = pgnutls_alpn_set_protocols(s, protocols, count, GNUTLS_ALPN_SERVER_PRECEDENCE) < 0))
750 pgnutls_perror(ret);
753 heap_free(protocols);
756 SECURITY_STATUS schan_imp_get_application_protocol(schan_imp_session session,
757 SecPkgContext_ApplicationProtocol *protocol)
759 gnutls_session_t s = (gnutls_session_t)session;
760 gnutls_datum_t selected;
762 memset(protocol, 0, sizeof(*protocol));
763 if (pgnutls_alpn_get_selected_protocol(s, &selected) < 0) return SEC_E_OK;
765 if (selected.size <= sizeof(protocol->ProtocolId))
767 protocol->ProtoNegoStatus = SecApplicationProtocolNegotiationStatus_Success;
768 protocol->ProtoNegoExt = SecApplicationProtocolNegotiationExt_ALPN;
769 protocol->ProtocolIdSize = selected.size;
770 memcpy(protocol->ProtocolId, selected.data, selected.size);
771 TRACE("returning %s\n", debugstr_an((const char *)selected.data, selected.size));
773 return SEC_E_OK;
776 SECURITY_STATUS schan_imp_set_dtls_mtu(schan_imp_session session, unsigned int mtu)
778 gnutls_session_t s = (gnutls_session_t)session;
780 pgnutls_dtls_set_mtu(s, mtu);
781 TRACE("MTU set to %u\n", mtu);
782 return SEC_E_OK;
785 static WCHAR *get_key_container_path(const CERT_CONTEXT *ctx)
787 static const WCHAR rsabaseW[] =
788 {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','C','r','y','p','t','o','\\','R','S','A','\\',0};
789 CERT_KEY_CONTEXT keyctx;
790 DWORD size = sizeof(keyctx), prov_size = 0;
791 CRYPT_KEY_PROV_INFO *prov;
792 WCHAR username[UNLEN + 1], *ret = NULL;
793 DWORD len = ARRAY_SIZE(username);
795 if (CertGetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, &keyctx, &size))
797 char *str;
798 if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, NULL, &size, 0)) return NULL;
799 if (!(str = heap_alloc(size))) return NULL;
800 if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, (BYTE *)str, &size, 0)) return NULL;
802 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
803 if (!(ret = heap_alloc(sizeof(rsabaseW) + len * sizeof(WCHAR))))
805 heap_free(str);
806 return NULL;
808 strcpyW(ret, rsabaseW);
809 MultiByteToWideChar(CP_ACP, 0, str, -1, ret + strlenW(ret), len);
810 heap_free(str);
812 else if (CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, NULL, &prov_size))
814 if (!(prov = heap_alloc(prov_size))) return NULL;
815 if (!CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, prov, &prov_size))
817 heap_free(prov);
818 return NULL;
820 if (!(ret = heap_alloc(sizeof(rsabaseW) + strlenW(prov->pwszContainerName) * sizeof(WCHAR))))
822 heap_free(prov);
823 return NULL;
825 strcpyW(ret, rsabaseW);
826 strcatW(ret, prov->pwszContainerName);
827 heap_free(prov);
830 if (!ret && GetUserNameW(username, &len) && (ret = heap_alloc(sizeof(rsabaseW) + len * sizeof(WCHAR))))
832 strcpyW(ret, rsabaseW);
833 strcatW(ret, username);
836 return ret;
839 #define MAX_LEAD_BYTES 8
840 static BYTE *get_key_blob(const CERT_CONTEXT *ctx, ULONG *size)
842 static const WCHAR keyexchangeW[] =
843 {'K','e','y','E','x','c','h','a','n','g','e','K','e','y','P','a','i','r',0};
844 static const WCHAR signatureW[] =
845 {'S','i','g','n','a','t','u','r','e','K','e','y','P','a','i','r',0};
846 BYTE *buf, *ret = NULL;
847 DATA_BLOB blob_in, blob_out;
848 DWORD spec = 0, type, len;
849 WCHAR *path;
850 HKEY hkey;
852 if (!(path = get_key_container_path(ctx))) return NULL;
853 if (RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hkey))
855 heap_free(path);
856 return NULL;
858 heap_free(path);
860 if (!RegQueryValueExW(hkey, keyexchangeW, 0, &type, NULL, &len)) spec = AT_KEYEXCHANGE;
861 else if (!RegQueryValueExW(hkey, signatureW, 0, &type, NULL, &len)) spec = AT_SIGNATURE;
862 else
864 RegCloseKey(hkey);
865 return NULL;
868 if (!(buf = heap_alloc(len + MAX_LEAD_BYTES)))
870 RegCloseKey(hkey);
871 return NULL;
874 if (!RegQueryValueExW(hkey, (spec == AT_KEYEXCHANGE) ? keyexchangeW : signatureW, 0, &type, buf, &len))
876 blob_in.pbData = buf;
877 blob_in.cbData = len;
878 if (CryptUnprotectData(&blob_in, NULL, NULL, NULL, NULL, 0, &blob_out))
880 assert(blob_in.cbData >= blob_out.cbData);
881 memcpy(buf, blob_out.pbData, blob_out.cbData);
882 LocalFree(blob_out.pbData);
883 *size = blob_out.cbData + MAX_LEAD_BYTES;
884 ret = buf;
887 else heap_free(buf);
889 RegCloseKey(hkey);
890 return ret;
893 static inline void reverse_bytes(BYTE *buf, ULONG len)
895 BYTE tmp;
896 ULONG i;
897 for (i = 0; i < len / 2; i++)
899 tmp = buf[i];
900 buf[i] = buf[len - i - 1];
901 buf[len - i - 1] = tmp;
905 static ULONG set_component(gnutls_datum_t *comp, BYTE *data, ULONG len, ULONG *buflen)
907 comp->data = data;
908 comp->size = len;
909 reverse_bytes(comp->data, comp->size);
910 if (comp->data[0] & 0x80) /* add leading 0 byte if most significant bit is set */
912 memmove(comp->data + 1, comp->data, *buflen);
913 comp->data[0] = 0;
914 comp->size++;
916 *buflen -= comp->size;
917 return comp->size;
920 static gnutls_x509_privkey_t get_x509_key(const CERT_CONTEXT *ctx)
922 gnutls_privkey_t key = NULL;
923 gnutls_x509_privkey_t x509key = NULL;
924 gnutls_datum_t m, e, d, p, q, u, e1, e2;
925 BYTE *ptr, *buffer;
926 RSAPUBKEY *rsakey;
927 DWORD size;
928 int ret;
930 if (!(buffer = get_key_blob(ctx, &size))) return NULL;
931 if (size < sizeof(BLOBHEADER)) goto done;
933 rsakey = (RSAPUBKEY *)(buffer + sizeof(BLOBHEADER));
934 TRACE("RSA key bitlen %u pubexp %u\n", rsakey->bitlen, rsakey->pubexp);
936 size -= sizeof(BLOBHEADER) + FIELD_OFFSET(RSAPUBKEY, pubexp);
937 set_component(&e, (BYTE *)&rsakey->pubexp, sizeof(rsakey->pubexp), &size);
939 ptr = (BYTE *)(rsakey + 1);
940 ptr += set_component(&m, ptr, rsakey->bitlen / 8, &size);
941 ptr += set_component(&p, ptr, rsakey->bitlen / 16, &size);
942 ptr += set_component(&q, ptr, rsakey->bitlen / 16, &size);
943 ptr += set_component(&e1, ptr, rsakey->bitlen / 16, &size);
944 ptr += set_component(&e2, ptr, rsakey->bitlen / 16, &size);
945 ptr += set_component(&u, ptr, rsakey->bitlen / 16, &size);
946 ptr += set_component(&d, ptr, rsakey->bitlen / 8, &size);
948 if ((ret = pgnutls_privkey_init(&key)) < 0)
950 pgnutls_perror(ret);
951 goto done;
954 if ((ret = pgnutls_privkey_import_rsa_raw(key, &m, &e, &d, &p, &q, &u, &e1, &e2)) < 0)
956 pgnutls_perror(ret);
957 goto done;
960 if ((ret = pgnutls_privkey_export_x509(key, &x509key)) < 0)
962 pgnutls_perror(ret);
965 done:
966 heap_free(buffer);
967 pgnutls_privkey_deinit(key);
968 return x509key;
971 static gnutls_x509_crt_t get_x509_crt(const CERT_CONTEXT *ctx)
973 gnutls_datum_t data;
974 gnutls_x509_crt_t crt;
975 int ret;
977 if (!ctx) return FALSE;
978 if (ctx->dwCertEncodingType != X509_ASN_ENCODING)
980 FIXME("encoding type %u not supported\n", ctx->dwCertEncodingType);
981 return NULL;
984 if ((ret = pgnutls_x509_crt_init(&crt)) < 0)
986 pgnutls_perror(ret);
987 return NULL;
990 data.data = ctx->pbCertEncoded;
991 data.size = ctx->cbCertEncoded;
992 if ((ret = pgnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_DER)) < 0)
994 pgnutls_perror(ret);
995 pgnutls_x509_crt_deinit(crt);
996 return NULL;
999 return crt;
1002 BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c, const CERT_CONTEXT *ctx)
1004 gnutls_certificate_credentials_t creds;
1005 gnutls_x509_crt_t crt;
1006 gnutls_x509_privkey_t key;
1007 int ret;
1009 ret = pgnutls_certificate_allocate_credentials(&creds);
1010 if (ret != GNUTLS_E_SUCCESS)
1012 pgnutls_perror(ret);
1013 return FALSE;
1016 if (!ctx)
1018 c->credentials = creds;
1019 return TRUE;
1022 if (!(crt = get_x509_crt(ctx)))
1024 pgnutls_certificate_free_credentials(creds);
1025 return FALSE;
1028 if (!(key = get_x509_key(ctx)))
1030 pgnutls_x509_crt_deinit(crt);
1031 pgnutls_certificate_free_credentials(creds);
1032 return FALSE;
1035 ret = pgnutls_certificate_set_x509_key(creds, &crt, 1, key);
1036 pgnutls_x509_privkey_deinit(key);
1037 pgnutls_x509_crt_deinit(crt);
1038 if (ret != GNUTLS_E_SUCCESS)
1040 pgnutls_perror(ret);
1041 pgnutls_certificate_free_credentials(creds);
1042 return FALSE;
1045 c->credentials = creds;
1046 return TRUE;
1049 void schan_imp_free_certificate_credentials(schan_credentials *c)
1051 pgnutls_certificate_free_credentials(c->credentials);
1054 static void schan_gnutls_log(int level, const char *msg)
1056 TRACE("<%d> %s", level, msg);
1059 BOOL schan_imp_init(void)
1061 const char *env_str;
1062 int ret;
1064 if ((env_str = getenv("GNUTLS_SYSTEM_PRIORITY_FILE")))
1066 WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str));
1068 else
1070 WARN("Setting GNUTLS_SYSTEM_PRIORITY_FILE to \"/dev/null\".\n");
1071 setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0);
1074 libgnutls_handle = dlopen(SONAME_LIBGNUTLS, RTLD_NOW);
1075 if (!libgnutls_handle)
1077 ERR_(winediag)("Failed to load libgnutls, secure connections will not be available.\n");
1078 return FALSE;
1081 #define LOAD_FUNCPTR(f) \
1082 if (!(p##f = dlsym(libgnutls_handle, #f))) \
1084 ERR("Failed to load %s\n", #f); \
1085 goto fail; \
1088 LOAD_FUNCPTR(gnutls_alert_get)
1089 LOAD_FUNCPTR(gnutls_alert_get_name)
1090 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
1091 LOAD_FUNCPTR(gnutls_certificate_free_credentials)
1092 LOAD_FUNCPTR(gnutls_certificate_get_peers)
1093 LOAD_FUNCPTR(gnutls_certificate_set_x509_key)
1094 LOAD_FUNCPTR(gnutls_cipher_get)
1095 LOAD_FUNCPTR(gnutls_cipher_get_key_size)
1096 LOAD_FUNCPTR(gnutls_credentials_set)
1097 LOAD_FUNCPTR(gnutls_deinit)
1098 LOAD_FUNCPTR(gnutls_global_deinit)
1099 LOAD_FUNCPTR(gnutls_global_init)
1100 LOAD_FUNCPTR(gnutls_global_set_log_function)
1101 LOAD_FUNCPTR(gnutls_global_set_log_level)
1102 LOAD_FUNCPTR(gnutls_handshake)
1103 LOAD_FUNCPTR(gnutls_init)
1104 LOAD_FUNCPTR(gnutls_kx_get)
1105 LOAD_FUNCPTR(gnutls_mac_get)
1106 LOAD_FUNCPTR(gnutls_mac_get_key_size)
1107 LOAD_FUNCPTR(gnutls_perror)
1108 LOAD_FUNCPTR(gnutls_protocol_get_version)
1109 LOAD_FUNCPTR(gnutls_priority_set_direct)
1110 LOAD_FUNCPTR(gnutls_privkey_deinit)
1111 LOAD_FUNCPTR(gnutls_privkey_init)
1112 LOAD_FUNCPTR(gnutls_record_get_max_size);
1113 LOAD_FUNCPTR(gnutls_record_recv);
1114 LOAD_FUNCPTR(gnutls_record_send);
1115 LOAD_FUNCPTR(gnutls_server_name_set)
1116 LOAD_FUNCPTR(gnutls_session_channel_binding)
1117 LOAD_FUNCPTR(gnutls_transport_get_ptr)
1118 LOAD_FUNCPTR(gnutls_transport_set_errno)
1119 LOAD_FUNCPTR(gnutls_transport_set_ptr)
1120 LOAD_FUNCPTR(gnutls_transport_set_pull_function)
1121 LOAD_FUNCPTR(gnutls_transport_set_push_function)
1122 LOAD_FUNCPTR(gnutls_x509_crt_deinit)
1123 LOAD_FUNCPTR(gnutls_x509_crt_import)
1124 LOAD_FUNCPTR(gnutls_x509_crt_init)
1125 LOAD_FUNCPTR(gnutls_x509_privkey_deinit)
1126 #undef LOAD_FUNCPTR
1128 if (!(pgnutls_cipher_get_block_size = dlsym(libgnutls_handle, "gnutls_cipher_get_block_size")))
1130 WARN("gnutls_cipher_get_block_size not found\n");
1131 pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
1133 if (!(pgnutls_transport_set_pull_timeout_function = dlsym(libgnutls_handle, "gnutls_transport_set_pull_timeout_function")))
1135 WARN("gnutls_transport_set_pull_timeout_function not found\n");
1136 pgnutls_transport_set_pull_timeout_function = compat_gnutls_transport_set_pull_timeout_function;
1138 if (!(pgnutls_alpn_set_protocols = dlsym(libgnutls_handle, "gnutls_alpn_set_protocols")))
1140 WARN("gnutls_alpn_set_protocols not found\n");
1141 pgnutls_alpn_set_protocols = compat_gnutls_alpn_set_protocols;
1143 if (!(pgnutls_alpn_get_selected_protocol = dlsym(libgnutls_handle, "gnutls_alpn_get_selected_protocol")))
1145 WARN("gnutls_alpn_get_selected_protocol not found\n");
1146 pgnutls_alpn_get_selected_protocol = compat_gnutls_alpn_get_selected_protocol;
1148 if (!(pgnutls_dtls_set_mtu = dlsym(libgnutls_handle, "gnutls_dtls_set_mtu")))
1150 WARN("gnutls_dtls_set_mtu not found\n");
1151 pgnutls_dtls_set_mtu = compat_gnutls_dtls_set_mtu;
1153 if (!(pgnutls_privkey_export_x509 = dlsym(libgnutls_handle, "gnutls_privkey_export_x509")))
1155 WARN("gnutls_privkey_export_x509 not found\n");
1156 pgnutls_privkey_export_x509 = compat_gnutls_privkey_export_x509;
1158 if (!(pgnutls_privkey_import_rsa_raw = dlsym(libgnutls_handle, "gnutls_privkey_import_rsa_raw")))
1160 WARN("gnutls_privkey_import_rsa_raw not found\n");
1161 pgnutls_privkey_import_rsa_raw = compat_gnutls_privkey_import_rsa_raw;
1164 ret = pgnutls_global_init();
1165 if (ret != GNUTLS_E_SUCCESS)
1167 pgnutls_perror(ret);
1168 goto fail;
1171 if (TRACE_ON(secur32))
1173 pgnutls_global_set_log_level(4);
1174 pgnutls_global_set_log_function(schan_gnutls_log);
1177 check_supported_protocols();
1178 return TRUE;
1180 fail:
1181 dlclose(libgnutls_handle);
1182 libgnutls_handle = NULL;
1183 return FALSE;
1186 void schan_imp_deinit(void)
1188 pgnutls_global_deinit();
1189 dlclose(libgnutls_handle);
1190 libgnutls_handle = NULL;
1193 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */