Add support for setting the DTLS timeout values, and set the retransmission timeout value to 0 to allow for retransmission on each call to schan_InitializeSecurityContext.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel.c | 6 ++++++ dlls/secur32/schannel_gnutls.c | 22 ++++++++++++++++++++++ dlls/secur32/secur32_priv.h | 8 ++++++++ 3 files changed, 36 insertions(+)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 141a191c7c6..f1b47a78d21 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -808,6 +808,12 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( else WARN("invalid buffer size %u\n", buffer->cbBuffer); }
+ if (is_dtls_context(ctx)) + { + struct set_dtls_timeouts_params params = { ctx->transport.session, 0, 60000 }; + GNUTLS_CALL( set_dtls_timeouts, ¶ms ); + } + phNewContext->dwLower = handle; phNewContext->dwUpper = 0; } diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index 31fdb769677..ac51cbb5d9f 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -60,6 +60,7 @@ static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t); static void (*pgnutls_transport_set_pull_timeout_function)(gnutls_session_t, int (*)(gnutls_transport_ptr_t, unsigned int)); static void (*pgnutls_dtls_set_mtu)(gnutls_session_t, unsigned int); +static void (*pgnutls_dtls_set_timeouts)(gnutls_session_t, unsigned int, unsigned int);
/* Not present in gnutls version < 3.2.0. */ static int (*pgnutls_alpn_get_selected_protocol)(gnutls_session_t, gnutls_datum_t *); @@ -198,6 +199,12 @@ static void compat_gnutls_dtls_set_mtu(gnutls_session_t session, unsigned int mt FIXME("\n"); }
+static void compat_gnutls_dtls_set_timeouts(gnutls_session_t session, unsigned int retrans_timeout, + unsigned int total_timeout) +{ + FIXME("\n"); +} + static void init_schan_buffers(struct schan_buffers *s, const PSecBufferDesc desc, int (*get_next_buffer)(const struct schan_transport *, struct schan_buffers *)) { @@ -989,6 +996,15 @@ static NTSTATUS schan_set_dtls_mtu( void *args ) return SEC_E_OK; }
+static NTSTATUS schan_set_dtls_timeouts( void *args ) +{ + const struct set_dtls_timeouts_params *params = args; + gnutls_session_t s = (gnutls_session_t)params->session; + + pgnutls_dtls_set_timeouts(s, params->retrans_timeout, params->total_timeout); + return SEC_E_OK; +} + static inline void reverse_bytes(BYTE *buf, ULONG len) { BYTE tmp; @@ -1245,6 +1261,11 @@ static NTSTATUS process_attach( void *args ) WARN("gnutls_dtls_set_mtu not found\n"); pgnutls_dtls_set_mtu = compat_gnutls_dtls_set_mtu; } + if (!(pgnutls_dtls_set_timeouts = dlsym(libgnutls_handle, "gnutls_dtls_set_timeouts"))) + { + WARN("gnutls_dtls_set_timeouts not found\n"); + pgnutls_dtls_set_timeouts = compat_gnutls_dtls_set_timeouts; + } if (!(pgnutls_privkey_export_x509 = dlsym(libgnutls_handle, "gnutls_privkey_export_x509"))) { WARN("gnutls_privkey_export_x509 not found\n"); @@ -1308,6 +1329,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = schan_set_application_protocols, schan_set_dtls_mtu, schan_set_session_target, + schan_set_dtls_timeouts, };
#endif /* SONAME_LIBGNUTLS */ diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h index c17adc96dc6..64edc0581d6 100644 --- a/dlls/secur32/secur32_priv.h +++ b/dlls/secur32/secur32_priv.h @@ -203,6 +203,13 @@ struct set_session_target_params const char *target; };
+struct set_dtls_timeouts_params +{ + schan_session session; + unsigned int retrans_timeout; + unsigned int total_timeout; +}; + enum schan_funcs { unix_process_attach, @@ -225,6 +232,7 @@ enum schan_funcs unix_set_application_protocols, unix_set_dtls_mtu, unix_set_session_target, + unix_set_dtls_timeouts, };
#endif /* __SECUR32_PRIV_H__ */
Since we write no data into this buffer, set the count to 0. Otherwise, some applications assume there has been alert data written into the buffer.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel.c | 7 +++++++ dlls/secur32/tests/schannel.c | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index f1b47a78d21..42f2e4b917f 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -894,6 +894,13 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer-ctx->transport.in.offset; }
+ for (i = 0; i < pOutput->cBuffers; i++) + { + SecBuffer *buffer = &pOutput->pBuffers[i]; + if (buffer->BufferType == SECBUFFER_ALERT && buffer->cbBuffer) + buffer->cbBuffer = 0; + } + *pfContextAttr = ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT | ISC_RET_CONFIDENTIALITY | ISC_RET_STREAM; if (ctx->req_ctx_attr & ISC_REQ_EXTENDED_ERROR) *pfContextAttr |= ISC_RET_EXTENDED_ERROR; if (ctx->req_ctx_attr & ISC_REQ_DATAGRAM) *pfContextAttr |= ISC_RET_DATAGRAM; diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 6c15addf2fc..6fbe0c34bd1 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -700,7 +700,7 @@ static void test_context_output_buffer_size(DWORD protocol, DWORD flags, ULONG c status = InitializeSecurityContextA(&cred_handle, NULL, (SEC_CHAR *)"localhost", ctxt_flags_req, 0, 0, &in_buffers, 0, &context, &out_buffers, &attrs, NULL); ok(status == SEC_I_CONTINUE_NEEDED, "%d: Expected SEC_I_CONTINUE_NEEDED, got %08x\n", i, status); - if (i) todo_wine ok(!buffer->cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty\n"); + if (i) ok(!buffer->cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty\n"); DeleteSecurityContext(&context); }
@@ -1661,7 +1661,7 @@ static void test_dtls(void) ok( !exp.LowPart, "got %08x\n", exp.LowPart ); ok( !exp.HighPart, "got %08x\n", exp.HighPart ); ok( buffers[1].pBuffers[1].BufferType == SECBUFFER_ALERT, "Expected buffertype SECBUFFER_ALERT, got %#x\n", buffers[1].pBuffers[1].BufferType); - todo_wine ok( !buffers[1].pBuffers[1].cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty, got %#x\n", buffers[1].pBuffers[1].cbBuffer); + ok( !buffers[1].pBuffers[1].cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty, got %#x\n", buffers[1].pBuffers[1].cbBuffer); prev_buf_len = buffers[1].pBuffers[0].cbBuffer;
/*
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=106137
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
When a NULL pInput argument is passed into InitializeSecurityContextW for an existing DTLS context, we need to retransmit the last handshake packet.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel.c | 41 ++++++++++++++++++++--------------- dlls/secur32/tests/schannel.c | 10 ++++----- 2 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 42f2e4b917f..ecc284750b5 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -812,6 +812,7 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( { struct set_dtls_timeouts_params params = { ctx->transport.session, 0, 60000 }; GNUTLS_CALL( set_dtls_timeouts, ¶ms ); + expected_size = 0; }
phNewContext->dwLower = handle; @@ -823,30 +824,34 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( unsigned char *ptr;
if (!(ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX))) return SEC_E_INVALID_HANDLE; - if (!pInput) return is_dtls_context(ctx) ? SEC_E_INSUFFICIENT_MEMORY : SEC_E_INCOMPLETE_MESSAGE; - if ((idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) == -1) return SEC_E_INCOMPLETE_MESSAGE; - - buffer = &pInput->pBuffers[idx]; - ptr = buffer->pvBuffer; + if (!pInput && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE; expected_size = 0;
- while (buffer->cbBuffer > expected_size + ctx->header_size) + if (pInput) { - record_size = ctx->header_size + read_record_size(ptr, ctx->header_size); + if ((idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) == -1) return SEC_E_INCOMPLETE_MESSAGE;
- if (buffer->cbBuffer < expected_size + record_size) break; - expected_size += record_size; - ptr += record_size; - } + buffer = &pInput->pBuffers[idx]; + ptr = buffer->pvBuffer;
- if (!expected_size) - { - TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n", - max(ctx->header_size + 1, record_size), buffer->cbBuffer); - return SEC_E_INCOMPLETE_MESSAGE; - } + while (buffer->cbBuffer > expected_size + ctx->header_size) + { + record_size = ctx->header_size + read_record_size(ptr, ctx->header_size);
- TRACE("Using expected_size %lu.\n", expected_size); + if (buffer->cbBuffer < expected_size + record_size) break; + expected_size += record_size; + ptr += record_size; + } + + if (!expected_size) + { + TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n", + max(ctx->header_size + 1, record_size), buffer->cbBuffer); + return SEC_E_INCOMPLETE_MESSAGE; + } + + TRACE("Using expected_size %lu.\n", expected_size); + }
if (phNewContext) *phNewContext = *phContext; } diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 6fbe0c34bd1..afcc382927a 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1693,17 +1693,17 @@ static void test_dtls(void) ctx_handle2.dwLower = ctx_handle2.dwUpper = 0xdeadbeef; status = InitializeSecurityContextA( &cred_handle, &ctx_handle, (SEC_CHAR *)"winetest", flags_req, 0, 16, NULL, 0, &ctx_handle2, &buffers[1], &attr, &exp ); - todo_wine ok( status == SEC_I_CONTINUE_NEEDED, "got %08x\n", status ); + ok( status == SEC_I_CONTINUE_NEEDED, "got %08x\n", status );
flags_ret = ISC_RET_MANUAL_CRED_VALIDATION | ISC_RET_STREAM | ISC_RET_EXTENDED_ERROR | ISC_RET_DATAGRAM | ISC_RET_USED_SUPPLIED_CREDS | ISC_RET_CONFIDENTIALITY | ISC_RET_SEQUENCE_DETECT | ISC_RET_REPLAY_DETECT; - todo_wine ok( attr == flags_ret, "got %08x\n", attr ); + ok( attr == flags_ret, "got %08x\n", attr ); todo_wine ok( exp.LowPart, "got %08x\n", exp.LowPart ); todo_wine ok( exp.HighPart, "got %08x\n", exp.HighPart ); ok( buffers[1].pBuffers[1].BufferType == SECBUFFER_ALERT, "Expected buffertype SECBUFFER_ALERT, got %#x\n", buffers[1].pBuffers[1].BufferType); - todo_wine ok( !buffers[1].pBuffers[1].cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty, got %#x\n", buffers[1].pBuffers[1].cbBuffer); - todo_wine ok( ctx_handle.dwLower == ctx_handle2.dwLower, "dwLower mismatch, expected %#lx, got %#lx\n", ctx_handle.dwLower, ctx_handle2.dwLower); - todo_wine ok( ctx_handle.dwUpper == ctx_handle2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", ctx_handle.dwUpper, ctx_handle2.dwUpper); + ok( !buffers[1].pBuffers[1].cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty, got %#x\n", buffers[1].pBuffers[1].cbBuffer); + ok( ctx_handle.dwLower == ctx_handle2.dwLower, "dwLower mismatch, expected %#lx, got %#lx\n", ctx_handle.dwLower, ctx_handle2.dwLower); + ok( ctx_handle.dwUpper == ctx_handle2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", ctx_handle.dwUpper, ctx_handle2.dwUpper);
/* With no new input buffer, output buffer length should match prior call. */ todo_wine ok(buffers[1].pBuffers[0].cbBuffer == prev_buf_len, "Output buffer size mismatch, expected %#x, got %#x\n",
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=106138
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
In order for DTLS retransmission to occur properly, we need to be in non-blocking mode.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel_gnutls.c | 7 +++---- dlls/secur32/tests/schannel.c | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index ac51cbb5d9f..3f947870a73 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -461,14 +461,13 @@ static NTSTATUS schan_get_enabled_protocols( void *args ) static int pull_timeout(gnutls_transport_ptr_t transport, unsigned int timeout) { struct schan_transport *t = (struct schan_transport*)transport; - gnutls_session_t s = (gnutls_session_t)t->session; SIZE_T count = 0;
TRACE("\n");
if (get_buffer(t, &t->in, &count)) return 1; - pgnutls_transport_set_errno(s, EAGAIN); - return -1; + + return 0; }
static NTSTATUS schan_create_session( void *args ) @@ -483,7 +482,7 @@ static NTSTATUS schan_create_session( void *args )
if (cred->enabled_protocols & (SP_PROT_DTLS1_0_CLIENT | SP_PROT_DTLS1_2_CLIENT)) { - flags |= GNUTLS_DATAGRAM; + flags |= GNUTLS_DATAGRAM | GNUTLS_NONBLOCK; }
err = pgnutls_init(s, flags); diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index afcc382927a..902a490a296 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1706,7 +1706,7 @@ static void test_dtls(void) ok( ctx_handle.dwUpper == ctx_handle2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", ctx_handle.dwUpper, ctx_handle2.dwUpper);
/* With no new input buffer, output buffer length should match prior call. */ - todo_wine ok(buffers[1].pBuffers[0].cbBuffer == prev_buf_len, "Output buffer size mismatch, expected %#x, got %#x\n", + ok(buffers[1].pBuffers[0].cbBuffer == prev_buf_len, "Output buffer size mismatch, expected %#x, got %#x\n", prev_buf_len, buffers[1].pBuffers[0].cbBuffer);
free_buffers( &buffers[0] );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=106139
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
Successive calls to InitializeSecurityContext without a new pInput buffer will result in retransmission, creating a handshake packet with an incremented sequence number value, but otherwise identical to the last call to InitializeSecurityContext.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/tests/schannel.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 902a490a296..abddfa138ab 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1623,6 +1623,7 @@ static void test_dtls(void) CtxtHandle ctx_handle, ctx_handle2; SecBufferDesc buffers[3]; ULONG flags_req, flags_ret, attr, prev_buf_len; + char *buf, *buf2;
init_cred( &cred ); cred.grbitEnabledProtocols = SP_PROT_DTLS_CLIENT | SP_PROT_DTLS1_2_CLIENT; @@ -1663,6 +1664,9 @@ static void test_dtls(void) ok( buffers[1].pBuffers[1].BufferType == SECBUFFER_ALERT, "Expected buffertype SECBUFFER_ALERT, got %#x\n", buffers[1].pBuffers[1].BufferType); ok( !buffers[1].pBuffers[1].cbBuffer, "Expected SECBUFFER_ALERT buffer to be empty, got %#x\n", buffers[1].pBuffers[1].cbBuffer); prev_buf_len = buffers[1].pBuffers[0].cbBuffer; + buf = HeapAlloc( GetProcessHeap(), 0, prev_buf_len ); + memcpy( buf, buffers[1].pBuffers[0].pvBuffer, prev_buf_len ); + ok( buf[10] == 0, "Expected initial packet to have sequence number value of 0, got %d\n", buf[10]);
/* * If we don't set the SECBUFFER_ALERT cbBuffer value, we will get @@ -1709,7 +1713,17 @@ static void test_dtls(void) ok(buffers[1].pBuffers[0].cbBuffer == prev_buf_len, "Output buffer size mismatch, expected %#x, got %#x\n", prev_buf_len, buffers[1].pBuffers[0].cbBuffer);
+ /* + * The retransmission packet and the original packet should only differ in + * their sequence number value. + */ + buf2 = (char *)buffers[1].pBuffers[0].pvBuffer; + ok( buf2[10] == 1, "Expected retransmitted packet to have sequence number value of 1, got %d\n", buf2[10]); + ok( !memcmp(buf2, buf, 9), "Lower portion mismatch between retransmitted packet and original packet\n"); + ok( !memcmp(buf2 + 11, buf + 11, prev_buf_len - 11), "Upper portion mismatch between retransmitted packet and original packet\n"); + free_buffers( &buffers[0] ); + HeapFree(GetProcessHeap(), 0, buf); HeapFree(GetProcessHeap(), 0, buffers[1].pBuffers[1].pvBuffer); free_buffers( &buffers[1] ); DeleteSecurityContext( &ctx_handle );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=106140
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1442: Test failed: got 00090317 schannel.c:1452: Test failed: DecryptMessage failed: 80090317