If a valid pointer for phNewContext is passed in alongside a valid phContext pointer, initialize phNewContext with the value of phContext on success.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 32181b3b35f..6b699cccce1 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -831,6 +831,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( }
TRACE("Using expected_size %lu.\n", expected_size); + + if (phNewContext) *phNewContext = *phContext; }
ctx->req_ctx_attr = fContextReq;
Add tests for passing in a phNewContext argument alongside an existing phContext argument.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/tests/schannel.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-)
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 1fceaae6a61..f72d71a3af3 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -953,7 +953,7 @@ static void test_communication(void) ULONG attrs; SCHANNEL_CRED cred; CredHandle cred_handle; - CtxtHandle context; + CtxtHandle context, context2; SecPkgCredentials_NamesA names; SecPkgContext_StreamSizes sizes; SecPkgContext_ConnectionInfo conn_info; @@ -1044,12 +1044,15 @@ todo_wine send(sock, buf->pvBuffer, buf->cbBuffer, 0); buf->cbBuffer = buf_size;
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM, - 0, 0, NULL, 0, NULL, &buffers[0], &attrs, NULL); + 0, 0, NULL, 0, &context2, &buffers[0], &attrs, NULL); ok(status == SEC_E_INCOMPLETE_MESSAGE, "Got unexpected status %#x.\n", status); ok(buffers[0].pBuffers[0].cbBuffer == buf_size, "Output buffer size changed.\n"); ok(buffers[0].pBuffers[0].BufferType == SECBUFFER_TOKEN, "Output buffer type changed.\n"); + ok( context2.dwLower == 0xdeadbeef, "Did not expect dwLower to be set on new context\n"); + ok( context2.dwUpper == 0xdeadbeef, "Did not expect dwUpper to be set on new context\n");
buffers[1].cBuffers = 1; buffers[1].pBuffers[0].cbBuffer = 0; @@ -1076,19 +1079,22 @@ todo_wine ok(buffers[0].pBuffers[0].cbBuffer == buf_size, "Output buffer size changed.\n"); ok(buffers[0].pBuffers[0].BufferType == SECBUFFER_TOKEN, "Output buffer type changed.\n");
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; buffers[1].pBuffers[0].cbBuffer = 5; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM, - 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL); + 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL); ok(status == SEC_E_INCOMPLETE_MESSAGE || status == SEC_E_INVALID_TOKEN, "Got unexpected status %#x.\n", status); ok(buffers[0].pBuffers[0].cbBuffer == buf_size, "Output buffer size changed.\n"); ok(buffers[0].pBuffers[0].BufferType == SECBUFFER_TOKEN, "Output buffer type changed.\n"); + ok( context2.dwLower == 0xdeadbeef, "Did not expect dwLower to be set on new context\n"); + ok( context2.dwUpper == 0xdeadbeef, "Did not expect dwUpper to be set on new context\n");
buffers[1].pBuffers[0].cbBuffer = ret; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, - 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL); + 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL); buffers[1].pBuffers[0].cbBuffer = buf_size; while (status == SEC_I_CONTINUE_NEEDED) { @@ -1096,11 +1102,15 @@ todo_wine send(sock, buf->pvBuffer, buf->cbBuffer, 0); buf->cbBuffer = buf_size;
+ ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#lx, got %#lx\n", context.dwLower, context2.dwLower); + ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", context.dwUpper, context2.dwUpper); + buf = &buffers[1].pBuffers[0]; ret = receive_data(sock, buf); if (ret == -1) return;
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; buf->BufferType = SECBUFFER_TOKEN;
status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", @@ -1338,7 +1348,7 @@ todo_wine
buffers[1].pBuffers[0].BufferType = SECBUFFER_TOKEN; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", - ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL, + ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL); buffers[1].pBuffers[0].cbBuffer = buf_size; while (status == SEC_I_CONTINUE_NEEDED) @@ -1347,14 +1357,18 @@ todo_wine send(sock, buf->pvBuffer, buf->cbBuffer, 0); buf->cbBuffer = buf_size;
+ todo_wine ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#lx, got %#lx\n", context.dwLower, context2.dwLower); + todo_wine ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", context.dwUpper, context2.dwUpper); + buf = &buffers[1].pBuffers[0]; ret = receive_data(sock, buf); if (ret == -1) return;
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; buf->BufferType = SECBUFFER_TOKEN; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", - ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL); + ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL); buffers[1].pBuffers[0].cbBuffer = buf_size; } ok (status == SEC_E_OK, "got %08x\n", status); @@ -1399,7 +1413,7 @@ static void test_application_protocol_negotiation(void) ULONG attrs; SCHANNEL_CRED cred; CredHandle cred_handle; - CtxtHandle context; + CtxtHandle context, context2; SecPkgContext_ApplicationProtocol protocol; SecBufferDesc buffers[3]; SecBuffer *buf; @@ -1467,9 +1481,10 @@ static void test_application_protocol_negotiation(void) if (ret == -1) return;
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; buffers[1].pBuffers[0].BufferType = SECBUFFER_TOKEN; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", - ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL, + ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM|ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL); buffers[1].pBuffers[0].cbBuffer = buf_size; while (status == SEC_I_CONTINUE_NEEDED) @@ -1478,11 +1493,15 @@ static void test_application_protocol_negotiation(void) send(sock, buf->pvBuffer, buf->cbBuffer, 0); buf->cbBuffer = buf_size;
+ ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#lx, got %#lx\n", context.dwLower, context2.dwLower); + ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#lx, got %#lx\n", context.dwUpper, context2.dwUpper); + buf = &buffers[1].pBuffers[0]; ret = receive_data(sock, buf); if (ret == -1) return;
+ context2.dwLower = context2.dwUpper = 0xdeadbeef; buf->BufferType = SECBUFFER_TOKEN; status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL);
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=105970
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1374: Test failed: got 00090317 schannel.c:1384: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1374: Test failed: got 00090317 schannel.c:1384: Test failed: DecryptMessage failed: 80090317
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/schannel.c | 13 ++++++++++++- dlls/secur32/tests/schannel.c | 2 -- 2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 6b699cccce1..fa5577d78e3 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -709,7 +709,7 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( SecBuffer *buffer; SecBuffer alloc_buffer = { 0 }; struct handshake_params params; - int idx; + int idx, i;
TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext, debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput, @@ -724,6 +724,17 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ptsExpiry->HighPart = 0; }
+ if (!pOutput || !pOutput->cBuffers) return SEC_E_INVALID_TOKEN; + for (i = 0; i < pOutput->cBuffers; i++) + { + ULONG buf_type = pOutput->pBuffers[i].BufferType; + + if ((buf_type != SECBUFFER_TOKEN) && (buf_type != SECBUFFER_ALERT)) + continue; + if (!pOutput->pBuffers[i].cbBuffer && !(fContextReq & ISC_REQ_ALLOCATE_MEMORY)) + return SEC_E_INSUFFICIENT_MEMORY; + } + if (!phContext) { ULONG_PTR handle; diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index f72d71a3af3..8b64189bbe4 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1023,7 +1023,6 @@ todo_wine status = InitializeSecurityContextA(&cred_handle, &context, (SEC_CHAR *)"localhost", ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM, 0, 0, &buffers[1], 0, NULL, &buffers[0], &attrs, NULL); -todo_wine ok(status == SEC_E_INSUFFICIENT_MEMORY || status == SEC_E_INVALID_TOKEN, "Expected SEC_E_INSUFFICIENT_MEMORY or SEC_E_INVALID_TOKEN, got %08x\n", status); ok(buffers[0].pBuffers[0].cbBuffer == 0, "Output buffer size was not set to 0.\n"); @@ -1031,7 +1030,6 @@ todo_wine status = InitializeSecurityContextA(&cred_handle, NULL, (SEC_CHAR *)"localhost", ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM, 0, 0, NULL, 0, &context, NULL, &attrs, NULL); -todo_wine ok(status == SEC_E_INVALID_TOKEN, "Expected SEC_E_INVALID_TOKEN, got %08x\n", status);
buffers[0].pBuffers[0].cbBuffer = buf_size;
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=105971
Your paranoid android.
=== w1064 (32 bit report) ===
secur32: schannel.c:1372: Test failed: got 00090317 schannel.c:1382: Test failed: DecryptMessage failed: 80090317
=== w1064 (64 bit report) ===
secur32: schannel.c:1372: Test failed: got 00090317 schannel.c:1382: Test failed: DecryptMessage failed: 80090317
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/secur32/tests/schannel.c | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 8b64189bbe4..6c15addf2fc 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -582,6 +582,12 @@ static void init_buffers(SecBufferDesc *desc, unsigned count, unsigned size) desc->pBuffers[0].pvBuffer = HeapAlloc(GetProcessHeap(), 0, size); }
+static void init_sec_buffer(SecBuffer *sec_buf, ULONG count, void *buf) +{ + sec_buf->cbBuffer = count; + sec_buf->pvBuffer = buf; +} + static void reset_buffers(SecBufferDesc *desc) { unsigned i; @@ -642,6 +648,67 @@ static int receive_data(SOCKET sock, SecBuffer *buf) return received; }
+static void test_context_output_buffer_size(DWORD protocol, DWORD flags, ULONG ctxt_flags_req) +{ + SCHANNEL_CRED cred; + CredHandle cred_handle; + CtxtHandle context; + SECURITY_STATUS status; + SecBuffer in_buffer = {0, SECBUFFER_EMPTY, NULL}; + SecBufferDesc in_buffers = {SECBUFFER_VERSION, 1, &in_buffer}; + SecBufferDesc out_buffers; + unsigned buf_size = 8192; + void *buf, *buf2; + ULONG attrs; + int i; + + init_cred(&cred); + cred.grbitEnabledProtocols = protocol; + cred.dwFlags = flags; + status = AcquireCredentialsHandleA(NULL, (SEC_CHAR *)UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + &cred, NULL, NULL, &cred_handle, NULL); + ok( status == SEC_E_OK, "got %08x\n", status ); + if (status != SEC_E_OK) return; + + init_buffers(&out_buffers, 4, buf_size); + out_buffers.pBuffers[0].BufferType = SECBUFFER_TOKEN; + buf = out_buffers.pBuffers[0].pvBuffer; + buf2 = out_buffers.pBuffers[1].pvBuffer = HeapAlloc(GetProcessHeap(), 0, buf_size); + for (i = 0; i < 2; i++) + { + SecBuffer *buffer = !i ? &out_buffers.pBuffers[0] : &out_buffers.pBuffers[1]; + + init_sec_buffer(&out_buffers.pBuffers[0], buf_size, buf); + if (i) buffer->BufferType = SECBUFFER_ALERT; + + buffer->cbBuffer = 0; + status = InitializeSecurityContextA(&cred_handle, NULL, (SEC_CHAR *)"localhost", ctxt_flags_req, + 0, 0, &in_buffers, 0, &context, &out_buffers, &attrs, NULL); + ok(status == SEC_E_INSUFFICIENT_MEMORY, "%d: Expected SEC_E_INSUFFICIENT_MEMORY, got %08x\n", i, status); + + if (i) init_sec_buffer(&out_buffers.pBuffers[0], buf_size, NULL); + init_sec_buffer(buffer, 0, NULL); + status = InitializeSecurityContextA(&cred_handle, NULL, (SEC_CHAR *)"localhost", + ctxt_flags_req | ISC_REQ_ALLOCATE_MEMORY, 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) FreeContextBuffer(out_buffers.pBuffers[0].pvBuffer); + FreeContextBuffer(buffer->pvBuffer); + DeleteSecurityContext(&context); + + if (i) init_sec_buffer(&out_buffers.pBuffers[0], buf_size, buf); + init_sec_buffer(buffer, buf_size, !i ? buf : buf2); + 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"); + DeleteSecurityContext(&context); + } + + HeapFree(GetProcessHeap(), 0, buf2); + free_buffers(&out_buffers); + FreeCredentialsHandle(&cred_handle); +} + static void test_InitializeSecurityContext(void) { SCHANNEL_CRED cred; @@ -974,6 +1041,9 @@ static void test_communication(void) return; }
+ test_context_output_buffer_size(SP_PROT_TLS1_CLIENT, SCH_CRED_NO_DEFAULT_CREDS|SCH_CRED_MANUAL_CRED_VALIDATION, + ISC_REQ_CONFIDENTIALITY|ISC_REQ_STREAM); + /* Create a socket and connect to test.winehq.org */ if ((sock = create_ssl_socket( "test.winehq.org" )) == -1) return;
@@ -1569,6 +1639,7 @@ static void test_dtls(void)
flags_req = ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_EXTENDED_ERROR | ISC_REQ_DATAGRAM | ISC_REQ_USE_SUPPLIED_CREDS | ISC_REQ_CONFIDENTIALITY | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT; + test_context_output_buffer_size(SP_PROT_DTLS_CLIENT | SP_PROT_DTLS1_2_CLIENT, SCH_CRED_NO_DEFAULT_CREDS, flags_req);
init_buffers( &buffers[0], 1, 128 ); buffers[0].pBuffers[0].BufferType = SECBUFFER_DTLS_MTU;
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=105972
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
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 fa5577d78e3..19e21da8d72 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -809,6 +809,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__ */
On Tue, Jan 25, 2022 at 10:32:11AM -0500, Connor McAdams wrote:
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 fa5577d78e3..19e21da8d72 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -809,6 +809,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__ */
2.25.1
All patches prior to this one are good, but this one and beyond will break things. Maybe this is the universe's way of telling me to send smaller patch sets. ;)
Essentially, the issue seems to be that we need to go back to non-blocking mode with gnutls on DTLS contexts. We behave in a non-blocking manner to begin with, seeing as we return immediately from the pull_timeout callback. If we don't do this, it takes all sorts of effort to work around it, and make it not get into a weird state when no data is received. If it isn't in non-blocking mode, it doesn't seem to complete a handshake if retransmission is involved.
Is there any good reason we switched away from GNUTLS_NONBLOCK in the first place? If so, I can try to work around it. But from my testing, having it enabled doesn't seem to cause issues. But maybe I'm missing something.
On Tue, 2022-01-25 at 19:24 -0500, Connor McAdams wrote:
On Tue, Jan 25, 2022 at 10:32:11AM -0500, Connor McAdams wrote:
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.
All patches prior to this one are good, but this one and beyond will break things. Maybe this is the universe's way of telling me to send smaller patch sets. ;)
Essentially, the issue seems to be that we need to go back to non-blocking mode with gnutls on DTLS contexts. We behave in a non-blocking manner to begin with, seeing as we return immediately from the pull_timeout callback. If we don't do this, it takes all sorts of effort to work around it, and make it not get into a weird state when no data is received. If it isn't in non-blocking mode, it doesn't seem to complete a handshake if retransmission is involved.
Is there any good reason we switched away from GNUTLS_NONBLOCK in the first place? If so, I can try to work around it. But from my testing, having it enabled doesn't seem to cause issues. But maybe I'm missing something.
GnuTLS blocking mode corresponds to the blocking behavior of the underlying transport (sockets, usually). We don't own the socket, so we can't pull data or know what mode is used by the client. In that sense the GnuTLS blocking mode is irrelevant; we always need to return to the caller immediately. If GnuTLS needs to be in non-blocking mode to handle retransmission correctly then we could switch back I guess.
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 19e21da8d72..2c328b96ce4 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -895,6 +895,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=105974
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/schannel_gnutls.c | 2 +- dlls/secur32/tests/schannel.c | 12 +++++----- 3 files changed, 30 insertions(+), 25 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 2c328b96ce4..72be32a2478 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -813,6 +813,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; @@ -824,30 +825,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/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index ac51cbb5d9f..0bf1337e803 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -466,7 +466,7 @@ static int pull_timeout(gnutls_transport_ptr_t transport, unsigned int timeout)
TRACE("\n");
- if (get_buffer(t, &t->in, &count)) return 1; + if (!t->in.limit || get_buffer(t, &t->in, &count)) return 1; pgnutls_transport_set_errno(s, EAGAIN); return -1; } diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 6fbe0c34bd1..902a490a296 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1693,20 +1693,20 @@ 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", + 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=105975
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=105976
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