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
23 #include "wine/port.h"
28 #ifdef SONAME_LIBGNUTLS
29 #include <gnutls/gnutls.h>
30 #include <gnutls/crypto.h>
31 #include <gnutls/abstract.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
);
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
128 #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 5)
129 #define GNUTLS_ALPN_SERVER_PRECEDENCE (1<<1)
132 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher
)
135 case GNUTLS_CIPHER_3DES_CBC
:
137 case GNUTLS_CIPHER_AES_128_CBC
:
138 case GNUTLS_CIPHER_AES_256_CBC
:
140 case GNUTLS_CIPHER_ARCFOUR_128
:
141 case GNUTLS_CIPHER_ARCFOUR_40
:
143 case GNUTLS_CIPHER_DES_CBC
:
145 case GNUTLS_CIPHER_NULL
:
147 case GNUTLS_CIPHER_RC2_40_CBC
:
150 FIXME("Unknown cipher %#x, returning 1\n", cipher
);
155 static void compat_gnutls_transport_set_pull_timeout_function(gnutls_session_t session
,
156 int (*func
)(gnutls_transport_ptr_t
, unsigned int))
161 static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey
, gnutls_x509_privkey_t
*key
)
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
)
174 return GNUTLS_E_UNKNOWN_PK_ALGORITHM
;
177 static int compat_gnutls_alpn_get_selected_protocol(gnutls_session_t session
, gnutls_datum_t
*protocol
)
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
)
187 return GNUTLS_E_INVALID_REQUEST
;
190 static void compat_gnutls_dtls_set_mtu(gnutls_session_t session
, unsigned int mtu
)
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
);
204 pgnutls_transport_set_errno(s
, ret
);
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
);
220 pgnutls_transport_set_errno(s
, ret
);
227 static const struct {
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
;
250 err
= pgnutls_init(&session
, GNUTLS_CLIENT
);
251 if (err
!= GNUTLS_E_SUCCESS
)
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
;
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
;
283 if (schan_get_buffer(t
, &t
->in
, &count
)) return 1;
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
;
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
)
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");
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;
327 *p
++ = disabled
? '-' : '+';
328 strcpy(p
, protocol_priority_flags
[i
].gnutls_flag
);
332 TRACE("Using %s priority\n", debugstr_a(priority
));
333 err
= pgnutls_priority_set_direct(*s
, priority
, NULL
);
334 if (err
!= GNUTLS_E_SUCCESS
)
341 err
= pgnutls_credentials_set(*s
, GNUTLS_CRD_CERTIFICATE
,
342 (gnutls_certificate_credentials_t
)cred
->credentials
);
343 if (err
!= GNUTLS_E_SUCCESS
)
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
);
357 void schan_imp_dispose_session(schan_imp_session session
)
359 gnutls_session_t s
= (gnutls_session_t
)session
;
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
;
383 err
= pgnutls_handshake(s
);
385 case GNUTLS_E_SUCCESS
:
386 TRACE("Handshake completed\n");
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
));
400 case GNUTLS_A_UNRECOGNIZED_NAME
:
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
;
417 return SEC_E_INTERNAL_ERROR
;
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.
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
;
440 FIXME("unknown protocol %d\n", proto
);
445 static ALG_ID
schannel_get_cipher_algid(gnutls_cipher_algorithm_t 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
;
462 FIXME("unknown algorithm %d\n", cipher
);
467 static ALG_ID
schannel_get_mac_algid(gnutls_mac_algorithm_t mac
, gnutls_cipher_algorithm_t cipher
)
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. */
484 case GNUTLS_CIPHER_AES_128_GCM
: return CALG_SHA_256
;
485 case GNUTLS_CIPHER_AES_256_GCM
: return CALG_SHA_384
;
491 FIXME("unknown algorithm %d, cipher %d\n", mac
, cipher
);
496 static ALG_ID
schannel_get_kx_algid(int kx
)
500 case GNUTLS_KX_UNKNOWN
: return 0;
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
;
511 FIXME("unknown algorithm %d\n", kx
);
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;
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
;
555 gnutls_session_t s
= (gnutls_session_t
)session
;
557 rc
= pgnutls_session_channel_binding(s
, GNUTLS_CB_TLS_UNIQUE
, &datum
);
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
;
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
);
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
);
591 case GNUTLS_KX_UNKNOWN
: return 0;
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
;
598 FIXME("unknown algorithm %d\n", kx
);
603 SECURITY_STATUS
schan_imp_get_session_peer_certificate(schan_imp_session session
, HCERTSTORE store
,
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
;
612 datum
= pgnutls_certificate_get_peers(s
, &list_size
);
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
);
621 CertFreeCertificateContext(cert
);
622 return GetLastError();
630 SECURITY_STATUS
schan_imp_send(schan_imp_session session
, const void *buffer
,
633 gnutls_session_t s
= (gnutls_session_t
)session
;
634 SSIZE_T ret
, total
= 0;
638 ret
= pgnutls_record_send(s
, (const char *)buffer
+ total
, *length
- total
);
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
);
650 if (schan_get_buffer(t
, &t
->out
, &count
)) continue;
651 return SEC_I_CONTINUE_NEEDED
;
656 return SEC_E_INTERNAL_ERROR
;
661 SECURITY_STATUS
schan_imp_recv(schan_imp_session session
, void *buffer
,
664 gnutls_session_t s
= (gnutls_session_t
)session
;
668 ret
= pgnutls_record_recv(s
, buffer
, *length
);
672 else if (ret
== GNUTLS_E_AGAIN
)
674 struct schan_transport
*t
= (struct schan_transport
*)pgnutls_transport_get_ptr(s
);
677 if (schan_get_buffer(t
, &t
->in
, &count
))
680 return SEC_I_CONTINUE_NEEDED
;
682 else if (ret
== GNUTLS_E_REHANDSHAKE
)
684 TRACE("Rehandshake requested\n");
685 return SEC_I_RENEGOTIATE
;
690 return SEC_E_INTERNAL_ERROR
;
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;
702 len
= buffer
[offset
++];
704 if (!len
|| len
> buflen
) return 0;
707 list
[count
].data
= &buffer
[offset
];
708 list
[count
].size
= len
;
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
;
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
);
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))
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
));
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
);
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
))
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
))))
808 strcpyW(ret
, rsabaseW
);
809 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
+ strlenW(ret
), len
);
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
))
820 if (!(ret
= heap_alloc(sizeof(rsabaseW
) + strlenW(prov
->pwszContainerName
) * sizeof(WCHAR
))))
825 strcpyW(ret
, rsabaseW
);
826 strcatW(ret
, prov
->pwszContainerName
);
830 if (!ret
&& GetUserNameW(username
, &len
) && (ret
= heap_alloc(sizeof(rsabaseW
) + len
* sizeof(WCHAR
))))
832 strcpyW(ret
, rsabaseW
);
833 strcatW(ret
, username
);
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
;
852 if (!(path
= get_key_container_path(ctx
))) return NULL
;
853 if (RegOpenKeyExW(HKEY_CURRENT_USER
, path
, 0, KEY_READ
, &hkey
))
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
;
868 if (!(buf
= heap_alloc(len
+ MAX_LEAD_BYTES
)))
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
;
893 static inline void reverse_bytes(BYTE
*buf
, ULONG len
)
897 for (i
= 0; i
< len
/ 2; 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
)
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
);
916 *buflen
-= 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
;
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)
954 if ((ret
= pgnutls_privkey_import_rsa_raw(key
, &m
, &e
, &d
, &p
, &q
, &u
, &e1
, &e2
)) < 0)
960 if ((ret
= pgnutls_privkey_export_x509(key
, &x509key
)) < 0)
967 pgnutls_privkey_deinit(key
);
971 static gnutls_x509_crt_t
get_x509_crt(const CERT_CONTEXT
*ctx
)
974 gnutls_x509_crt_t crt
;
977 if (!ctx
) return FALSE
;
978 if (ctx
->dwCertEncodingType
!= X509_ASN_ENCODING
)
980 FIXME("encoding type %u not supported\n", ctx
->dwCertEncodingType
);
984 if ((ret
= pgnutls_x509_crt_init(&crt
)) < 0)
990 data
.data
= ctx
->pbCertEncoded
;
991 data
.size
= ctx
->cbCertEncoded
;
992 if ((ret
= pgnutls_x509_crt_import(crt
, &data
, GNUTLS_X509_FMT_DER
)) < 0)
995 pgnutls_x509_crt_deinit(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
;
1009 ret
= pgnutls_certificate_allocate_credentials(&creds
);
1010 if (ret
!= GNUTLS_E_SUCCESS
)
1012 pgnutls_perror(ret
);
1018 c
->credentials
= creds
;
1022 if (!(crt
= get_x509_crt(ctx
)))
1024 pgnutls_certificate_free_credentials(creds
);
1028 if (!(key
= get_x509_key(ctx
)))
1030 pgnutls_x509_crt_deinit(crt
);
1031 pgnutls_certificate_free_credentials(creds
);
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
);
1045 c
->credentials
= creds
;
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
;
1064 if ((env_str
= getenv("GNUTLS_SYSTEM_PRIORITY_FILE")))
1066 WARN("GNUTLS_SYSTEM_PRIORITY_FILE is %s.\n", debugstr_a(env_str
));
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");
1081 #define LOAD_FUNCPTR(f) \
1082 if (!(p##f = dlsym(libgnutls_handle, #f))) \
1084 ERR("Failed to load %s\n", #f); \
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
)
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
);
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();
1181 dlclose(libgnutls_handle
);
1182 libgnutls_handle
= NULL
;
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 */