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.2.0. */
54 static int (*pgnutls_alpn_get_selected_protocol
)(gnutls_session_t
, gnutls_datum_t
*);
55 static int (*pgnutls_alpn_set_protocols
)(gnutls_session_t
, const gnutls_datum_t
*,
56 unsigned, unsigned int);
58 /* Not present in gnutls version < 3.3.0. */
59 static int (*pgnutls_privkey_import_rsa_raw
)(gnutls_privkey_t
, const gnutls_datum_t
*,
60 const gnutls_datum_t
*, const gnutls_datum_t
*,
61 const gnutls_datum_t
*, const gnutls_datum_t
*,
62 const gnutls_datum_t
*, const gnutls_datum_t
*,
63 const gnutls_datum_t
*);
65 /* Not present in gnutls version < 3.4.0. */
66 static int (*pgnutls_privkey_export_x509
)(gnutls_privkey_t
, gnutls_x509_privkey_t
*);
68 static void *libgnutls_handle
;
69 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
70 MAKE_FUNCPTR(gnutls_alert_get
);
71 MAKE_FUNCPTR(gnutls_alert_get_name
);
72 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials
);
73 MAKE_FUNCPTR(gnutls_certificate_free_credentials
);
74 MAKE_FUNCPTR(gnutls_certificate_get_peers
);
75 MAKE_FUNCPTR(gnutls_certificate_set_x509_key
);
76 MAKE_FUNCPTR(gnutls_cipher_get
);
77 MAKE_FUNCPTR(gnutls_cipher_get_key_size
);
78 MAKE_FUNCPTR(gnutls_credentials_set
);
79 MAKE_FUNCPTR(gnutls_deinit
);
80 MAKE_FUNCPTR(gnutls_global_deinit
);
81 MAKE_FUNCPTR(gnutls_global_init
);
82 MAKE_FUNCPTR(gnutls_global_set_log_function
);
83 MAKE_FUNCPTR(gnutls_global_set_log_level
);
84 MAKE_FUNCPTR(gnutls_handshake
);
85 MAKE_FUNCPTR(gnutls_init
);
86 MAKE_FUNCPTR(gnutls_kx_get
);
87 MAKE_FUNCPTR(gnutls_mac_get
);
88 MAKE_FUNCPTR(gnutls_mac_get_key_size
);
89 MAKE_FUNCPTR(gnutls_perror
);
90 MAKE_FUNCPTR(gnutls_protocol_get_version
);
91 MAKE_FUNCPTR(gnutls_priority_set_direct
);
92 MAKE_FUNCPTR(gnutls_privkey_deinit
);
93 MAKE_FUNCPTR(gnutls_privkey_init
);
94 MAKE_FUNCPTR(gnutls_record_get_max_size
);
95 MAKE_FUNCPTR(gnutls_record_recv
);
96 MAKE_FUNCPTR(gnutls_record_send
);
97 MAKE_FUNCPTR(gnutls_server_name_set
);
98 MAKE_FUNCPTR(gnutls_transport_get_ptr
);
99 MAKE_FUNCPTR(gnutls_transport_set_errno
);
100 MAKE_FUNCPTR(gnutls_transport_set_ptr
);
101 MAKE_FUNCPTR(gnutls_transport_set_pull_function
);
102 MAKE_FUNCPTR(gnutls_transport_set_push_function
);
103 MAKE_FUNCPTR(gnutls_x509_crt_deinit
);
104 MAKE_FUNCPTR(gnutls_x509_crt_import
);
105 MAKE_FUNCPTR(gnutls_x509_crt_init
);
106 MAKE_FUNCPTR(gnutls_x509_privkey_deinit
);
109 #if GNUTLS_VERSION_MAJOR < 3
110 #define GNUTLS_CIPHER_AES_192_CBC 92
111 #define GNUTLS_CIPHER_AES_128_GCM 93
112 #define GNUTLS_CIPHER_AES_256_GCM 94
114 #define GNUTLS_MAC_AEAD 200
116 #define GNUTLS_KX_ANON_ECDH 11
117 #define GNUTLS_KX_ECDHE_RSA 12
118 #define GNUTLS_KX_ECDHE_ECDSA 13
119 #define GNUTLS_KX_ECDHE_PSK 14
122 #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 5)
123 #define GNUTLS_ALPN_SERVER_PRECEDENCE (1<<1)
126 static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher
)
129 case GNUTLS_CIPHER_3DES_CBC
:
131 case GNUTLS_CIPHER_AES_128_CBC
:
132 case GNUTLS_CIPHER_AES_256_CBC
:
134 case GNUTLS_CIPHER_ARCFOUR_128
:
135 case GNUTLS_CIPHER_ARCFOUR_40
:
137 case GNUTLS_CIPHER_DES_CBC
:
139 case GNUTLS_CIPHER_NULL
:
141 case GNUTLS_CIPHER_RC2_40_CBC
:
144 FIXME("Unknown cipher %#x, returning 1\n", cipher
);
149 static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey
, gnutls_x509_privkey_t
*key
)
152 return GNUTLS_E_UNKNOWN_PK_ALGORITHM
;
155 static int compat_gnutls_privkey_import_rsa_raw(gnutls_privkey_t key
, const gnutls_datum_t
*p1
,
156 const gnutls_datum_t
*p2
, const gnutls_datum_t
*p3
,
157 const gnutls_datum_t
*p4
, const gnutls_datum_t
*p5
,
158 const gnutls_datum_t
*p6
, const gnutls_datum_t
*p7
,
159 const gnutls_datum_t
*p8
)
162 return GNUTLS_E_UNKNOWN_PK_ALGORITHM
;
165 static int compat_gnutls_alpn_get_selected_protocol(gnutls_session_t session
, gnutls_datum_t
*protocol
)
168 return GNUTLS_E_INVALID_REQUEST
;
171 static int compat_gnutls_alpn_set_protocols(gnutls_session_t session
, const gnutls_datum_t
*protocols
,
172 unsigned size
, unsigned int flags
)
175 return GNUTLS_E_INVALID_REQUEST
;
178 static ssize_t
schan_pull_adapter(gnutls_transport_ptr_t transport
,
179 void *buff
, size_t buff_len
)
181 struct schan_transport
*t
= (struct schan_transport
*)transport
;
182 gnutls_session_t s
= (gnutls_session_t
)schan_session_for_transport(t
);
184 int ret
= schan_pull(transport
, buff
, &buff_len
);
187 pgnutls_transport_set_errno(s
, ret
);
194 static ssize_t
schan_push_adapter(gnutls_transport_ptr_t transport
,
195 const void *buff
, size_t buff_len
)
197 struct schan_transport
*t
= (struct schan_transport
*)transport
;
198 gnutls_session_t s
= (gnutls_session_t
)schan_session_for_transport(t
);
200 int ret
= schan_push(transport
, buff
, &buff_len
);
203 pgnutls_transport_set_errno(s
, ret
);
210 static const struct {
212 const char *gnutls_flag
;
213 } protocol_priority_flags
[] = {
214 {SP_PROT_TLS1_3_CLIENT
, "VERS-TLS1.3"},
215 {SP_PROT_TLS1_2_CLIENT
, "VERS-TLS1.2"},
216 {SP_PROT_TLS1_1_CLIENT
, "VERS-TLS1.1"},
217 {SP_PROT_TLS1_0_CLIENT
, "VERS-TLS1.0"},
218 {SP_PROT_SSL3_CLIENT
, "VERS-SSL3.0"}
219 /* {SP_PROT_SSL2_CLIENT} is not supported by GnuTLS */
222 static DWORD supported_protocols
;
224 static void check_supported_protocols(void)
226 gnutls_session_t session
;
231 err
= pgnutls_init(&session
, GNUTLS_CLIENT
);
232 if (err
!= GNUTLS_E_SUCCESS
)
238 for(i
= 0; i
< ARRAY_SIZE(protocol_priority_flags
); i
++)
240 sprintf(priority
, "NORMAL:-%s", protocol_priority_flags
[i
].gnutls_flag
);
241 err
= pgnutls_priority_set_direct(session
, priority
, NULL
);
242 if (err
== GNUTLS_E_SUCCESS
)
244 TRACE("%s is supported\n", protocol_priority_flags
[i
].gnutls_flag
);
245 supported_protocols
|= protocol_priority_flags
[i
].enable_flag
;
248 TRACE("%s is not supported\n", protocol_priority_flags
[i
].gnutls_flag
);
251 pgnutls_deinit(session
);
254 DWORD
schan_imp_enabled_protocols(void)
256 return supported_protocols
;
259 BOOL
schan_imp_create_session(schan_imp_session
*session
, schan_credentials
*cred
)
261 gnutls_session_t
*s
= (gnutls_session_t
*)session
;
262 char priority
[128] = "NORMAL:%LATEST_RECORD_VERSION", *p
;
263 BOOL using_vers_all
= FALSE
, disabled
;
266 int err
= pgnutls_init(s
, cred
->credential_use
== SECPKG_CRED_INBOUND
? GNUTLS_SERVER
: GNUTLS_CLIENT
);
267 if (err
!= GNUTLS_E_SUCCESS
)
273 p
= priority
+ strlen(priority
);
275 /* VERS-ALL is nice to use for forward compatibility. It was introduced before support for TLS1.3,
276 * so if TLS1.3 is supported, we may safely use it. Otherwise explicitly disable all known
277 * disabled protocols. */
278 if (supported_protocols
& SP_PROT_TLS1_3_CLIENT
)
280 strcpy(p
, ":-VERS-ALL");
282 using_vers_all
= TRUE
;
285 for (i
= 0; i
< ARRAY_SIZE(protocol_priority_flags
); i
++)
287 if (!(supported_protocols
& protocol_priority_flags
[i
].enable_flag
)) continue;
289 disabled
= !(cred
->enabled_protocols
& protocol_priority_flags
[i
].enable_flag
);
290 if (using_vers_all
&& disabled
) continue;
293 *p
++ = disabled
? '-' : '+';
294 strcpy(p
, protocol_priority_flags
[i
].gnutls_flag
);
298 TRACE("Using %s priority\n", debugstr_a(priority
));
299 err
= pgnutls_priority_set_direct(*s
, priority
, NULL
);
300 if (err
!= GNUTLS_E_SUCCESS
)
307 err
= pgnutls_credentials_set(*s
, GNUTLS_CRD_CERTIFICATE
,
308 (gnutls_certificate_credentials_t
)cred
->credentials
);
309 if (err
!= GNUTLS_E_SUCCESS
)
316 pgnutls_transport_set_pull_function(*s
, schan_pull_adapter
);
317 pgnutls_transport_set_push_function(*s
, schan_push_adapter
);
322 void schan_imp_dispose_session(schan_imp_session session
)
324 gnutls_session_t s
= (gnutls_session_t
)session
;
328 void schan_imp_set_session_transport(schan_imp_session session
,
329 struct schan_transport
*t
)
331 gnutls_session_t s
= (gnutls_session_t
)session
;
332 pgnutls_transport_set_ptr(s
, (gnutls_transport_ptr_t
)t
);
335 void schan_imp_set_session_target(schan_imp_session session
, const char *target
)
337 gnutls_session_t s
= (gnutls_session_t
)session
;
339 pgnutls_server_name_set( s
, GNUTLS_NAME_DNS
, target
, strlen(target
) );
342 SECURITY_STATUS
schan_imp_handshake(schan_imp_session session
)
344 gnutls_session_t s
= (gnutls_session_t
)session
;
348 err
= pgnutls_handshake(s
);
350 case GNUTLS_E_SUCCESS
:
351 TRACE("Handshake completed\n");
355 TRACE("Continue...\n");
356 return SEC_I_CONTINUE_NEEDED
;
358 case GNUTLS_E_WARNING_ALERT_RECEIVED
:
360 gnutls_alert_description_t alert
= pgnutls_alert_get(s
);
362 WARN("WARNING ALERT: %d %s\n", alert
, pgnutls_alert_get_name(alert
));
365 case GNUTLS_A_UNRECOGNIZED_NAME
:
369 return SEC_E_INTERNAL_ERROR
;
373 case GNUTLS_E_FATAL_ALERT_RECEIVED
:
375 gnutls_alert_description_t alert
= pgnutls_alert_get(s
);
376 WARN("FATAL ALERT: %d %s\n", alert
, pgnutls_alert_get_name(alert
));
377 return SEC_E_INTERNAL_ERROR
;
382 return SEC_E_INTERNAL_ERROR
;
390 static DWORD
schannel_get_protocol(gnutls_protocol_t proto
)
392 /* FIXME: currently schannel only implements client connections, but
393 * there's no reason it couldn't be used for servers as well. The
394 * context doesn't tell us which it is, so assume client for now.
398 case GNUTLS_SSL3
: return SP_PROT_SSL3_CLIENT
;
399 case GNUTLS_TLS1_0
: return SP_PROT_TLS1_0_CLIENT
;
400 case GNUTLS_TLS1_1
: return SP_PROT_TLS1_1_CLIENT
;
401 case GNUTLS_TLS1_2
: return SP_PROT_TLS1_2_CLIENT
;
403 FIXME("unknown protocol %d\n", proto
);
408 static ALG_ID
schannel_get_cipher_algid(gnutls_cipher_algorithm_t cipher
)
412 case GNUTLS_CIPHER_UNKNOWN
:
413 case GNUTLS_CIPHER_NULL
: return 0;
414 case GNUTLS_CIPHER_ARCFOUR_40
:
415 case GNUTLS_CIPHER_ARCFOUR_128
: return CALG_RC4
;
416 case GNUTLS_CIPHER_DES_CBC
: return CALG_DES
;
417 case GNUTLS_CIPHER_3DES_CBC
: return CALG_3DES
;
418 case GNUTLS_CIPHER_AES_128_CBC
:
419 case GNUTLS_CIPHER_AES_128_GCM
: return CALG_AES_128
;
420 case GNUTLS_CIPHER_AES_192_CBC
: return CALG_AES_192
;
421 case GNUTLS_CIPHER_AES_256_GCM
:
422 case GNUTLS_CIPHER_AES_256_CBC
: return CALG_AES_256
;
423 case GNUTLS_CIPHER_RC2_40_CBC
: return CALG_RC2
;
425 FIXME("unknown algorithm %d\n", cipher
);
430 static ALG_ID
schannel_get_mac_algid(gnutls_mac_algorithm_t mac
, gnutls_cipher_algorithm_t cipher
)
434 case GNUTLS_MAC_UNKNOWN
:
435 case GNUTLS_MAC_NULL
: return 0;
436 case GNUTLS_MAC_MD2
: return CALG_MD2
;
437 case GNUTLS_MAC_MD5
: return CALG_MD5
;
438 case GNUTLS_MAC_SHA1
: return CALG_SHA1
;
439 case GNUTLS_MAC_SHA256
: return CALG_SHA_256
;
440 case GNUTLS_MAC_SHA384
: return CALG_SHA_384
;
441 case GNUTLS_MAC_SHA512
: return CALG_SHA_512
;
442 case GNUTLS_MAC_AEAD
:
443 /* When using AEAD (such as GCM), we return PRF algorithm instead
444 which is defined in RFC 5289. */
447 case GNUTLS_CIPHER_AES_128_GCM
: return CALG_SHA_256
;
448 case GNUTLS_CIPHER_AES_256_GCM
: return CALG_SHA_384
;
454 FIXME("unknown algorithm %d, cipher %d\n", mac
, cipher
);
459 static ALG_ID
schannel_get_kx_algid(int kx
)
463 case GNUTLS_KX_UNKNOWN
: return 0;
465 case GNUTLS_KX_RSA_EXPORT
: return CALG_RSA_KEYX
;
466 case GNUTLS_KX_DHE_PSK
:
467 case GNUTLS_KX_DHE_DSS
:
468 case GNUTLS_KX_DHE_RSA
: return CALG_DH_EPHEM
;
469 case GNUTLS_KX_ANON_ECDH
: return CALG_ECDH
;
470 case GNUTLS_KX_ECDHE_RSA
:
471 case GNUTLS_KX_ECDHE_PSK
:
472 case GNUTLS_KX_ECDHE_ECDSA
: return CALG_ECDH_EPHEM
;
474 FIXME("unknown algorithm %d\n", kx
);
479 unsigned int schan_imp_get_session_cipher_block_size(schan_imp_session session
)
481 gnutls_session_t s
= (gnutls_session_t
)session
;
482 return pgnutls_cipher_get_block_size(pgnutls_cipher_get(s
));
485 unsigned int schan_imp_get_max_message_size(schan_imp_session session
)
487 return pgnutls_record_get_max_size((gnutls_session_t
)session
);
490 SECURITY_STATUS
schan_imp_get_connection_info(schan_imp_session session
,
491 SecPkgContext_ConnectionInfo
*info
)
493 gnutls_session_t s
= (gnutls_session_t
)session
;
494 gnutls_protocol_t proto
= pgnutls_protocol_get_version(s
);
495 gnutls_cipher_algorithm_t alg
= pgnutls_cipher_get(s
);
496 gnutls_mac_algorithm_t mac
= pgnutls_mac_get(s
);
497 gnutls_kx_algorithm_t kx
= pgnutls_kx_get(s
);
499 info
->dwProtocol
= schannel_get_protocol(proto
);
500 info
->aiCipher
= schannel_get_cipher_algid(alg
);
501 info
->dwCipherStrength
= pgnutls_cipher_get_key_size(alg
) * 8;
502 info
->aiHash
= schannel_get_mac_algid(mac
, alg
);
503 info
->dwHashStrength
= pgnutls_mac_get_key_size(mac
) * 8;
504 info
->aiExch
= schannel_get_kx_algid(kx
);
505 /* FIXME: info->dwExchStrength? */
506 info
->dwExchStrength
= 0;
510 ALG_ID
schan_imp_get_key_signature_algorithm(schan_imp_session session
)
512 gnutls_session_t s
= (gnutls_session_t
)session
;
513 gnutls_kx_algorithm_t kx
= pgnutls_kx_get(s
);
515 TRACE("(%p)\n", session
);
519 case GNUTLS_KX_UNKNOWN
: return 0;
521 case GNUTLS_KX_RSA_EXPORT
:
522 case GNUTLS_KX_DHE_RSA
:
523 case GNUTLS_KX_ECDHE_RSA
: return CALG_RSA_SIGN
;
524 case GNUTLS_KX_ECDHE_ECDSA
: return CALG_ECDSA
;
526 FIXME("unknown algorithm %d\n", kx
);
531 SECURITY_STATUS
schan_imp_get_session_peer_certificate(schan_imp_session session
, HCERTSTORE store
,
534 gnutls_session_t s
= (gnutls_session_t
)session
;
535 PCCERT_CONTEXT cert
= NULL
;
536 const gnutls_datum_t
*datum
;
537 unsigned list_size
, i
;
540 datum
= pgnutls_certificate_get_peers(s
, &list_size
);
542 return SEC_E_INTERNAL_ERROR
;
544 for(i
= 0; i
< list_size
; i
++) {
545 res
= CertAddEncodedCertificateToStore(store
, X509_ASN_ENCODING
, datum
[i
].data
, datum
[i
].size
,
546 CERT_STORE_ADD_REPLACE_EXISTING
, i
? NULL
: &cert
);
549 CertFreeCertificateContext(cert
);
550 return GetLastError();
558 SECURITY_STATUS
schan_imp_send(schan_imp_session session
, const void *buffer
,
561 gnutls_session_t s
= (gnutls_session_t
)session
;
562 SSIZE_T ret
, total
= 0;
566 ret
= pgnutls_record_send(s
, (const char *)buffer
+ total
, *length
- total
);
570 TRACE( "sent %ld now %ld/%ld\n", ret
, total
, *length
);
571 if (total
== *length
) return SEC_E_OK
;
573 else if (ret
== GNUTLS_E_AGAIN
)
575 struct schan_transport
*t
= (struct schan_transport
*)pgnutls_transport_get_ptr(s
);
578 if (schan_get_buffer(t
, &t
->out
, &count
)) continue;
579 return SEC_I_CONTINUE_NEEDED
;
584 return SEC_E_INTERNAL_ERROR
;
589 SECURITY_STATUS
schan_imp_recv(schan_imp_session session
, void *buffer
,
592 gnutls_session_t s
= (gnutls_session_t
)session
;
596 ret
= pgnutls_record_recv(s
, buffer
, *length
);
600 else if (ret
== GNUTLS_E_AGAIN
)
602 struct schan_transport
*t
= (struct schan_transport
*)pgnutls_transport_get_ptr(s
);
605 if (schan_get_buffer(t
, &t
->in
, &count
))
608 return SEC_I_CONTINUE_NEEDED
;
610 else if (ret
== GNUTLS_E_REHANDSHAKE
)
612 TRACE("Rehandshake requested\n");
613 return SEC_I_RENEGOTIATE
;
618 return SEC_E_INTERNAL_ERROR
;
624 static unsigned int parse_alpn_protocol_list(unsigned char *buffer
, unsigned int buflen
, gnutls_datum_t
*list
)
626 unsigned int len
, offset
= 0, count
= 0;
630 len
= buffer
[offset
++];
632 if (!len
|| len
> buflen
) return 0;
635 list
[count
].data
= &buffer
[offset
];
636 list
[count
].size
= len
;
646 void schan_imp_set_application_protocols(schan_imp_session session
, unsigned char *buffer
, unsigned int buflen
)
648 gnutls_session_t s
= (gnutls_session_t
)session
;
649 unsigned int extension_len
, extension
, count
= 0, offset
= 0;
650 unsigned short list_len
;
651 gnutls_datum_t
*protocols
;
654 if (sizeof(extension_len
) > buflen
) return;
655 extension_len
= *(unsigned int *)&buffer
[offset
];
656 offset
+= sizeof(extension_len
);
658 if (offset
+ sizeof(extension
) > buflen
) return;
659 extension
= *(unsigned int *)&buffer
[offset
];
660 if (extension
!= SecApplicationProtocolNegotiationExt_ALPN
)
662 FIXME("extension %u not supported\n", extension
);
665 offset
+= sizeof(extension
);
667 if (offset
+ sizeof(list_len
) > buflen
) return;
668 list_len
= *(unsigned short *)&buffer
[offset
];
669 offset
+= sizeof(list_len
);
671 if (offset
+ list_len
> buflen
) return;
672 count
= parse_alpn_protocol_list(&buffer
[offset
], list_len
, NULL
);
673 if (!count
|| !(protocols
= heap_alloc(count
* sizeof(*protocols
)))) return;
675 parse_alpn_protocol_list(&buffer
[offset
], list_len
, protocols
);
676 if ((ret
= pgnutls_alpn_set_protocols(s
, protocols
, count
, GNUTLS_ALPN_SERVER_PRECEDENCE
) < 0))
681 heap_free(protocols
);
684 SECURITY_STATUS
schan_imp_get_application_protocol(schan_imp_session session
,
685 SecPkgContext_ApplicationProtocol
*protocol
)
687 gnutls_session_t s
= (gnutls_session_t
)session
;
688 gnutls_datum_t selected
;
690 memset(protocol
, 0, sizeof(*protocol
));
691 if (pgnutls_alpn_get_selected_protocol(s
, &selected
) < 0) return SEC_E_OK
;
693 if (selected
.size
<= sizeof(protocol
->ProtocolId
))
695 protocol
->ProtoNegoStatus
= SecApplicationProtocolNegotiationStatus_Success
;
696 protocol
->ProtoNegoExt
= SecApplicationProtocolNegotiationExt_ALPN
;
697 protocol
->ProtocolIdSize
= selected
.size
;
698 memcpy(protocol
->ProtocolId
, selected
.data
, selected
.size
);
699 TRACE("returning %s\n", debugstr_an((const char *)selected
.data
, selected
.size
));
704 static WCHAR
*get_key_container_path(const CERT_CONTEXT
*ctx
)
706 static const WCHAR rsabaseW
[] =
707 {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','C','r','y','p','t','o','\\','R','S','A','\\',0};
708 CERT_KEY_CONTEXT keyctx
;
709 DWORD size
= sizeof(keyctx
), prov_size
= 0;
710 CRYPT_KEY_PROV_INFO
*prov
;
711 WCHAR username
[UNLEN
+ 1], *ret
= NULL
;
712 DWORD len
= ARRAY_SIZE(username
);
714 if (CertGetCertificateContextProperty(ctx
, CERT_KEY_CONTEXT_PROP_ID
, &keyctx
, &size
))
717 if (!CryptGetProvParam(keyctx
.hCryptProv
, PP_CONTAINER
, NULL
, &size
, 0)) return NULL
;
718 if (!(str
= heap_alloc(size
))) return NULL
;
719 if (!CryptGetProvParam(keyctx
.hCryptProv
, PP_CONTAINER
, (BYTE
*)str
, &size
, 0)) return NULL
;
721 len
= MultiByteToWideChar(CP_ACP
, 0, str
, -1, NULL
, 0);
722 if (!(ret
= heap_alloc(sizeof(rsabaseW
) + len
* sizeof(WCHAR
))))
727 strcpyW(ret
, rsabaseW
);
728 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
+ strlenW(ret
), len
);
731 else if (CertGetCertificateContextProperty(ctx
, CERT_KEY_PROV_INFO_PROP_ID
, NULL
, &prov_size
))
733 if (!(prov
= heap_alloc(prov_size
))) return NULL
;
734 if (!CertGetCertificateContextProperty(ctx
, CERT_KEY_PROV_INFO_PROP_ID
, prov
, &prov_size
))
739 if (!(ret
= heap_alloc(sizeof(rsabaseW
) + strlenW(prov
->pwszContainerName
) * sizeof(WCHAR
))))
744 strcpyW(ret
, rsabaseW
);
745 strcatW(ret
, prov
->pwszContainerName
);
749 if (!ret
&& GetUserNameW(username
, &len
) && (ret
= heap_alloc(sizeof(rsabaseW
) + len
* sizeof(WCHAR
))))
751 strcpyW(ret
, rsabaseW
);
752 strcatW(ret
, username
);
758 #define MAX_LEAD_BYTES 8
759 static BYTE
*get_key_blob(const CERT_CONTEXT
*ctx
, ULONG
*size
)
761 static const WCHAR keyexchangeW
[] =
762 {'K','e','y','E','x','c','h','a','n','g','e','K','e','y','P','a','i','r',0};
763 static const WCHAR signatureW
[] =
764 {'S','i','g','n','a','t','u','r','e','K','e','y','P','a','i','r',0};
765 BYTE
*buf
, *ret
= NULL
;
766 DATA_BLOB blob_in
, blob_out
;
767 DWORD spec
= 0, type
, len
;
771 if (!(path
= get_key_container_path(ctx
))) return NULL
;
772 if (RegOpenKeyExW(HKEY_CURRENT_USER
, path
, 0, KEY_READ
, &hkey
))
779 if (!RegQueryValueExW(hkey
, keyexchangeW
, 0, &type
, NULL
, &len
)) spec
= AT_KEYEXCHANGE
;
780 else if (!RegQueryValueExW(hkey
, signatureW
, 0, &type
, NULL
, &len
)) spec
= AT_SIGNATURE
;
787 if (!(buf
= heap_alloc(len
+ MAX_LEAD_BYTES
)))
793 if (!RegQueryValueExW(hkey
, (spec
== AT_KEYEXCHANGE
) ? keyexchangeW
: signatureW
, 0, &type
, buf
, &len
))
795 blob_in
.pbData
= buf
;
796 blob_in
.cbData
= len
;
797 if (CryptUnprotectData(&blob_in
, NULL
, NULL
, NULL
, NULL
, 0, &blob_out
))
799 assert(blob_in
.cbData
>= blob_out
.cbData
);
800 memcpy(buf
, blob_out
.pbData
, blob_out
.cbData
);
801 LocalFree(blob_out
.pbData
);
802 *size
= blob_out
.cbData
+ MAX_LEAD_BYTES
;
812 static inline void reverse_bytes(BYTE
*buf
, ULONG len
)
816 for (i
= 0; i
< len
/ 2; i
++)
819 buf
[i
] = buf
[len
- i
- 1];
820 buf
[len
- i
- 1] = tmp
;
824 static ULONG
set_component(gnutls_datum_t
*comp
, BYTE
*data
, ULONG len
, ULONG
*buflen
)
828 reverse_bytes(comp
->data
, comp
->size
);
829 if (comp
->data
[0] & 0x80) /* add leading 0 byte if most significant bit is set */
831 memmove(comp
->data
+ 1, comp
->data
, *buflen
);
835 *buflen
-= comp
->size
;
839 static gnutls_x509_privkey_t
get_x509_key(const CERT_CONTEXT
*ctx
)
841 gnutls_privkey_t key
= NULL
;
842 gnutls_x509_privkey_t x509key
= NULL
;
843 gnutls_datum_t m
, e
, d
, p
, q
, u
, e1
, e2
;
849 if (!(buffer
= get_key_blob(ctx
, &size
))) return NULL
;
850 if (size
< sizeof(BLOBHEADER
)) goto done
;
852 rsakey
= (RSAPUBKEY
*)(buffer
+ sizeof(BLOBHEADER
));
853 TRACE("RSA key bitlen %u pubexp %u\n", rsakey
->bitlen
, rsakey
->pubexp
);
855 size
-= sizeof(BLOBHEADER
) + FIELD_OFFSET(RSAPUBKEY
, pubexp
);
856 set_component(&e
, (BYTE
*)&rsakey
->pubexp
, sizeof(rsakey
->pubexp
), &size
);
858 ptr
= (BYTE
*)(rsakey
+ 1);
859 ptr
+= set_component(&m
, ptr
, rsakey
->bitlen
/ 8, &size
);
860 ptr
+= set_component(&p
, ptr
, rsakey
->bitlen
/ 16, &size
);
861 ptr
+= set_component(&q
, ptr
, rsakey
->bitlen
/ 16, &size
);
862 ptr
+= set_component(&e1
, ptr
, rsakey
->bitlen
/ 16, &size
);
863 ptr
+= set_component(&e2
, ptr
, rsakey
->bitlen
/ 16, &size
);
864 ptr
+= set_component(&u
, ptr
, rsakey
->bitlen
/ 16, &size
);
865 ptr
+= set_component(&d
, ptr
, rsakey
->bitlen
/ 8, &size
);
867 if ((ret
= pgnutls_privkey_init(&key
)) < 0)
873 if ((ret
= pgnutls_privkey_import_rsa_raw(key
, &m
, &e
, &d
, &p
, &q
, &u
, &e1
, &e2
)) < 0)
879 if ((ret
= pgnutls_privkey_export_x509(key
, &x509key
)) < 0)
886 pgnutls_privkey_deinit(key
);
890 static gnutls_x509_crt_t
get_x509_crt(const CERT_CONTEXT
*ctx
)
893 gnutls_x509_crt_t crt
;
896 if (!ctx
) return FALSE
;
897 if (ctx
->dwCertEncodingType
!= X509_ASN_ENCODING
)
899 FIXME("encoding type %u not supported\n", ctx
->dwCertEncodingType
);
903 if ((ret
= pgnutls_x509_crt_init(&crt
)) < 0)
909 data
.data
= ctx
->pbCertEncoded
;
910 data
.size
= ctx
->cbCertEncoded
;
911 if ((ret
= pgnutls_x509_crt_import(crt
, &data
, GNUTLS_X509_FMT_DER
)) < 0)
914 pgnutls_x509_crt_deinit(crt
);
921 BOOL
schan_imp_allocate_certificate_credentials(schan_credentials
*c
, const CERT_CONTEXT
*ctx
)
923 gnutls_certificate_credentials_t creds
;
924 gnutls_x509_crt_t crt
;
925 gnutls_x509_privkey_t key
;
928 ret
= pgnutls_certificate_allocate_credentials(&creds
);
929 if (ret
!= GNUTLS_E_SUCCESS
)
937 c
->credentials
= creds
;
941 if (!(crt
= get_x509_crt(ctx
)))
943 pgnutls_certificate_free_credentials(creds
);
947 if (!(key
= get_x509_key(ctx
)))
949 pgnutls_x509_crt_deinit(crt
);
950 pgnutls_certificate_free_credentials(creds
);
954 ret
= pgnutls_certificate_set_x509_key(creds
, &crt
, 1, key
);
955 pgnutls_x509_privkey_deinit(key
);
956 pgnutls_x509_crt_deinit(crt
);
957 if (ret
!= GNUTLS_E_SUCCESS
)
960 pgnutls_certificate_free_credentials(creds
);
964 c
->credentials
= creds
;
968 void schan_imp_free_certificate_credentials(schan_credentials
*c
)
970 pgnutls_certificate_free_credentials(c
->credentials
);
973 static void schan_gnutls_log(int level
, const char *msg
)
975 TRACE("<%d> %s", level
, msg
);
978 BOOL
schan_imp_init(void)
982 libgnutls_handle
= dlopen(SONAME_LIBGNUTLS
, RTLD_NOW
);
983 if (!libgnutls_handle
)
985 ERR_(winediag
)("Failed to load libgnutls, secure connections will not be available.\n");
989 #define LOAD_FUNCPTR(f) \
990 if (!(p##f = dlsym(libgnutls_handle, #f))) \
992 ERR("Failed to load %s\n", #f); \
996 LOAD_FUNCPTR(gnutls_alert_get
)
997 LOAD_FUNCPTR(gnutls_alert_get_name
)
998 LOAD_FUNCPTR(gnutls_certificate_allocate_credentials
)
999 LOAD_FUNCPTR(gnutls_certificate_free_credentials
)
1000 LOAD_FUNCPTR(gnutls_certificate_get_peers
)
1001 LOAD_FUNCPTR(gnutls_certificate_set_x509_key
)
1002 LOAD_FUNCPTR(gnutls_cipher_get
)
1003 LOAD_FUNCPTR(gnutls_cipher_get_key_size
)
1004 LOAD_FUNCPTR(gnutls_credentials_set
)
1005 LOAD_FUNCPTR(gnutls_deinit
)
1006 LOAD_FUNCPTR(gnutls_global_deinit
)
1007 LOAD_FUNCPTR(gnutls_global_init
)
1008 LOAD_FUNCPTR(gnutls_global_set_log_function
)
1009 LOAD_FUNCPTR(gnutls_global_set_log_level
)
1010 LOAD_FUNCPTR(gnutls_handshake
)
1011 LOAD_FUNCPTR(gnutls_init
)
1012 LOAD_FUNCPTR(gnutls_kx_get
)
1013 LOAD_FUNCPTR(gnutls_mac_get
)
1014 LOAD_FUNCPTR(gnutls_mac_get_key_size
)
1015 LOAD_FUNCPTR(gnutls_perror
)
1016 LOAD_FUNCPTR(gnutls_protocol_get_version
)
1017 LOAD_FUNCPTR(gnutls_priority_set_direct
)
1018 LOAD_FUNCPTR(gnutls_privkey_deinit
)
1019 LOAD_FUNCPTR(gnutls_privkey_init
)
1020 LOAD_FUNCPTR(gnutls_record_get_max_size
);
1021 LOAD_FUNCPTR(gnutls_record_recv
);
1022 LOAD_FUNCPTR(gnutls_record_send
);
1023 LOAD_FUNCPTR(gnutls_server_name_set
)
1024 LOAD_FUNCPTR(gnutls_transport_get_ptr
)
1025 LOAD_FUNCPTR(gnutls_transport_set_errno
)
1026 LOAD_FUNCPTR(gnutls_transport_set_ptr
)
1027 LOAD_FUNCPTR(gnutls_transport_set_pull_function
)
1028 LOAD_FUNCPTR(gnutls_transport_set_push_function
)
1029 LOAD_FUNCPTR(gnutls_x509_crt_deinit
)
1030 LOAD_FUNCPTR(gnutls_x509_crt_import
)
1031 LOAD_FUNCPTR(gnutls_x509_crt_init
)
1032 LOAD_FUNCPTR(gnutls_x509_privkey_deinit
)
1035 if (!(pgnutls_cipher_get_block_size
= dlsym(libgnutls_handle
, "gnutls_cipher_get_block_size")))
1037 WARN("gnutls_cipher_get_block_size not found\n");
1038 pgnutls_cipher_get_block_size
= compat_cipher_get_block_size
;
1040 if (!(pgnutls_alpn_set_protocols
= dlsym(libgnutls_handle
, "gnutls_alpn_set_protocols")))
1042 WARN("gnutls_alpn_set_protocols not found\n");
1043 pgnutls_alpn_set_protocols
= compat_gnutls_alpn_set_protocols
;
1045 if (!(pgnutls_alpn_get_selected_protocol
= dlsym(libgnutls_handle
, "gnutls_alpn_get_selected_protocol")))
1047 WARN("gnutls_alpn_get_selected_protocol not found\n");
1048 pgnutls_alpn_get_selected_protocol
= compat_gnutls_alpn_get_selected_protocol
;
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(schan_gnutls_log
);
1074 check_supported_protocols();
1078 dlclose(libgnutls_handle
);
1079 libgnutls_handle
= NULL
;
1083 void schan_imp_deinit(void)
1085 pgnutls_global_deinit();
1086 dlclose(libgnutls_handle
);
1087 libgnutls_handle
= NULL
;
1090 #endif /* SONAME_LIBGNUTLS && !HAVE_SECURITY_SECURITY_H */