... and fix output buffer allocation on the way.
I am not aware of any app which strictly depends on the ApplyControlToken / connection shutdown to be implemented. Yet I saw a few games calling ApplyToken to perform the connection shutdown (similar to what is described in [1]). I once implemented that while debugging something and probably it makes some sense support secure connection shutdown once the app tries to do so (which as I understand is now recommended to do before closing socket).
1. https://learn.microsoft.com/en-us/windows/win32/secauthn/shutting-down-an-sc...
From: Paul Gofman pgofman@codeweavers.com
--- dlls/secur32/tests/schannel.c | 133 ++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+)
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 25ae197bca6..9c3d40667e0 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1778,6 +1778,138 @@ static void test_dtls(void) FreeCredentialsHandle( &cred_handle ); }
+static void test_connection_shutdown(void) +{ + static const BYTE message_broken[] = {0x15, 0x03, 0x01, 0x00, 0x02, 0x01, 0x00}; + static const BYTE message[] = {0x15, 0x03, 0x03, 0x00, 0x02, 0x01, 0x00}; + CtxtHandle context, context2; + SecBufferDesc buffers[2]; + SECURITY_STATUS status; + CredHandle cred_handle; + SCHANNEL_CRED cred; + SecBuffer *buf; + ULONG attrs; + void *tmp; + + init_cred(&cred); + cred.grbitEnabledProtocols = 0; + cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION; + + status = AcquireCredentialsHandleA( NULL, (SEC_CHAR *)UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + &cred, NULL, NULL, &cred_handle, NULL ); + ok( status == SEC_E_OK, "got %08lx\n", status ); + + init_buffers( &buffers[0], 2, 0 ); + init_buffers( &buffers[1], 1, 1000 ); + buffers[0].cBuffers = 1; + buffers[0].pBuffers[0].BufferType = SECBUFFER_EMPTY; + buffers[1].cBuffers = 1; + buffers[1].pBuffers[0].BufferType = SECBUFFER_TOKEN; + buffers[1].pBuffers[0].cbBuffer = 1000; + tmp = buffers[1].pBuffers[0].pvBuffer; + buffers[1].pBuffers[0].pvBuffer = NULL; + status = InitializeSecurityContextA( &cred_handle, NULL, (SEC_CHAR *)"localhost", + ISC_REQ_CONFIDENTIALITY | ISC_REQ_STREAM, + 0, 0, &buffers[0], 0, &context, &buffers[1], &attrs, NULL ); + ok( status == SEC_I_CONTINUE_NEEDED, "Expected SEC_I_CONTINUE_NEEDED, got %08lx\n", status ); + todo_wine ok( !!buffers[1].pBuffers[0].pvBuffer, "Got NULL buffer.\n" ); + FreeContextBuffer( buffers[1].pBuffers[0].pvBuffer ); + buffers[1].pBuffers[0].pvBuffer = tmp; + + buf = &buffers[0].pBuffers[0]; + buffers[0].cBuffers = 2; + buf->cbBuffer = sizeof(DWORD); + *(DWORD *)buf->pvBuffer = SCHANNEL_SHUTDOWN; + buf->BufferType = SECBUFFER_TOKEN; + buffers[0].pBuffers[1] = buffers[0].pBuffers[0]; + + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + + buffers[0].pBuffers[1].cbBuffer = 0; + buffers[0].pBuffers[1].BufferType = SECBUFFER_EMPTY; + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + + *(DWORD *)buf->pvBuffer = SCHANNEL_RENEGOTIATE; + buffers[0].cBuffers = 1; + status = ApplyControlToken( &context, buffers ); + ok( status == SEC_E_UNSUPPORTED_FUNCTION, "got %08lx.\n", status ); + + status = ApplyControlToken(NULL, buffers); + ok( status == SEC_E_INVALID_HANDLE, "got %08lx.\n", status ); + + status = ApplyControlToken( &context, NULL ); + todo_wine ok( status == SEC_E_INTERNAL_ERROR, "got %08lx.\n", status ); + + *(DWORD *)buf->pvBuffer = SCHANNEL_SHUTDOWN; + + buf->BufferType = SECBUFFER_ALERT; + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + buf->BufferType = SECBUFFER_DATA; + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + + buf->BufferType = SECBUFFER_TOKEN; + + buf->cbBuffer = 2; + status = ApplyControlToken( &context, buffers ); + ok( status == SEC_E_UNSUPPORTED_FUNCTION, "got %08lx.\n", status ); + + buf->cbBuffer = sizeof(DWORD) + 1; + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + + buf->cbBuffer = 1000; + buf->BufferType = SECBUFFER_TOKEN; + context2.dwLower = context2.dwUpper = 0xdeadbeef; + status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, &buffers[1], 0, + &context2, &buffers[0], &attrs, NULL ); + todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + todo_wine ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#Ix, got %#Ix\n", + context.dwLower, context2.dwLower ); + todo_wine ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#Ix, got %#Ix\n", + context.dwUpper, context2.dwUpper ); + todo_wine ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); + todo_wine ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) + || broken( memcmp( buf->pvBuffer, message_broken, sizeof(message_broken) )) /* win7 */, + "message data mismatch.\n" ); + + buf->BufferType = SECBUFFER_TOKEN; + buf->cbBuffer = 1000; + context2.dwLower = context2.dwUpper = 0xdeadbeef; + status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, NULL, 0, + &context2, &buffers[1], &attrs, NULL ); + todo_wine ok( status == SEC_E_INCOMPLETE_MESSAGE, "got %08lx.\n", status ); + ok( buf->cbBuffer == 1000, "got cbBuffer %#lx.\n", buf->cbBuffer ); + ok( context2.dwLower == 0xdeadbeef, "dwLower mismatch, got %#Ix\n", context2.dwLower ); + ok( context2.dwUpper == 0xdeadbeef, "dwUpper mismatch, got %#Ix\n", context2.dwUpper ); + + buf->cbBuffer = sizeof(DWORD); + *(DWORD *)buf->pvBuffer = SCHANNEL_SHUTDOWN; + buf->BufferType = SECBUFFER_TOKEN; + status = ApplyControlToken( &context, buffers ); + todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + + buf->cbBuffer = 1000; + status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, + NULL, 0, NULL, &buffers[0], &attrs, NULL ); + todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + todo_wine ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); + todo_wine ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) + || broken( memcmp( buf->pvBuffer, message_broken, sizeof(message_broken) )) /* win7 */, + "message data mismatch.\n" ); + + free_buffers( &buffers[0] ); + free_buffers( &buffers[1] ); + DeleteSecurityContext( &context ); + FreeCredentialsHandle( &cred_handle ); +} + START_TEST(schannel) { WSADATA wsa_data; @@ -1791,4 +1923,5 @@ START_TEST(schannel) test_communication(); test_application_protocol_negotiation(); test_dtls(); + test_connection_shutdown(); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/secur32/schannel.c | 14 ++++++-------- dlls/secur32/tests/schannel.c | 11 ++++++----- 2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 4699f79ac1f..76b35c2419e 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -945,13 +945,6 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ctx->req_ctx_attr = fContextReq;
/* Perform the TLS handshake */ - if (fContextReq & ISC_REQ_ALLOCATE_MEMORY) - { - alloc_buffer.cbBuffer = extra_size; - alloc_buffer.BufferType = SECBUFFER_TOKEN; - alloc_buffer.pvBuffer = RtlAllocateHeap( GetProcessHeap(), 0, extra_size ); - } - memset(&input_desc, 0, sizeof(input_desc)); if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) != -1) { @@ -967,8 +960,13 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( { output_desc.cBuffers = 1; output_desc.pBuffers = &pOutput->pBuffers[idx]; - if (!output_desc.pBuffers->pvBuffer) + if (!output_desc.pBuffers->pvBuffer || (fContextReq & ISC_REQ_ALLOCATE_MEMORY)) + { + alloc_buffer.cbBuffer = extra_size; + alloc_buffer.BufferType = SECBUFFER_TOKEN; + alloc_buffer.pvBuffer = RtlAllocateHeap( GetProcessHeap(), 0, extra_size ); output_desc.pBuffers = &alloc_buffer; + } }
params.session = ctx->session; diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 9c3d40667e0..0a4b74ed29f 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -695,11 +695,12 @@ static void test_context_output_buffer_size(DWORD protocol, DWORD flags, ULONG c 0, 0, &in_buffers, 0, &context, &out_buffers, &attrs, NULL); ok(status == SEC_E_INSUFFICIENT_MEMORY, "%d: Expected SEC_E_INSUFFICIENT_MEMORY, got %08lx\n", i, status);
- if (i) init_sec_buffer(&out_buffers.pBuffers[0], buf_size, NULL); + if (i) init_sec_buffer(&out_buffers.pBuffers[0], buf_size, (void *)0xdeadbeef); 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 %08lx\n", i, status); + ok(out_buffers.pBuffers[0].pvBuffer != (void *)0xdeadbeef, "got %p.\n", out_buffers.pBuffers[0].pvBuffer); if (i) FreeContextBuffer(out_buffers.pBuffers[0].pvBuffer); FreeContextBuffer(buffer->pvBuffer); DeleteSecurityContext(&context); @@ -1812,7 +1813,7 @@ static void test_connection_shutdown(void) ISC_REQ_CONFIDENTIALITY | ISC_REQ_STREAM, 0, 0, &buffers[0], 0, &context, &buffers[1], &attrs, NULL ); ok( status == SEC_I_CONTINUE_NEEDED, "Expected SEC_I_CONTINUE_NEEDED, got %08lx\n", status ); - todo_wine ok( !!buffers[1].pBuffers[0].pvBuffer, "Got NULL buffer.\n" ); + ok( !!buffers[1].pBuffers[0].pvBuffer, "Got NULL buffer.\n" ); FreeContextBuffer( buffers[1].pBuffers[0].pvBuffer ); buffers[1].pBuffers[0].pvBuffer = tmp;
@@ -1870,9 +1871,9 @@ static void test_connection_shutdown(void) status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL ); todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); - todo_wine ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#Ix, got %#Ix\n", + ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#Ix, got %#Ix\n", context.dwLower, context2.dwLower ); - todo_wine ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#Ix, got %#Ix\n", + ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#Ix, got %#Ix\n", context.dwUpper, context2.dwUpper ); todo_wine ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); todo_wine ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) @@ -1884,7 +1885,7 @@ static void test_connection_shutdown(void) context2.dwLower = context2.dwUpper = 0xdeadbeef; status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, NULL, 0, &context2, &buffers[1], &attrs, NULL ); - todo_wine ok( status == SEC_E_INCOMPLETE_MESSAGE, "got %08lx.\n", status ); + ok( status == SEC_E_INCOMPLETE_MESSAGE, "got %08lx.\n", status ); ok( buf->cbBuffer == 1000, "got cbBuffer %#lx.\n", buf->cbBuffer ); ok( context2.dwLower == 0xdeadbeef, "dwLower mismatch, got %#Ix\n", context2.dwLower ); ok( context2.dwUpper == 0xdeadbeef, "dwUpper mismatch, got %#Ix\n", context2.dwUpper );
From: Paul Gofman pgofman@codeweavers.com
--- dlls/secur32/schannel.c | 23 +++++++++++++++++++++-- dlls/secur32/tests/schannel.c | 16 ++++++++-------- 2 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 76b35c2419e..29b870e95a5 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -1573,6 +1573,25 @@ static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context return SEC_E_OK; }
+static SECURITY_STATUS SEC_ENTRY schan_ApplyControlToken(PCtxtHandle context_handle, PSecBufferDesc input) +{ + TRACE("%p %p\n", context_handle, input); + + dump_buffer_desc(input); + + if (!context_handle) return SEC_E_INVALID_HANDLE; + if (!input) return SEC_E_INTERNAL_ERROR; + + if (input->cBuffers != 1) return SEC_E_INVALID_TOKEN; + if (input->pBuffers[0].BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN; + if (input->pBuffers[0].cbBuffer < sizeof(DWORD)) return SEC_E_UNSUPPORTED_FUNCTION; + if (*(DWORD *)input->pBuffers[0].pvBuffer != SCHANNEL_SHUTDOWN) return SEC_E_UNSUPPORTED_FUNCTION; + + FIXME("stub.\n"); + + return SEC_E_OK; +} + static const SecurityFunctionTableA schanTableA = { 1, NULL, /* EnumerateSecurityPackagesA */ @@ -1584,7 +1603,7 @@ static const SecurityFunctionTableA schanTableA = { NULL, /* AcceptSecurityContext */ NULL, /* CompleteAuthToken */ schan_DeleteSecurityContext, - NULL, /* ApplyControlToken */ + schan_ApplyControlToken, /* ApplyControlToken */ schan_QueryContextAttributesA, NULL, /* ImpersonateSecurityContext */ NULL, /* RevertSecurityContext */ @@ -1615,7 +1634,7 @@ static const SecurityFunctionTableW schanTableW = { NULL, /* AcceptSecurityContext */ NULL, /* CompleteAuthToken */ schan_DeleteSecurityContext, - NULL, /* ApplyControlToken */ + schan_ApplyControlToken, /* ApplyControlToken */ schan_QueryContextAttributesW, NULL, /* ImpersonateSecurityContext */ NULL, /* RevertSecurityContext */ diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 0a4b74ed29f..9882406cb9e 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1825,12 +1825,12 @@ static void test_connection_shutdown(void) buffers[0].pBuffers[1] = buffers[0].pBuffers[0];
status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status );
buffers[0].pBuffers[1].cbBuffer = 0; buffers[0].pBuffers[1].BufferType = SECBUFFER_EMPTY; status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status );
*(DWORD *)buf->pvBuffer = SCHANNEL_RENEGOTIATE; buffers[0].cBuffers = 1; @@ -1841,16 +1841,16 @@ static void test_connection_shutdown(void) ok( status == SEC_E_INVALID_HANDLE, "got %08lx.\n", status );
status = ApplyControlToken( &context, NULL ); - todo_wine ok( status == SEC_E_INTERNAL_ERROR, "got %08lx.\n", status ); + ok( status == SEC_E_INTERNAL_ERROR, "got %08lx.\n", status );
*(DWORD *)buf->pvBuffer = SCHANNEL_SHUTDOWN;
buf->BufferType = SECBUFFER_ALERT; status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); buf->BufferType = SECBUFFER_DATA; status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status ); + ok( status == SEC_E_INVALID_TOKEN, "got %08lx.\n", status );
buf->BufferType = SECBUFFER_TOKEN;
@@ -1860,10 +1860,10 @@ static void test_connection_shutdown(void)
buf->cbBuffer = sizeof(DWORD) + 1; status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + ok( status == SEC_E_OK, "got %08lx.\n", status );
status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + ok( status == SEC_E_OK, "got %08lx.\n", status );
buf->cbBuffer = 1000; buf->BufferType = SECBUFFER_TOKEN; @@ -1894,7 +1894,7 @@ static void test_connection_shutdown(void) *(DWORD *)buf->pvBuffer = SCHANNEL_SHUTDOWN; buf->BufferType = SECBUFFER_TOKEN; status = ApplyControlToken( &context, buffers ); - todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + ok( status == SEC_E_OK, "got %08lx.\n", status );
buf->cbBuffer = 1000; status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0,
From: Paul Gofman pgofman@codeweavers.com
--- dlls/secur32/schannel.c | 12 +++++++++--- dlls/secur32/schannel_gnutls.c | 24 ++++++++++++++++++++++++ dlls/secur32/secur32_priv.h | 7 +++++++ dlls/secur32/tests/schannel.c | 12 ++++++------ 4 files changed, 46 insertions(+), 9 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 29b870e95a5..98c6f93bc1c 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -65,6 +65,7 @@ struct schan_context ULONG req_ctx_attr; const CERT_CONTEXT *cert; SIZE_T header_size; + BOOL shutdown_requested; };
static struct schan_handle *schan_handle_table; @@ -901,9 +902,9 @@ 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 && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE; + if (!pInput && !ctx->shutdown_requested && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE;
- if (pInput) + if (!ctx->shutdown_requested && pInput) { if (!validate_input_buffers(pInput)) return SEC_E_INVALID_TOKEN; if ((idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) == -1) return SEC_E_INCOMPLETE_MESSAGE; @@ -976,6 +977,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( params.input_offset = &input_offset; params.output_buffer_idx = &output_buffer_idx; params.output_offset = &output_offset; + params.control_token = ctx->shutdown_requested ? control_token_shutdown : control_token_none; + ctx->shutdown_requested = FALSE; ret = GNUTLS_CALL( handshake, ¶ms );
if (output_buffer_idx != -1) @@ -1575,6 +1578,8 @@ static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context
static SECURITY_STATUS SEC_ENTRY schan_ApplyControlToken(PCtxtHandle context_handle, PSecBufferDesc input) { + struct schan_context *ctx; + TRACE("%p %p\n", context_handle, input);
dump_buffer_desc(input); @@ -1587,7 +1592,8 @@ static SECURITY_STATUS SEC_ENTRY schan_ApplyControlToken(PCtxtHandle context_han if (input->pBuffers[0].cbBuffer < sizeof(DWORD)) return SEC_E_UNSUPPORTED_FUNCTION; if (*(DWORD *)input->pBuffers[0].pvBuffer != SCHANNEL_SHUTDOWN) return SEC_E_UNSUPPORTED_FUNCTION;
- FIXME("stub.\n"); + ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX); + ctx->shutdown_requested = TRUE;
return SEC_E_OK; } diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index 6d65f41ca1b..b26344aa85e 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -121,6 +121,7 @@ MAKE_FUNCPTR(gnutls_x509_crt_deinit); MAKE_FUNCPTR(gnutls_x509_crt_import); MAKE_FUNCPTR(gnutls_x509_crt_init); MAKE_FUNCPTR(gnutls_x509_privkey_deinit); +MAKE_FUNCPTR(gnutls_alert_send); #undef MAKE_FUNCPTR
#if GNUTLS_VERSION_MAJOR < 3 @@ -557,6 +558,25 @@ static NTSTATUS schan_handshake( void *args ) t->in.limit = params->input_size; init_schan_buffers(&t->out, params->output);
+ if (params->control_token == control_token_shutdown) + { + err = pgnutls_alert_send(s, GNUTLS_AL_WARNING, GNUTLS_A_CLOSE_NOTIFY); + if (err == GNUTLS_E_SUCCESS) + { + status = SEC_E_OK; + } + else if (err == GNUTLS_E_AGAIN) + { + status = SEC_E_INVALID_TOKEN; + } + else + { + pgnutls_perror(err); + status = SEC_E_INTERNAL_ERROR; + } + goto done; + } + while (1) { err = pgnutls_handshake(s); @@ -598,6 +618,7 @@ static NTSTATUS schan_handshake( void *args ) break; }
+done: *params->input_offset = t->in.offset; *params->output_buffer_idx = t->out.current_buffer_idx; *params->output_offset = t->out.offset; @@ -1427,6 +1448,7 @@ static NTSTATUS process_attach( void *args ) LOAD_FUNCPTR(gnutls_x509_crt_import) LOAD_FUNCPTR(gnutls_x509_crt_init) LOAD_FUNCPTR(gnutls_x509_privkey_deinit) + LOAD_FUNCPTR(gnutls_alert_send) #undef LOAD_FUNCPTR
if (!(pgnutls_cipher_get_block_size = dlsym(libgnutls_handle, "gnutls_cipher_get_block_size"))) @@ -1707,6 +1729,7 @@ static NTSTATUS wow64_schan_handshake( void *args ) PTR32 input_offset; PTR32 output_buffer_idx; PTR32 output_offset; + enum control_token control_token; } const *params32 = args; struct handshake_params params = { @@ -1717,6 +1740,7 @@ static NTSTATUS wow64_schan_handshake( void *args ) ULongToPtr(params32->input_offset), ULongToPtr(params32->output_buffer_idx), ULongToPtr(params32->output_offset), + params32->control_token, }; if (params32->input) { diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h index d1321b7d6fd..c43b1f446c4 100644 --- a/dlls/secur32/secur32_priv.h +++ b/dlls/secur32/secur32_priv.h @@ -147,6 +147,12 @@ struct get_unique_channel_binding_params ULONG *bufsize; };
+enum control_token +{ + control_token_none, + control_token_shutdown, +}; + struct handshake_params { schan_session session; @@ -156,6 +162,7 @@ struct handshake_params ULONG *input_offset; int *output_buffer_idx; ULONG *output_offset; + enum control_token control_token; };
struct recv_params diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 9882406cb9e..d9edee01811 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1870,13 +1870,13 @@ static void test_connection_shutdown(void) context2.dwLower = context2.dwUpper = 0xdeadbeef; status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, &buffers[1], 0, &context2, &buffers[0], &attrs, NULL ); - todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); + ok( status == SEC_E_OK, "got %08lx.\n", status ); ok( context.dwLower == context2.dwLower, "dwLower mismatch, expected %#Ix, got %#Ix\n", context.dwLower, context2.dwLower ); ok( context.dwUpper == context2.dwUpper, "dwUpper mismatch, expected %#Ix, got %#Ix\n", context.dwUpper, context2.dwUpper ); - todo_wine ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); - todo_wine ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) + ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); + ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) || broken( memcmp( buf->pvBuffer, message_broken, sizeof(message_broken) )) /* win7 */, "message data mismatch.\n" );
@@ -1899,9 +1899,9 @@ static void test_connection_shutdown(void) buf->cbBuffer = 1000; status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, NULL, 0, NULL, &buffers[0], &attrs, NULL ); - todo_wine ok( status == SEC_E_OK, "got %08lx.\n", status ); - todo_wine ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); - todo_wine ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) + ok( status == SEC_E_OK, "got %08lx.\n", status ); + ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); + ok( !memcmp( buf->pvBuffer, message, sizeof(message) ) || broken( memcmp( buf->pvBuffer, message_broken, sizeof(message_broken) )) /* win7 */, "message data mismatch.\n" );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=125877
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
secur32: schannel.c:1879: Test failed: message data mismatch. schannel.c:1904: Test failed: message data mismatch.
=== w7u_adm (32 bit report) ===
secur32: schannel.c:1879: Test failed: message data mismatch. schannel.c:1904: Test failed: message data mismatch.
=== w7u_el (32 bit report) ===
secur32: schannel.c:1879: Test failed: message data mismatch. schannel.c:1904: Test failed: message data mismatch.