2 * WPA Supplicant / SSL/TLS interface functions for Microsoft Schannel
3 * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
16 * FIX: Go through all SSPI functions and verify what needs to be freed
17 * FIX: session resumption
18 * TODO: add support for server cert chain validation
19 * TODO: add support for CA cert validation
20 * TODO: add support for EAP-TLS (client cert/key conf)
29 #define SECURITY_WIN32
39 PSecurityFunctionTable sspi
;
40 HCERTSTORE my_cert_store
;
43 struct tls_connection
{
44 int established
, start
;
45 int failed
, read_alerts
, write_alerts
;
47 SCHANNEL_CRED schannel_cred
;
56 static int schannel_load_lib(struct tls_global
*global
)
58 INIT_SECURITY_INTERFACE pInitSecurityInterface
;
60 global
->hsecurity
= LoadLibrary("Secur32.dll");
61 if (global
->hsecurity
== NULL
) {
62 wpa_printf(MSG_ERROR
, "%s: Could not load Secur32.dll - 0x%x",
63 __func__
, (unsigned int) GetLastError());
67 pInitSecurityInterface
= (INIT_SECURITY_INTERFACE
) GetProcAddress(
68 global
->hsecurity
, "InitSecurityInterfaceA");
69 if (pInitSecurityInterface
== NULL
) {
70 wpa_printf(MSG_ERROR
, "%s: Could not find "
71 "InitSecurityInterfaceA from Secur32.dll",
73 FreeLibrary(global
->hsecurity
);
74 global
->hsecurity
= NULL
;
78 global
->sspi
= pInitSecurityInterface();
79 if (global
->sspi
== NULL
) {
80 wpa_printf(MSG_ERROR
, "%s: Could not read security "
82 __func__
, (unsigned int) GetLastError());
83 FreeLibrary(global
->hsecurity
);
84 global
->hsecurity
= NULL
;
92 void * tls_init(const struct tls_config
*conf
)
94 struct tls_global
*global
;
96 global
= malloc(sizeof(*global
));
99 memset(global
, 0, sizeof(*global
));
100 if (schannel_load_lib(global
)) {
108 void tls_deinit(void *ssl_ctx
)
110 struct tls_global
*global
= ssl_ctx
;
112 if (global
->my_cert_store
)
113 CertCloseStore(global
->my_cert_store
, 0);
114 FreeLibrary(global
->hsecurity
);
119 int tls_get_errors(void *ssl_ctx
)
125 struct tls_connection
* tls_connection_init(void *ssl_ctx
)
127 struct tls_connection
*conn
;
129 conn
= malloc(sizeof(*conn
));
132 memset(conn
, 0, sizeof(*conn
));
139 void tls_connection_deinit(void *ssl_ctx
, struct tls_connection
*conn
)
148 int tls_connection_established(void *ssl_ctx
, struct tls_connection
*conn
)
150 return conn
? conn
->established
: 0;
154 int tls_connection_shutdown(void *ssl_ctx
, struct tls_connection
*conn
)
156 struct tls_global
*global
= ssl_ctx
;
160 conn
->eap_tls_prf_set
= 0;
161 conn
->established
= conn
->failed
= 0;
162 conn
->read_alerts
= conn
->write_alerts
= 0;
163 global
->sspi
->DeleteSecurityContext(&conn
->context
);
164 /* FIX: what else needs to be reseted? */
170 int tls_global_ca_cert(void *_ssl_ctx
, const char *ca_cert
)
176 int tls_global_set_verify(void *ssl_ctx
, int check_crl
)
182 int tls_connection_set_verify(void *ssl_ctx
, struct tls_connection
*conn
,
189 int tls_global_client_cert(void *_ssl_ctx
, const char *client_cert
)
195 int tls_global_private_key(void *_ssl_ctx
, const char *private_key
,
196 const char *private_key_passwd
)
202 int tls_connection_get_keys(void *ssl_ctx
, struct tls_connection
*conn
,
203 struct tls_keys
*keys
)
205 if (conn
== NULL
|| keys
== NULL
|| !conn
->eap_tls_prf_set
)
208 memset(keys
, 0, sizeof(*keys
));
211 * Cannot get master_key from Schannel, but EapKeyBlock can be used to
212 * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and
213 * EAP-TTLS cannot use this, though, since they are using different
214 * labels. The only option could be to implement TLSv1 completely here
215 * and just use Schannel or CryptoAPI for low-level crypto
218 keys
->eap_tls_prf
= conn
->eap_tls_prf
;
219 keys
->eap_tls_prf_len
= sizeof(conn
->eap_tls_prf
);
225 static u8
* tls_conn_hs_clienthello(struct tls_global
*global
,
226 struct tls_connection
*conn
,
229 DWORD sspi_flags
, sspi_flags_out
;
230 SecBufferDesc outbuf
;
231 SecBuffer outbufs
[1];
232 SECURITY_STATUS status
;
235 sspi_flags
= ISC_REQ_REPLAY_DETECT
|
236 ISC_REQ_CONFIDENTIALITY
|
237 ISC_RET_EXTENDED_ERROR
|
238 ISC_REQ_ALLOCATE_MEMORY
|
239 ISC_REQ_MANUAL_CRED_VALIDATION
;
241 wpa_printf(MSG_DEBUG
, "%s: Generating ClientHello", __func__
);
243 outbufs
[0].pvBuffer
= NULL
;
244 outbufs
[0].BufferType
= SECBUFFER_TOKEN
;
245 outbufs
[0].cbBuffer
= 0;
248 outbuf
.pBuffers
= outbufs
;
249 outbuf
.ulVersion
= SECBUFFER_VERSION
;
251 status
= global
->sspi
->InitializeSecurityContextA(
252 &conn
->creds
, NULL
, NULL
/* server name */, sspi_flags
, 0,
253 SECURITY_NATIVE_DREP
, NULL
, 0, &conn
->context
,
254 &outbuf
, &sspi_flags_out
, &ts_expiry
);
255 if (status
!= SEC_I_CONTINUE_NEEDED
) {
256 wpa_printf(MSG_ERROR
, "%s: InitializeSecurityContextA "
258 __func__
, (unsigned int) status
);
262 if (outbufs
[0].cbBuffer
!= 0 && outbufs
[0].pvBuffer
) {
264 wpa_hexdump(MSG_MSGDUMP
, "SChannel - ClientHello",
265 outbufs
[0].pvBuffer
, outbufs
[0].cbBuffer
);
267 *out_len
= outbufs
[0].cbBuffer
;
268 buf
= malloc(*out_len
);
271 memcpy(buf
, outbufs
[0].pvBuffer
, *out_len
);
272 global
->sspi
->FreeContextBuffer(outbufs
[0].pvBuffer
);
276 wpa_printf(MSG_ERROR
, "SChannel: Failed to generate ClientHello");
282 #ifndef SECPKG_ATTR_EAP_KEY_BLOCK
283 #define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b
285 typedef struct _SecPkgContext_EapKeyBlock
{
288 } SecPkgContext_EapKeyBlock
, *PSecPkgContext_EapKeyBlock
;
289 #endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */
291 static int tls_get_eap(struct tls_global
*global
, struct tls_connection
*conn
)
293 SECURITY_STATUS status
;
294 SecPkgContext_EapKeyBlock kb
;
296 /* Note: Windows NT and Windows Me/98/95 do not support getting
299 status
= global
->sspi
->QueryContextAttributes(
300 &conn
->context
, SECPKG_ATTR_EAP_KEY_BLOCK
, &kb
);
301 if (status
!= SEC_E_OK
) {
302 wpa_printf(MSG_DEBUG
, "%s: QueryContextAttributes("
303 "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)",
304 __func__
, (int) status
);
308 wpa_hexdump_key(MSG_MSGDUMP
, "Schannel - EapKeyBlock - rgbKeys",
309 kb
.rgbKeys
, sizeof(kb
.rgbKeys
));
310 wpa_hexdump_key(MSG_MSGDUMP
, "Schannel - EapKeyBlock - rgbIVs",
311 kb
.rgbIVs
, sizeof(kb
.rgbIVs
));
313 memcpy(conn
->eap_tls_prf
, kb
.rgbKeys
, sizeof(kb
.rgbKeys
));
314 conn
->eap_tls_prf_set
= 1;
318 u8
* tls_connection_handshake(void *ssl_ctx
, struct tls_connection
*conn
,
319 const u8
*in_data
, size_t in_len
,
322 struct tls_global
*global
= ssl_ctx
;
323 DWORD sspi_flags
, sspi_flags_out
;
324 SecBufferDesc inbuf
, outbuf
;
325 SecBuffer inbufs
[2], outbufs
[1];
326 SECURITY_STATUS status
;
331 return tls_conn_hs_clienthello(global
, conn
, out_len
);
334 wpa_printf(MSG_DEBUG
, "SChannel: %d bytes handshake data to process",
337 sspi_flags
= ISC_REQ_REPLAY_DETECT
|
338 ISC_REQ_CONFIDENTIALITY
|
339 ISC_RET_EXTENDED_ERROR
|
340 ISC_REQ_ALLOCATE_MEMORY
|
341 ISC_REQ_MANUAL_CRED_VALIDATION
;
343 /* Input buffer for Schannel */
344 inbufs
[0].pvBuffer
= (u8
*) in_data
;
345 inbufs
[0].cbBuffer
= in_len
;
346 inbufs
[0].BufferType
= SECBUFFER_TOKEN
;
348 /* Place for leftover data from Schannel */
349 inbufs
[1].pvBuffer
= NULL
;
350 inbufs
[1].cbBuffer
= 0;
351 inbufs
[1].BufferType
= SECBUFFER_EMPTY
;
354 inbuf
.pBuffers
= inbufs
;
355 inbuf
.ulVersion
= SECBUFFER_VERSION
;
357 /* Output buffer for Schannel */
358 outbufs
[0].pvBuffer
= NULL
;
359 outbufs
[0].cbBuffer
= 0;
360 outbufs
[0].BufferType
= SECBUFFER_TOKEN
;
363 outbuf
.pBuffers
= outbufs
;
364 outbuf
.ulVersion
= SECBUFFER_VERSION
;
366 status
= global
->sspi
->InitializeSecurityContextA(
367 &conn
->creds
, &conn
->context
, NULL
, sspi_flags
, 0,
368 SECURITY_NATIVE_DREP
, &inbuf
, 0, NULL
,
369 &outbuf
, &sspi_flags_out
, &ts_expiry
);
371 wpa_printf(MSG_MSGDUMP
, "Schannel: InitializeSecurityContextA -> "
372 "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d "
373 "intype[1]=%d outlen[0]=%d",
374 (int) status
, (int) inbufs
[0].cbBuffer
,
375 (int) inbufs
[0].BufferType
, (int) inbufs
[1].cbBuffer
,
376 (int) inbufs
[1].BufferType
,
377 (int) outbufs
[0].cbBuffer
);
378 if (status
== SEC_E_OK
|| status
== SEC_I_CONTINUE_NEEDED
||
379 (FAILED(status
) && (sspi_flags_out
& ISC_RET_EXTENDED_ERROR
))) {
380 if (outbufs
[0].cbBuffer
!= 0 && outbufs
[0].pvBuffer
) {
381 wpa_hexdump(MSG_MSGDUMP
, "SChannel - output",
382 outbufs
[0].pvBuffer
, outbufs
[0].cbBuffer
);
383 *out_len
= outbufs
[0].cbBuffer
;
384 out_buf
= malloc(*out_len
);
387 memcpy(out_buf
, outbufs
[0].pvBuffer
, *out_len
);
388 global
->sspi
->FreeContextBuffer(outbufs
[0].pvBuffer
);
389 outbufs
[0].pvBuffer
= NULL
;
394 case SEC_E_INCOMPLETE_MESSAGE
:
395 wpa_printf(MSG_DEBUG
, "Schannel: SEC_E_INCOMPLETE_MESSAGE");
397 case SEC_I_CONTINUE_NEEDED
:
398 wpa_printf(MSG_DEBUG
, "Schannel: SEC_I_CONTINUE_NEEDED");
401 /* TODO: verify server certificate chain */
402 wpa_printf(MSG_DEBUG
, "Schannel: SEC_E_OK - Handshake "
403 "completed successfully");
404 conn
->established
= 1;
405 tls_get_eap(global
, conn
);
407 /* Need to return something to get final TLS ACK. */
411 if (inbufs
[1].BufferType
== SECBUFFER_EXTRA
) {
412 wpa_hexdump(MSG_MSGDUMP
, "SChannel - Encrypted "
414 inbufs
[1].pvBuffer
, inbufs
[1].cbBuffer
);
415 /* FIX: need to fix TLS API to allow this data to be
416 * passed to the caller */
417 global
->sspi
->FreeContextBuffer(inbufs
[1].pvBuffer
);
418 inbufs
[1].pvBuffer
= NULL
;
421 case SEC_I_INCOMPLETE_CREDENTIALS
:
422 wpa_printf(MSG_DEBUG
,
423 "Schannel: SEC_I_INCOMPLETE_CREDENTIALS");
425 case SEC_E_WRONG_PRINCIPAL
:
426 wpa_printf(MSG_DEBUG
, "Schannel: SEC_E_WRONG_PRINCIPAL");
428 case SEC_E_INTERNAL_ERROR
:
429 wpa_printf(MSG_DEBUG
, "Schannel: SEC_E_INTERNAL_ERROR");
433 if (FAILED(status
)) {
434 wpa_printf(MSG_DEBUG
, "Schannel: Handshake failed "
435 "(out_buf=%p)", out_buf
);
437 global
->sspi
->DeleteSecurityContext(&conn
->context
);
441 if (inbufs
[1].BufferType
== SECBUFFER_EXTRA
) {
442 /* TODO: Can this happen? What to do with this data? */
443 wpa_hexdump(MSG_MSGDUMP
, "SChannel - Leftover data",
444 inbufs
[1].pvBuffer
, inbufs
[1].cbBuffer
);
445 global
->sspi
->FreeContextBuffer(inbufs
[1].pvBuffer
);
446 inbufs
[1].pvBuffer
= NULL
;
453 u8
* tls_connection_server_handshake(void *ssl_ctx
,
454 struct tls_connection
*conn
,
455 const u8
*in_data
, size_t in_len
,
462 int tls_connection_encrypt(void *ssl_ctx
, struct tls_connection
*conn
,
463 const u8
*in_data
, size_t in_len
,
464 u8
*out_data
, size_t out_len
)
466 struct tls_global
*global
= ssl_ctx
;
467 SECURITY_STATUS status
;
470 SecPkgContext_StreamSizes sizes
;
474 status
= global
->sspi
->QueryContextAttributes(&conn
->context
,
475 SECPKG_ATTR_STREAM_SIZES
,
477 if (status
!= SEC_E_OK
) {
478 wpa_printf(MSG_DEBUG
, "%s: QueryContextAttributes failed",
482 wpa_printf(MSG_DEBUG
, "%s: Stream sizes: header=%u trailer=%u",
484 (unsigned int) sizes
.cbHeader
,
485 (unsigned int) sizes
.cbTrailer
);
487 total_len
= sizes
.cbHeader
+ in_len
+ sizes
.cbTrailer
;
489 if (out_len
< total_len
) {
490 wpa_printf(MSG_DEBUG
, "%s: too short out_data (out_len=%lu "
491 "in_len=%lu total_len=%lu)", __func__
,
492 (unsigned long) out_len
, (unsigned long) in_len
,
493 (unsigned long) total_len
);
497 memset(&bufs
, 0, sizeof(bufs
));
498 bufs
[0].pvBuffer
= out_data
;
499 bufs
[0].cbBuffer
= sizes
.cbHeader
;
500 bufs
[0].BufferType
= SECBUFFER_STREAM_HEADER
;
502 memcpy(out_data
+ sizes
.cbHeader
, in_data
, in_len
);
503 bufs
[1].pvBuffer
= out_data
+ sizes
.cbHeader
;
504 bufs
[1].cbBuffer
= in_len
;
505 bufs
[1].BufferType
= SECBUFFER_DATA
;
507 bufs
[2].pvBuffer
= out_data
+ sizes
.cbHeader
+ in_len
;
508 bufs
[2].cbBuffer
= sizes
.cbTrailer
;
509 bufs
[2].BufferType
= SECBUFFER_STREAM_TRAILER
;
511 buf
.ulVersion
= SECBUFFER_VERSION
;
515 status
= global
->sspi
->EncryptMessage(&conn
->context
, 0, &buf
, 0);
517 wpa_printf(MSG_MSGDUMP
, "Schannel: EncryptMessage -> "
518 "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
519 "len[2]=%d type[2]=%d",
521 (int) bufs
[0].cbBuffer
, (int) bufs
[0].BufferType
,
522 (int) bufs
[1].cbBuffer
, (int) bufs
[1].BufferType
,
523 (int) bufs
[2].cbBuffer
, (int) bufs
[2].BufferType
);
524 wpa_printf(MSG_MSGDUMP
, "Schannel: EncryptMessage pointers: "
525 "out_data=%p bufs %p %p %p",
526 out_data
, bufs
[0].pvBuffer
, bufs
[1].pvBuffer
,
529 for (i
= 0; i
< 3; i
++) {
530 if (bufs
[i
].pvBuffer
&& bufs
[i
].BufferType
!= SECBUFFER_EMPTY
)
532 wpa_hexdump(MSG_MSGDUMP
, "SChannel: bufs",
533 bufs
[i
].pvBuffer
, bufs
[i
].cbBuffer
);
537 if (status
== SEC_E_OK
) {
538 wpa_printf(MSG_DEBUG
, "%s: SEC_E_OK", __func__
);
539 wpa_hexdump_key(MSG_MSGDUMP
, "Schannel: Encrypted data from "
540 "EncryptMessage", out_data
, total_len
);
544 wpa_printf(MSG_DEBUG
, "%s: Failed - status=%d",
545 __func__
, (int) status
);
550 int tls_connection_decrypt(void *ssl_ctx
, struct tls_connection
*conn
,
551 const u8
*in_data
, size_t in_len
,
552 u8
*out_data
, size_t out_len
)
554 struct tls_global
*global
= ssl_ctx
;
555 SECURITY_STATUS status
;
560 if (out_len
< in_len
) {
561 wpa_printf(MSG_DEBUG
, "%s: out_len=%lu < in_len=%lu", __func__
,
562 (unsigned long) out_len
, (unsigned long) in_len
);
566 wpa_hexdump(MSG_MSGDUMP
, "Schannel: Encrypted data to DecryptMessage",
568 memset(&bufs
, 0, sizeof(bufs
));
569 memcpy(out_data
, in_data
, in_len
);
570 bufs
[0].pvBuffer
= out_data
;
571 bufs
[0].cbBuffer
= in_len
;
572 bufs
[0].BufferType
= SECBUFFER_DATA
;
574 bufs
[1].BufferType
= SECBUFFER_EMPTY
;
575 bufs
[2].BufferType
= SECBUFFER_EMPTY
;
576 bufs
[3].BufferType
= SECBUFFER_EMPTY
;
578 buf
.ulVersion
= SECBUFFER_VERSION
;
582 status
= global
->sspi
->DecryptMessage(&conn
->context
, &buf
, 0,
584 wpa_printf(MSG_MSGDUMP
, "Schannel: DecryptMessage -> "
585 "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
586 "len[2]=%d type[2]=%d len[3]=%d type[3]=%d",
588 (int) bufs
[0].cbBuffer
, (int) bufs
[0].BufferType
,
589 (int) bufs
[1].cbBuffer
, (int) bufs
[1].BufferType
,
590 (int) bufs
[2].cbBuffer
, (int) bufs
[2].BufferType
,
591 (int) bufs
[3].cbBuffer
, (int) bufs
[3].BufferType
);
592 wpa_printf(MSG_MSGDUMP
, "Schannel: DecryptMessage pointers: "
593 "out_data=%p bufs %p %p %p %p",
594 out_data
, bufs
[0].pvBuffer
, bufs
[1].pvBuffer
,
595 bufs
[2].pvBuffer
, bufs
[3].pvBuffer
);
598 case SEC_E_INCOMPLETE_MESSAGE
:
599 wpa_printf(MSG_DEBUG
, "%s: SEC_E_INCOMPLETE_MESSAGE",
603 wpa_printf(MSG_DEBUG
, "%s: SEC_E_OK", __func__
);
604 for (i
= 0; i
< 4; i
++) {
605 if (bufs
[i
].BufferType
== SECBUFFER_DATA
)
609 wpa_printf(MSG_DEBUG
, "%s: No output data from "
610 "DecryptMessage", __func__
);
613 wpa_hexdump_key(MSG_MSGDUMP
, "Schannel: Decrypted data from "
615 bufs
[i
].pvBuffer
, bufs
[i
].cbBuffer
);
616 if (bufs
[i
].cbBuffer
> out_len
) {
617 wpa_printf(MSG_DEBUG
, "%s: Too long output data",
621 memmove(out_data
, bufs
[i
].pvBuffer
, bufs
[i
].cbBuffer
);
622 return bufs
[i
].cbBuffer
;
625 wpa_printf(MSG_DEBUG
, "%s: Failed - status=%d",
626 __func__
, (int) status
);
631 int tls_connection_resumed(void *ssl_ctx
, struct tls_connection
*conn
)
638 int tls_connection_set_master_key(void *ssl_ctx
, struct tls_connection
*conn
,
639 const u8
*key
, size_t key_len
)
643 #endif /* EAP_FAST */
646 int tls_connection_set_anon_dh(void *ssl_ctx
, struct tls_connection
*conn
)
652 int tls_get_cipher(void *ssl_ctx
, struct tls_connection
*conn
,
653 char *buf
, size_t buflen
)
659 int tls_connection_enable_workaround(void *ssl_ctx
,
660 struct tls_connection
*conn
)
667 /* ClientHello TLS extensions require a patch to openssl, so this function is
668 * commented out unless explicitly needed for EAP-FAST in order to be able to
669 * build this file with unmodified openssl. */
670 int tls_connection_client_hello_ext(void *ssl_ctx
, struct tls_connection
*conn
,
671 int ext_type
, const u8
*data
,
676 #endif /* EAP_FAST */
679 int tls_connection_get_failed(void *ssl_ctx
, struct tls_connection
*conn
)
687 int tls_connection_get_read_alerts(void *ssl_ctx
, struct tls_connection
*conn
)
691 return conn
->read_alerts
;
695 int tls_connection_get_write_alerts(void *ssl_ctx
, struct tls_connection
*conn
)
699 return conn
->write_alerts
;
703 int tls_connection_set_params(void *tls_ctx
, struct tls_connection
*conn
,
704 const struct tls_connection_params
*params
)
706 struct tls_global
*global
= tls_ctx
;
708 SECURITY_STATUS status
;
714 if (global
->my_cert_store
== NULL
&&
715 (global
->my_cert_store
= CertOpenSystemStore(0, "MY")) == NULL
) {
716 wpa_printf(MSG_ERROR
, "%s: CertOpenSystemStore failed - 0x%x",
717 __func__
, (unsigned int) GetLastError());
721 memset(&conn
->schannel_cred
, 0, sizeof(conn
->schannel_cred
));
722 conn
->schannel_cred
.dwVersion
= SCHANNEL_CRED_VERSION
;
723 conn
->schannel_cred
.grbitEnabledProtocols
= SP_PROT_TLS1
;
724 algs
[0] = CALG_RSA_KEYX
;
725 conn
->schannel_cred
.cSupportedAlgs
= 1;
726 conn
->schannel_cred
.palgSupportedAlgs
= algs
;
727 conn
->schannel_cred
.dwFlags
|= SCH_CRED_NO_DEFAULT_CREDS
;
728 status
= global
->sspi
->AcquireCredentialsHandleA(
729 NULL
, UNISP_NAME_A
, SECPKG_CRED_OUTBOUND
, NULL
,
730 &conn
->schannel_cred
, NULL
, NULL
, &conn
->creds
, &ts_expiry
);
731 if (status
!= SEC_E_OK
) {
732 wpa_printf(MSG_DEBUG
, "%s: AcquireCredentialsHandleA failed - "
733 "0x%x", __func__
, (unsigned int) status
);