... 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...
-- v2: secur32: Implement ApplyControlToken for SCHANNEL_SHUTDOWN. secur32: Add semi-stub for ApplyControlToken (schannel). secur32: Allocate buffer for either ISC_REQ_ALLOCATE_MEMORY or NULL output in schan_InitializeSecurityContextW(). secure32/tests: Add tests for ApplyControlToken (schannel).
From: Paul Gofman pgofman@codeweavers.com
--- dlls/secur32/tests/schannel.c | 128 ++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+)
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 25ae197bca6..9d911ac9603 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1778,6 +1778,133 @@ static void test_dtls(void) FreeCredentialsHandle( &cred_handle ); }
+static void test_connection_shutdown(void) +{ + static const BYTE message[] = {0x15, 0x03, 0x01, 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 = SP_PROT_TLS1_CLIENT; + 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) ), "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) ), "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 +1918,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 9d911ac9603..c22cc0ab3c8 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); @@ -1811,7 +1812,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;
@@ -1869,9 +1870,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) ), "message data mismatch.\n" ); @@ -1881,7 +1882,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 c22cc0ab3c8..62ef9c75837 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1824,12 +1824,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; @@ -1840,16 +1840,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;
@@ -1859,10 +1859,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; @@ -1891,7 +1891,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 62ef9c75837..33915351cb3 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1869,13 +1869,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) ), "message data mismatch.\n" ); + ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); + ok( !memcmp( buf->pvBuffer, message, sizeof(message) ), "message data mismatch.\n" );
buf->BufferType = SECBUFFER_TOKEN; buf->cbBuffer = 1000; @@ -1896,9 +1896,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) ), "message data mismatch.\n" ); + 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) ), "message data mismatch.\n" );
free_buffers( &buffers[0] ); free_buffers( &buffers[1] );
This merge request was approved by Hans Leidekker.