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
27 #include "wine/port.h"
32 #ifdef SONAME_LIBGNUTLS
33 #include <gnutls/gnutls.h>
34 #include <gnutls/crypto.h>
35 #include <gnutls/abstract.h>
39 #define WIN32_NO_STATUS
45 #include "secur32_priv.h"
47 #include "wine/debug.h"
48 #include "wine/unicode.h"
50 #if defined(SONAME_LIBGNUTLS)
52 WINE_DEFAULT_DEBUG_CHANNEL(secur32
);
53 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
55 static const struct schan_callbacks
*callbacks
;
57 /* Not present in gnutls version < 2.9.10. */
58 static int (*pgnutls_cipher_get_block_size
)(gnutls_cipher_algorithm_t
);
60 /* Not present in gnutls version < 3.0. */
61 static void (*pgnutls_transport_set_pull_timeout_function
)(gnutls_session_t
,
62 int (*)(gnutls_transport_ptr_t
, unsigned int));
63 static void (*pgnutls_dtls_set_mtu
)(gnutls_session_t
, unsigned int);
65 /* Not present in gnutls version < 3.2.0. */
66 static int (*pgnutls_alpn_get_selected_protocol
)(gnutls_session_t
, gnutls_datum_t
*);
67 static int (*pgnutls_alpn_set_protocols
)(gnutls_session_t
, const gnutls_datum_t
*,
68 unsigned, unsigned int);
70 /* Not present in gnutls version < 3.3.0. */
71 static int (*pgnutls_privkey_import_rsa_raw
)(gnutls_privkey_t
, const gnutls_datum_t
*,
72 const gnutls_datum_t
*, const gnutls_datum_t
*,
73 const gnutls_datum_t
*, const gnutls_datum_t
*,
74 const gnutls_datum_t
*, const gnutls_datum_t
*,
75 const gnutls_datum_t
*);
77 /* Not present in gnutls version < 3.4.0. */
78 static int (*pgnutls_privkey_export_x509
)(gnutls_privkey_t
, gnutls_x509_privkey_t
*);
80 static void *libgnutls_handle
;
81 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
82 MAKE_FUNCPTR(gnutls_alert_get
);
83 MAKE_FUNCPTR(gnutls_alert_get_name
);
84 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials
);
85 MAKE_FUNCPTR(gnutls_certificate_free_credentials
);
86 MAKE_FUNCPTR(gnutls_certificate_get_peers
);
87 MAKE_FUNCPTR(gnutls_certificate_set_x509_key
);
88 MAKE_FUNCPTR(gnutls_cipher_get
);
89 MAKE_FUNCPTR(gnutls_cipher_get_key_size
);
90 MAKE_FUNCPTR(gnutls_credentials_set
);
91 MAKE_FUNCPTR(gnutls_deinit
);
92 MAKE_FUNCPTR(gnutls_global_deinit
);
93 MAKE_FUNCPTR(gnutls_global_init
);
94 MAKE_FUNCPTR(gnutls_global_set_log_function
);
95 MAKE_FUNCPTR(gnutls_global_set_log_level
);
96 MAKE_FUNCPTR(gnutls_handshake
);
97 MAKE_FUNCPTR(gnutls_init
);
98 MAKE_FUNCPTR(gnutls_kx_get
);
99 MAKE_FUNCPTR(gnutls_mac_get
);
100 MAKE_FUNCPTR(gnutls_mac_get_key_size
);
101 MAKE_FUNCPTR(gnutls_perror
);
102 MAKE_FUNCPTR(gnutls_protocol_get_version
);
103 MAKE_FUNCPTR(gnutls_priority_set_direct
);
104 MAKE_FUNCPTR(gnutls_privkey_deinit
);
105 MAKE_FUNCPTR(gnutls_privkey_init
);
106 MAKE_FUNCPTR(gnutls_record_get_max_size
);
107 MAKE_FUNCPTR(gnutls_record_recv
);
108 MAKE_FUNCPTR(gnutls_record_send
);
109 MAKE_FUNCPTR(gnutls_server_name_set
);
110 MAKE_FUNCPTR(gnutls_session_channel_binding
);
111 MAKE_FUNCPTR(gnutls_transport_get_ptr
);
112 MAKE_FUNCPTR(gnutls_transport_set_errno
);
113 MAKE_FUNCPTR(gnutls_transport_set_ptr
);
114 MAKE_FUNCPTR(gnutls_transport_set_pull_function
);
115 MAKE_FUNCPTR(gnutls_transport_set_push_function
);
116 MAKE_FUNCPTR(gnutls_x509_crt_deinit
);
117 MAKE_FUNCPTR(gnutls_x509_crt_import
);
118 MAKE_FUNCPTR(gnutls_x509_crt_init
);
119 MAKE_FUNCPTR(gnutls_x509_privkey_deinit
);
122 #if GNUTLS_VERSION_MAJOR < 3
123 #define GNUTLS_CIPHER_AES_192_CBC 92
124 #define GNUTLS_CIPHER_AES_128_GCM 93
125 #define GNUTLS_CIPHER_AES_256_GCM 94
127 #define GNUTLS_MAC_AEAD 200
129 #define GNUTLS_KX_ANON_ECDH 11
130 #define GNUTLS_KX_ECDHE_RSA 12
131 #define GNUTLS_KX_ECDHE_ECDSA 13
132 #define GNUTLS_KX_ECDHE_PSK 14
135 #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 5)
136 #define GNUTLS_ALPN_SERVER_PRECEDENCE (1<<1)
139 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher
)
142 case GNUTLS_CIPHER_3DES_CBC
:
144 case GNUTLS_CIPHER_AES_128_CBC
:
145 case GNUTLS_CIPHER_AES_256_CBC
:
147 case GNUTLS_CIPHER_ARCFOUR_128
:
148 case GNUTLS_CIPHER_ARCFOUR_40
:
150 case GNUTLS_CIPHER_DES_CBC
:
152 case GNUTLS_CIPHER_NULL
:
154 case GNUTLS_CIPHER_RC2_40_CBC
:
157 FIXME("Unknown cipher %#x, returning 1\n", cipher
);
162 static void compat_gnutls_transport_set_pull_timeout_function(gnutls_session_t session
,
163 int (*func
)(gnutls_transport_ptr_t
, unsigned int))
168 static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey
, gnutls_x509_privkey_t
*key
)
171 return GNUTLS_E_UNKNOWN_PK_ALGORITHM
;
174 static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key
, const gnutls_datum_t
*p1
,
175 const gnutls_datum_t
*p2
, const gnutls_datum_t
*p3
,
176 const gnutls_datum_t
*p4
, const gnutls_datum_t
*p5
,
177 const gnutls_datum_t
*p6
, const gnutls_datum_t
*p7
,
178 const gnutls_datum_t
*p8
)
181 return GNUTLS_E_UNKNOWN_PK_ALGORITHM
;
184 static int compat_gnutls_alpn_get_selected_protocol(gnutls_session_t session
, gnutls_datum_t
*protocol
)
187 return GNUTLS_E_INVALID_REQUEST
;
190 static int compat_gnutls_alpn_set_protocols(gnutls_session_t session
, const gnutls_datum_t
*protocols
,
191 unsigned size
, unsigned int flags
)
194 return GNUTLS_E_INVALID_REQUEST
;
197 static void compat_gnutls_dtls_set_mtu(gnutls_session_t session
, unsigned int mtu
)
202 static ssize_t
pull_adapter(gnutls_transport_ptr_t transport
, void *buff
, size_t buff_len
)
204 struct schan_transport
*t
= (struct schan_transport
*)transport
;
205 gnutls_session_t s
= (gnutls_session_t
)callbacks
->get_session_for_transport(t
);
207 int ret
= callbacks
->pull(transport
, buff
, &buff_len
);
210 pgnutls_transport_set_errno(s
, EAGAIN
);
215 FIXME("unhandled error from pull callback %d\n", ret
);
222 static ssize_t
push_adapter(gnutls_transport_ptr_t transport
, const void *buff
, size_t buff_len
)
224 struct schan_transport
*t
= (struct schan_transport
*)transport
;
225 gnutls_session_t s
= (gnutls_session_t
)callbacks
->get_session_for_transport(t
);
227 int ret
= callbacks
->push(transport
, buff
, &buff_len
);
230 pgnutls_transport_set_errno(s
, EAGAIN
);
235 FIXME("unhandled error from push callback %d\n", ret
);
242 static const struct {
244 const char *gnutls_flag
;
245 } protocol_priority_flags
[] = {
246 {SP_PROT_DTLS1_2_CLIENT
, "VERS-DTLS1.2"},
247 {SP_PROT_DTLS1_0_CLIENT
, "VERS-DTLS1.0"},
248 {SP_PROT_TLS1_3_CLIENT
, "VERS-TLS1.3"},
249 {SP_PROT_TLS1_2_CLIENT
, "VERS-TLS1.2"},
250 {SP_PROT_TLS1_1_CLIENT
, "VERS-TLS1.1"},
251 {SP_PROT_TLS1_0_CLIENT
, "VERS-TLS1.0"},
252 {SP_PROT_SSL3_CLIENT
, "VERS-SSL3.0"}
253 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
256 static DWORD supported_protocols
;
258 static void check_supported_protocols(void)
260 gnutls_session_t session
;
265 err
= pgnutls_init(&session
, GNUTLS_CLIENT
);
266 if (err
!= GNUTLS_E_SUCCESS
)
272 for(i
= 0; i
< ARRAY_SIZE(protocol_priority_flags
); i
++)
274 sprintf(priority
, "NORMAL:-%s", protocol_priority_flags
[i
].gnutls_flag
);
275 err
= pgnutls_priority_set_direct(session
, priority
, NULL
);
276 if (err
== GNUTLS_E_SUCCESS
)
278 TRACE("%s is supported\n", protocol_priority_flags
[i
].gnutls_flag
);
279 supported_protocols
|= protocol_priority_flags
[i
].enable_flag
;
282 TRACE("%s is not supported\n", protocol_priority_flags
[i
].gnutls_flag
);
285 pgnutls_deinit(session
);
288 static DWORD CDECL
schan_get_enabled_protocols(void)
290 return supported_protocols
;
293 static int pull_timeout(gnutls_transport_ptr_t transport
, unsigned int timeout
)
295 struct schan_transport
*t
= (struct schan_transport
*)transport
;
296 gnutls_session_t s
= (gnutls_session_t
)callbacks
->get_session_for_transport(t
);
301 if (callbacks
->get_buffer(t
, &t
->in
, &count
)) return 1;
302 pgnutls_transport_set_errno(s
, EAGAIN
);
306 static BOOL CDECL
schan_create_session(schan_session
*session
, schan_credentials
*cred
)
308 gnutls_session_t
*s
= (gnutls_session_t
*)session
;
309 char priority
[128] = "NORMAL:%LATEST_RECORD_VERSION", *p
;
310 BOOL using_vers_all
= FALSE
, disabled
;
311 unsigned int i
, flags
= (cred
->credential_use
== SECPKG_CRED_INBOUND
) ? GNUTLS_SERVER
: GNUTLS_CLIENT
;
314 if (cred
->enabled_protocols
& (SP_PROT_DTLS1_0_CLIENT
| SP_PROT_DTLS1_2_CLIENT
))
316 flags
|= GNUTLS_DATAGRAM
;
319 err
= pgnutls_init(s
, flags
);
320 if (err
!= GNUTLS_E_SUCCESS
)
326 p
= priority
+ strlen(priority
);
328 /* VERS-ALL is nice to use for forward compatibility. It was introduced before support for TLS1.3,
329 * so if TLS1.3 is supported, we may safely use it. Otherwise explicitly disable all known
330 * disabled protocols. */
331 if (supported_protocols
& SP_PROT_TLS1_3_CLIENT
)
333 strcpy(p
, ":-VERS-ALL");
335 using_vers_all
= TRUE
;
338 for (i
= 0; i
< ARRAY_SIZE(protocol_priority_flags
); i
++)
340 if (!(supported_protocols
& protocol_priority_flags
[i
].enable_flag
)) continue;
342 disabled
= !(cred
->enabled_protocols
& protocol_priority_flags
[i
].enable_flag
);
343 if (using_vers_all
&& disabled
) continue;
346 *p
++ = disabled
? '-' : '+';
347 strcpy(p
, protocol_priority_flags
[i
].gnutls_flag
);
351 TRACE("Using %s priority\n", debugstr_a(priority
));
352 err
= pgnutls_priority_set_direct(*s
, priority
, NULL
);
353 if (err
!= GNUTLS_E_SUCCESS
)
360 err
= pgnutls_credentials_set(*s
, GNUTLS_CRD_CERTIFICATE
,
361 (gnutls_certificate_credentials_t
)cred
->credentials
);
362 if (err
!= GNUTLS_E_SUCCESS
)
369 pgnutls_transport_set_pull_function(*s
, pull_adapter
);
370 if (flags
& GNUTLS_DATAGRAM
) pgnutls_transport_set_pull_timeout_function(*s
, pull_timeout
);
371 pgnutls_transport_set_push_function(*s
, push_adapter
);
376 static void CDECL
schan_dispose_session(schan_session session
)
378 gnutls_session_t s
= (gnutls_session_t
)session
;
382 static void CDECL
schan_set_session_transport(schan_session session
, struct schan_transport
*t
)
384 gnutls_session_t s
= (gnutls_session_t
)session
;
385 pgnutls_transport_set_ptr(s
, (gnutls_transport_ptr_t
)t
);
388 static void CDECL
schan_set_session_target(schan_session session
, const char *target
)
390 gnutls_session_t s
= (gnutls_session_t
)session
;
391 pgnutls_server_name_set( s
, GNUTLS_NAME_DNS
, target
, strlen(target
) );
394 static SECURITY_STATUS CDECL
schan_handshake(schan_session session
)
396 gnutls_session_t s
= (gnutls_session_t
)session
;
400 err
= pgnutls_handshake(s
);
402 case GNUTLS_E_SUCCESS
:
403 TRACE("Handshake completed\n");
407 TRACE("Continue...\n");
408 return SEC_I_CONTINUE_NEEDED
;
410 case GNUTLS_E_WARNING_ALERT_RECEIVED
:
412 gnutls_alert_description_t alert
= pgnutls_alert_get(s
);
414 WARN("WARNING ALERT: %d %s\n", alert
, pgnutls_alert_get_name(alert
));
417 case GNUTLS_A_UNRECOGNIZED_NAME
:
421 return SEC_E_INTERNAL_ERROR
;
425 case GNUTLS_E_FATAL_ALERT_RECEIVED
:
427 gnutls_alert_description_t alert
= pgnutls_alert_get(s
);
428 WARN("FATAL ALERT: %d %s\n", alert
, pgnutls_alert_get_name(alert
));
429 return SEC_E_INTERNAL_ERROR
;
434 return SEC_E_INTERNAL_ERROR
;
442 static DWORD
get_protocol(gnutls_protocol_t proto
)
444 /* FIXME: currently schannel only implements client connections, but
445 * there's no reason it couldn't be used for servers as well. The
446 * context doesn't tell us which it is, so assume client for now.
450 case GNUTLS_SSL3
: return SP_PROT_SSL3_CLIENT
;
451 case GNUTLS_TLS1_0
: return SP_PROT_TLS1_0_CLIENT
;
452 case GNUTLS_TLS1_1
: return SP_PROT_TLS1_1_CLIENT
;
453 case GNUTLS_TLS1_2
: return SP_PROT_TLS1_2_CLIENT
;
454 case GNUTLS_DTLS1_0
: return SP_PROT_DTLS1_0_CLIENT
;
455 case GNUTLS_DTLS1_2
: return SP_PROT_DTLS1_2_CLIENT
;
457 FIXME("unknown protocol %d\n", proto
);
462 static ALG_ID
get_cipher_algid(gnutls_cipher_algorithm_t cipher
)
466 case GNUTLS_CIPHER_UNKNOWN
:
467 case GNUTLS_CIPHER_NULL
: return 0;
468 case GNUTLS_CIPHER_ARCFOUR_40
:
469 case GNUTLS_CIPHER_ARCFOUR_128
: return CALG_RC4
;
470 case GNUTLS_CIPHER_DES_CBC
: return CALG_DES
;
471 case GNUTLS_CIPHER_3DES_CBC
: return CALG_3DES
;
472 case GNUTLS_CIPHER_AES_128_CBC
:
473 case GNUTLS_CIPHER_AES_128_GCM
: return CALG_AES_128
;
474 case GNUTLS_CIPHER_AES_192_CBC
: return CALG_AES_192
;
475 case GNUTLS_CIPHER_AES_256_GCM
:
476 case GNUTLS_CIPHER_AES_256_CBC
: return CALG_AES_256
;
477 case GNUTLS_CIPHER_RC2_40_CBC
: return CALG_RC2
;
479 FIXME("unknown algorithm %d\n", cipher
);
484 static ALG_ID
get_mac_algid(gnutls_mac_algorithm_t mac
, gnutls_cipher_algorithm_t cipher
)
488 case GNUTLS_MAC_UNKNOWN
:
489 case GNUTLS_MAC_NULL
: return 0;
490 case GNUTLS_MAC_MD2
: return CALG_MD2
;
491 case GNUTLS_MAC_MD5
: return CALG_MD5
;
492 case GNUTLS_MAC_SHA1
: return CALG_SHA1
;
493 case GNUTLS_MAC_SHA256
: return CALG_SHA_256
;
494 case GNUTLS_MAC_SHA384
: return CALG_SHA_384
;
495 case GNUTLS_MAC_SHA512
: return CALG_SHA_512
;
496 case GNUTLS_MAC_AEAD
:
497 /* When using AEAD (such as GCM), we return PRF algorithm instead
498 which is defined in RFC 5289. */
501 case GNUTLS_CIPHER_AES_128_GCM
: return CALG_SHA_256
;
502 case GNUTLS_CIPHER_AES_256_GCM
: return CALG_SHA_384
;
508 FIXME("unknown algorithm %d, cipher %d\n", mac
, cipher
);
513 static ALG_ID
get_kx_algid(int kx
)
517 case GNUTLS_KX_UNKNOWN
: return 0;
519 case GNUTLS_KX_RSA_EXPORT
: return CALG_RSA_KEYX
;
520 case GNUTLS_KX_DHE_PSK
:
521 case GNUTLS_KX_DHE_DSS
:
522 case GNUTLS_KX_DHE_RSA
: return CALG_DH_EPHEM
;
523 case GNUTLS_KX_ANON_ECDH
: return CALG_ECDH
;
524 case GNUTLS_KX_ECDHE_RSA
:
525 case GNUTLS_KX_ECDHE_PSK
:
526 case GNUTLS_KX_ECDHE_ECDSA
: return CALG_ECDH_EPHEM
;
528 FIXME("unknown algorithm %d\n", kx
);
533 static unsigned int CDECL
schan_get_session_cipher_block_size(schan_session session
)
535 gnutls_session_t s
= (gnutls_session_t
)session
;
536 return pgnutls_cipher_get_block_size(pgnutls_cipher_get(s
));
539 static unsigned int CDECL
schan_get_max_message_size(schan_session session
)
541 return pgnutls_record_get_max_size((gnutls_session_t
)session
);
544 static SECURITY_STATUS CDECL
schan_get_connection_info(schan_session session
, SecPkgContext_ConnectionInfo
*info
)
546 gnutls_session_t s
= (gnutls_session_t
)session
;
547 gnutls_protocol_t proto
= pgnutls_protocol_get_version(s
);
548 gnutls_cipher_algorithm_t alg
= pgnutls_cipher_get(s
);
549 gnutls_mac_algorithm_t mac
= pgnutls_mac_get(s
);
550 gnutls_kx_algorithm_t kx
= pgnutls_kx_get(s
);
552 info
->dwProtocol
= get_protocol(proto
);
553 info
->aiCipher
= get_cipher_algid(alg
);
554 info
->dwCipherStrength
= pgnutls_cipher_get_key_size(alg
) * 8;
555 info
->aiHash
= get_mac_algid(mac
, alg
);
556 info
->dwHashStrength
= pgnutls_mac_get_key_size(mac
) * 8;
557 info
->aiExch
= get_kx_algid(kx
);
558 /* FIXME: info->dwExchStrength? */
559 info
->dwExchStrength
= 0;
563 static SECURITY_STATUS CDECL
schan_get_unique_channel_binding(schan_session session
, SecPkgContext_Bindings
*bindings
)
565 static const char prefix
[] = "tls-unique:";
566 gnutls_datum_t datum
;
570 gnutls_session_t s
= (gnutls_session_t
)session
;
572 rc
= pgnutls_session_channel_binding(s
, GNUTLS_CB_TLS_UNIQUE
, &datum
);
576 return SEC_E_INTERNAL_ERROR
;
579 bindings
->BindingsLength
= sizeof(SEC_CHANNEL_BINDINGS
) + sizeof(prefix
)-1 + datum
.size
;
580 bindings
->Bindings
= RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY
, bindings
->BindingsLength
);
581 if (!bindings
->Bindings
)
582 ret
= SEC_E_INSUFFICIENT_MEMORY
;
585 bindings
->Bindings
->cbApplicationDataLength
= sizeof(prefix
)-1 + datum
.size
;
586 bindings
->Bindings
->dwApplicationDataOffset
= sizeof(SEC_CHANNEL_BINDINGS
);
587 p
= (char*)(bindings
->Bindings
+1);
588 memcpy(p
, prefix
, sizeof(prefix
)-1);
589 p
+= sizeof(prefix
)-1;
590 memcpy(p
, datum
.data
, datum
.size
);
597 static ALG_ID CDECL
schan_get_key_signature_algorithm(schan_session session
)
599 gnutls_session_t s
= (gnutls_session_t
)session
;
600 gnutls_kx_algorithm_t kx
= pgnutls_kx_get(s
);
602 TRACE("(%p)\n", session
);
606 case GNUTLS_KX_UNKNOWN
: return 0;
608 case GNUTLS_KX_RSA_EXPORT
:
609 case GNUTLS_KX_DHE_RSA
:
610 case GNUTLS_KX_ECDHE_RSA
: return CALG_RSA_SIGN
;
611 case GNUTLS_KX_ECDHE_ECDSA
: return CALG_ECDSA
;
613 FIXME("unknown algorithm %d\n", kx
);
618 static SECURITY_STATUS CDECL
schan_get_session_peer_certificate(schan_session session
, struct schan_cert_list
*list
)
620 gnutls_session_t s
= (gnutls_session_t
)session
;
621 const gnutls_datum_t
*datum
;
622 unsigned int i
, size
;
625 if (!(datum
= pgnutls_certificate_get_peers(s
, &list
->count
))) return SEC_E_INTERNAL_ERROR
;
627 size
= list
->count
* sizeof(list
->certs
[0]);
628 for (i
= 0; i
< list
->count
; i
++) size
+= datum
[i
].size
;
629 if (!(list
->certs
= RtlAllocateHeap(GetProcessHeap(), 0, size
))) return SEC_E_INSUFFICIENT_MEMORY
;
631 ptr
= (BYTE
*)&list
->certs
[list
->count
];
632 for (i
= 0; i
< list
->count
; i
++)
634 list
->certs
[i
].cbData
= datum
[i
].size
;
635 list
->certs
[i
].pbData
= ptr
;
636 memcpy(list
->certs
[i
].pbData
, datum
[i
].data
, datum
[i
].size
);
637 ptr
+= datum
[i
].size
;
643 static SECURITY_STATUS CDECL
schan_send(schan_session session
, const void *buffer
, SIZE_T
*length
)
645 gnutls_session_t s
= (gnutls_session_t
)session
;
646 SSIZE_T ret
, total
= 0;
650 ret
= pgnutls_record_send(s
, (const char *)buffer
+ total
, *length
- total
);
654 TRACE( "sent %ld now %ld/%ld\n", ret
, total
, *length
);
655 if (total
== *length
) return SEC_E_OK
;
657 else if (ret
== GNUTLS_E_AGAIN
)
659 struct schan_transport
*t
= (struct schan_transport
*)pgnutls_transport_get_ptr(s
);
662 if (callbacks
->get_buffer(t
, &t
->out
, &count
)) continue;
663 return SEC_I_CONTINUE_NEEDED
;
668 return SEC_E_INTERNAL_ERROR
;
673 static SECURITY_STATUS CDECL
schan_recv(schan_session session
, void *buffer
, SIZE_T
*length
)
675 gnutls_session_t s
= (gnutls_session_t
)session
;
679 ret
= pgnutls_record_recv(s
, buffer
, *length
);
683 else if (ret
== GNUTLS_E_AGAIN
)
685 struct schan_transport
*t
= (struct schan_transport
*)pgnutls_transport_get_ptr(s
);
688 if (callbacks
->get_buffer(t
, &t
->in
, &count
))
691 return SEC_I_CONTINUE_NEEDED
;
693 else if (ret
== GNUTLS_E_REHANDSHAKE
)
695 TRACE("Rehandshake requested\n");
696 return SEC_I_RENEGOTIATE
;
701 return SEC_E_INTERNAL_ERROR
;
707 static unsigned int parse_alpn_protocol_list(unsigned char *buffer
, unsigned int buflen
, gnutls_datum_t
*list
)
709 unsigned int len
, offset
= 0, count
= 0;
713 len
= buffer
[offset
++];
715 if (!len
|| len
> buflen
) return 0;
718 list
[count
].data
= &buffer
[offset
];
719 list
[count
].size
= len
;
729 static void CDECL
schan_set_application_protocols(schan_session session
, unsigned char *buffer
, unsigned int buflen
)
731 gnutls_session_t s
= (gnutls_session_t
)session
;
732 unsigned int extension_len
, extension
, count
= 0, offset
= 0;
733 unsigned short list_len
;
734 gnutls_datum_t
*protocols
;
737 if (sizeof(extension_len
) > buflen
) return;
738 extension_len
= *(unsigned int *)&buffer
[offset
];
739 offset
+= sizeof(extension_len
);
741 if (offset
+ sizeof(extension
) > buflen
) return;
742 extension
= *(unsigned int *)&buffer
[offset
];
743 if (extension
!= SecApplicationProtocolNegotiationExt_ALPN
)
745 FIXME("extension %u not supported\n", extension
);
748 offset
+= sizeof(extension
);
750 if (offset
+ sizeof(list_len
) > buflen
) return;
751 list_len
= *(unsigned short *)&buffer
[offset
];
752 offset
+= sizeof(list_len
);
754 if (offset
+ list_len
> buflen
) return;
755 count
= parse_alpn_protocol_list(&buffer
[offset
], list_len
, NULL
);
756 if (!count
|| !(protocols
= RtlAllocateHeap(GetProcessHeap(), 0, count
* sizeof(*protocols
)))) return;
758 parse_alpn_protocol_list(&buffer
[offset
], list_len
, protocols
);
759 if ((ret
= pgnutls_alpn_set_protocols(s
, protocols
, count
, GNUTLS_ALPN_SERVER_PRECEDENCE
) < 0))
764 RtlFreeHeap(GetProcessHeap(), 0, protocols
);
767 static SECURITY_STATUS CDECL
schan_get_application_protocol(schan_session session
,
768 SecPkgContext_ApplicationProtocol
*protocol
)
770 gnutls_session_t s
= (gnutls_session_t
)session
;
771 gnutls_datum_t selected
;
773 memset(protocol
, 0, sizeof(*protocol
));
774 if (pgnutls_alpn_get_selected_protocol(s
, &selected
) < 0) return SEC_E_OK
;
776 if (selected
.size
<= sizeof(protocol
->ProtocolId
))
778 protocol
->ProtoNegoStatus
= SecApplicationProtocolNegotiationStatus_Success
;
779 protocol
->ProtoNegoExt
= SecApplicationProtocolNegotiationExt_ALPN
;
780 protocol
->ProtocolIdSize
= selected
.size
;
781 memcpy(protocol
->ProtocolId
, selected
.data
, selected
.size
);
782 TRACE("returning %s\n", wine_dbgstr_an((const char *)selected
.data
, selected
.size
));
787 static SECURITY_STATUS CDECL
schan_set_dtls_mtu(schan_session session
, unsigned int mtu
)
789 gnutls_session_t s
= (gnutls_session_t
)session
;
791 pgnutls_dtls_set_mtu(s
, mtu
);
792 TRACE("MTU set to %u\n", mtu
);
796 static inline void reverse_bytes(BYTE
*buf
, ULONG len
)
800 for (i
= 0; i
< len
/ 2; i
++)
803 buf
[i
] = buf
[len
- i
- 1];
804 buf
[len
- i
- 1] = tmp
;
808 static ULONG
set_component(gnutls_datum_t
*comp
, BYTE
*data
, ULONG len
, ULONG
*buflen
)
812 reverse_bytes(comp
->data
, comp
->size
);
813 if (comp
->data
[0] & 0x80) /* add leading 0 byte if most significant bit is set */
815 memmove(comp
->data
+ 1, comp
->data
, *buflen
);
819 *buflen
-= comp
->size
;
823 static gnutls_x509_privkey_t
get_x509_key(const DATA_BLOB
*key_blob
)
825 gnutls_privkey_t key
= NULL
;
826 gnutls_x509_privkey_t x509key
= NULL
;
827 gnutls_datum_t m
, e
, d
, p
, q
, u
, e1
, e2
;
830 DWORD size
= key_blob
->cbData
;
833 if (size
< sizeof(BLOBHEADER
)) return NULL
;
835 rsakey
= (RSAPUBKEY
*)(key_blob
->pbData
+ sizeof(BLOBHEADER
));
836 TRACE("RSA key bitlen %u pubexp %u\n", rsakey
->bitlen
, rsakey
->pubexp
);
838 size
-= sizeof(BLOBHEADER
) + FIELD_OFFSET(RSAPUBKEY
, pubexp
);
839 set_component(&e
, (BYTE
*)&rsakey
->pubexp
, sizeof(rsakey
->pubexp
), &size
);
841 ptr
= (BYTE
*)(rsakey
+ 1);
842 ptr
+= set_component(&m
, ptr
, rsakey
->bitlen
/ 8, &size
);
843 ptr
+= set_component(&p
, ptr
, rsakey
->bitlen
/ 16, &size
);
844 ptr
+= set_component(&q
, ptr
, rsakey
->bitlen
/ 16, &size
);
845 ptr
+= set_component(&e1
, ptr
, rsakey
->bitlen
/ 16, &size
);
846 ptr
+= set_component(&e2
, ptr
, rsakey
->bitlen
/ 16, &size
);
847 ptr
+= set_component(&u
, ptr
, rsakey
->bitlen
/ 16, &size
);
848 ptr
+= set_component(&d
, ptr
, rsakey
->bitlen
/ 8, &size
);
850 if ((ret
= pgnutls_privkey_init(&key
)) < 0)
856 if (((ret
= pgnutls_privkey_import_rsa_raw(key
, &m
, &e
, &d
, &p
, &q
, &u
, &e1
, &e2
)) < 0) ||
857 (ret
= pgnutls_privkey_export_x509(key
, &x509key
)) < 0)
860 pgnutls_privkey_deinit(key
);
867 static gnutls_x509_crt_t
get_x509_crt(const CERT_CONTEXT
*ctx
)
870 gnutls_x509_crt_t crt
;
873 if (!ctx
) return FALSE
;
874 if (ctx
->dwCertEncodingType
!= X509_ASN_ENCODING
)
876 FIXME("encoding type %u not supported\n", ctx
->dwCertEncodingType
);
880 if ((ret
= pgnutls_x509_crt_init(&crt
)) < 0)
886 data
.data
= ctx
->pbCertEncoded
;
887 data
.size
= ctx
->cbCertEncoded
;
888 if ((ret
= pgnutls_x509_crt_import(crt
, &data
, GNUTLS_X509_FMT_DER
)) < 0)
891 pgnutls_x509_crt_deinit(crt
);
898 static BOOL CDECL
schan_allocate_certificate_credentials(schan_credentials
*c
, const CERT_CONTEXT
*ctx
,
899 const DATA_BLOB
*key_blob
)
901 gnutls_certificate_credentials_t creds
;
902 gnutls_x509_crt_t crt
;
903 gnutls_x509_privkey_t key
;
906 ret
= pgnutls_certificate_allocate_credentials(&creds
);
907 if (ret
!= GNUTLS_E_SUCCESS
)
915 c
->credentials
= creds
;
919 if (!(crt
= get_x509_crt(ctx
)))
921 pgnutls_certificate_free_credentials(creds
);
925 if (!(key
= get_x509_key(key_blob
)))
927 pgnutls_x509_crt_deinit(crt
);
928 pgnutls_certificate_free_credentials(creds
);
932 ret
= pgnutls_certificate_set_x509_key(creds
, &crt
, 1, key
);
933 pgnutls_x509_privkey_deinit(key
);
934 pgnutls_x509_crt_deinit(crt
);
935 if (ret
!= GNUTLS_E_SUCCESS
)
938 pgnutls_certificate_free_credentials(creds
);
942 c
->credentials
= creds
;
946 static void CDECL
schan_free_certificate_credentials(schan_credentials
*c
)
948 pgnutls_certificate_free_credentials(c
->credentials
);
951 static void gnutls_log(int level
, const char *msg
)
953 TRACE("<%d> %s", level
, msg
);
956 static BOOL
gnutls_initialize(void)
961 if ((env_str
= getenv("GNUTLS_SYSTEM_PRIORITY_FILE")))
963 WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str
));
967 WARN("Setting GNUTLS_SYSTEM_PRIORITY_FILE to \"/dev/null\".\n");
968 setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0);
971 libgnutls_handle
= dlopen(SONAME_LIBGNUTLS
, RTLD_NOW
);
972 if (!libgnutls_handle
)
974 ERR_(winediag
)("Failed to load libgnutls, secure connections will not be available.\n");
978 #define LOAD_FUNCPTR(f) \
979 if (!(p##f = dlsym(libgnutls_handle, #f))) \
981 ERR("Failed to load %s\n", #f); \
985 LOAD_FUNCPTR(gnutls_alert_get
)
986 LOAD_FUNCPTR(gnutls_alert_get_name
)
987 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials
)
988 LOAD_FUNCPTR(gnutls_certificate_free_credentials
)
989 LOAD_FUNCPTR(gnutls_certificate_get_peers
)
990 LOAD_FUNCPTR(gnutls_certificate_set_x509_key
)
991 LOAD_FUNCPTR(gnutls_cipher_get
)
992 LOAD_FUNCPTR(gnutls_cipher_get_key_size
)
993 LOAD_FUNCPTR(gnutls_credentials_set
)
994 LOAD_FUNCPTR(gnutls_deinit
)
995 LOAD_FUNCPTR(gnutls_global_deinit
)
996 LOAD_FUNCPTR(gnutls_global_init
)
997 LOAD_FUNCPTR(gnutls_global_set_log_function
)
998 LOAD_FUNCPTR(gnutls_global_set_log_level
)
999 LOAD_FUNCPTR(gnutls_handshake
)
1000 LOAD_FUNCPTR(gnutls_init
)
1001 LOAD_FUNCPTR(gnutls_kx_get
)
1002 LOAD_FUNCPTR(gnutls_mac_get
)
1003 LOAD_FUNCPTR(gnutls_mac_get_key_size
)
1004 LOAD_FUNCPTR(gnutls_perror
)
1005 LOAD_FUNCPTR(gnutls_protocol_get_version
)
1006 LOAD_FUNCPTR(gnutls_priority_set_direct
)
1007 LOAD_FUNCPTR(gnutls_privkey_deinit
)
1008 LOAD_FUNCPTR(gnutls_privkey_init
)
1009 LOAD_FUNCPTR(gnutls_record_get_max_size
);
1010 LOAD_FUNCPTR(gnutls_record_recv
);
1011 LOAD_FUNCPTR(gnutls_record_send
);
1012 LOAD_FUNCPTR(gnutls_server_name_set
)
1013 LOAD_FUNCPTR(gnutls_session_channel_binding
)
1014 LOAD_FUNCPTR(gnutls_transport_get_ptr
)
1015 LOAD_FUNCPTR(gnutls_transport_set_errno
)
1016 LOAD_FUNCPTR(gnutls_transport_set_ptr
)
1017 LOAD_FUNCPTR(gnutls_transport_set_pull_function
)
1018 LOAD_FUNCPTR(gnutls_transport_set_push_function
)
1019 LOAD_FUNCPTR(gnutls_x509_crt_deinit
)
1020 LOAD_FUNCPTR(gnutls_x509_crt_import
)
1021 LOAD_FUNCPTR(gnutls_x509_crt_init
)
1022 LOAD_FUNCPTR(gnutls_x509_privkey_deinit
)
1025 if (!(pgnutls_cipher_get_block_size
= dlsym(libgnutls_handle
, "gnutls_cipher_get_block_size")))
1027 WARN("gnutls_cipher_get_block_size not found\n");
1028 pgnutls_cipher_get_block_size
= compat_cipher_get_block_size
;
1030 if (!(pgnutls_transport_set_pull_timeout_function
= dlsym(libgnutls_handle
, "gnutls_transport_set_pull_timeout_function")))
1032 WARN("gnutls_transport_set_pull_timeout_function not found\n");
1033 pgnutls_transport_set_pull_timeout_function
= compat_gnutls_transport_set_pull_timeout_function
;
1035 if (!(pgnutls_alpn_set_protocols
= dlsym(libgnutls_handle
, "gnutls_alpn_set_protocols")))
1037 WARN("gnutls_alpn_set_protocols not found\n");
1038 pgnutls_alpn_set_protocols
= compat_gnutls_alpn_set_protocols
;
1040 if (!(pgnutls_alpn_get_selected_protocol
= dlsym(libgnutls_handle
, "gnutls_alpn_get_selected_protocol")))
1042 WARN("gnutls_alpn_get_selected_protocol not found\n");
1043 pgnutls_alpn_get_selected_protocol
= compat_gnutls_alpn_get_selected_protocol
;
1045 if (!(pgnutls_dtls_set_mtu
= dlsym(libgnutls_handle
, "gnutls_dtls_set_mtu")))
1047 WARN("gnutls_dtls_set_mtu not found\n");
1048 pgnutls_dtls_set_mtu
= compat_gnutls_dtls_set_mtu
;
1050 if (!(pgnutls_privkey_export_x509
= dlsym(libgnutls_handle
, "gnutls_privkey_export_x509")))
1052 WARN("gnutls_privkey_export_x509 not found\n");
1053 pgnutls_privkey_export_x509
= compat_gnutls_privkey_export_x509
;
1055 if (!(pgnutls_privkey_import_rsa_raw
= dlsym(libgnutls_handle
, "gnutls_privkey_import_rsa_raw")))
1057 WARN("gnutls_privkey_import_rsa_raw not found\n");
1058 pgnutls_privkey_import_rsa_raw
= compat_gnutls_privkey_import_rsa_raw
;
1061 ret
= pgnutls_global_init();
1062 if (ret
!= GNUTLS_E_SUCCESS
)
1064 pgnutls_perror(ret
);
1068 if (TRACE_ON(secur32
))
1070 pgnutls_global_set_log_level(4);
1071 pgnutls_global_set_log_function(gnutls_log
);
1074 check_supported_protocols();
1078 dlclose(libgnutls_handle
);
1079 libgnutls_handle
= NULL
;
1083 static void gnutls_uninitialize(void)
1085 pgnutls_global_deinit();
1086 dlclose(libgnutls_handle
);
1087 libgnutls_handle
= NULL
;
1090 static const struct schan_funcs funcs
=
1092 schan_allocate_certificate_credentials
,
1093 schan_create_session
,
1094 schan_dispose_session
,
1095 schan_free_certificate_credentials
,
1096 schan_get_application_protocol
,
1097 schan_get_connection_info
,
1098 schan_get_enabled_protocols
,
1099 schan_get_key_signature_algorithm
,
1100 schan_get_max_message_size
,
1101 schan_get_session_cipher_block_size
,
1102 schan_get_session_peer_certificate
,
1103 schan_get_unique_channel_binding
,
1107 schan_set_application_protocols
,
1109 schan_set_session_target
,
1110 schan_set_session_transport
,
1113 NTSTATUS CDECL
__wine_init_unix_lib( HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
1117 case DLL_PROCESS_ATTACH
:
1118 if (!gnutls_initialize()) return STATUS_DLL_NOT_FOUND
;
1120 *(const struct schan_funcs
**)ptr_out
= &funcs
;
1122 case DLL_PROCESS_DETACH
:
1123 if (libgnutls_handle
) gnutls_uninitialize();
1126 return STATUS_SUCCESS
;
1129 #endif /* SONAME_LIBGNUTLS */