From: Hans Leidekker hans@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55086 --- dlls/secur32/schannel.c | 46 +++++++++++++---- dlls/secur32/schannel_gnutls.c | 93 +++++++++++++++++++++++++++------- dlls/secur32/secur32_priv.h | 7 ++- dlls/secur32/tests/schannel.c | 13 +++++ include/schannel.h | 37 ++++++++++++++ 5 files changed, 166 insertions(+), 30 deletions(-)
diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c index 05994ed0753..8d446af8fe1 100644 --- a/dlls/secur32/schannel.c +++ b/dlls/secur32/schannel.c @@ -63,7 +63,9 @@ struct schan_context ULONG req_ctx_attr; const CERT_CONTEXT *cert; SIZE_T header_size; - BOOL shutdown_requested; + enum control_token control_token; + unsigned int alert_type; + unsigned int alert_number; };
static struct schan_handle *schan_handle_table; @@ -865,9 +867,9 @@ static SECURITY_STATUS establish_context( unsigned char *ptr;
if (phContext && !(ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX))) return SEC_E_INVALID_HANDLE; - if (!pInput && !ctx->shutdown_requested && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE; + if (!pInput && !ctx->control_token && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE;
- if (!ctx->shutdown_requested && pInput) + if (!ctx->control_token && 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; @@ -940,8 +942,10 @@ static SECURITY_STATUS establish_context( 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; + params.control_token = ctx->control_token; + params.alert_type = ctx->alert_type; + params.alert_number = ctx->alert_number; + ctx->control_token = CONTROL_TOKEN_NONE; ret = GNUTLS_CALL( handshake, ¶ms );
if (output_buffer_idx != -1) @@ -1588,23 +1592,43 @@ 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; + DWORD type;
TRACE("%p %p\n", context_handle, input);
dump_buffer_desc(input);
- if (!context_handle) return SEC_E_INVALID_HANDLE; + if (!context_handle || !(ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX))) + 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; + if (input->pBuffers[0].cbBuffer < sizeof(type)) return SEC_E_UNSUPPORTED_FUNCTION; + type = *(DWORD *)input->pBuffers[0].pvBuffer;
- ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX); - ctx->shutdown_requested = TRUE; + switch (type) + { + case SCHANNEL_SHUTDOWN: + ctx->control_token = CONTROL_TOKEN_SHUTDOWN; + ctx->alert_type = TLS1_ALERT_WARNING; + ctx->alert_number = TLS1_ALERT_CLOSE_NOTIFY; + return SEC_E_OK;
- return SEC_E_OK; + case SCHANNEL_ALERT: + { + SCHANNEL_ALERT_TOKEN *alert = input->pBuffers[0].pvBuffer; + if (input->pBuffers[0].cbBuffer < sizeof(*alert)) return SEC_E_INVALID_TOKEN; + ctx->control_token = CONTROL_TOKEN_ALERT; + ctx->alert_type = alert->dwAlertType; + ctx->alert_number = alert->dwAlertNumber; + return SEC_E_OK; + } + + default: + FIXME("token type %lu not supported\n", type); + return SEC_E_UNSUPPORTED_FUNCTION; + } }
static const SecurityFunctionTableA schanTableA = { diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c index 06d56fccee1..600b0d0166a 100644 --- a/dlls/secur32/schannel_gnutls.c +++ b/dlls/secur32/schannel_gnutls.c @@ -84,6 +84,7 @@ static void *libgnutls_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f MAKE_FUNCPTR(gnutls_alert_get); MAKE_FUNCPTR(gnutls_alert_get_name); +MAKE_FUNCPTR(gnutls_alert_send); MAKE_FUNCPTR(gnutls_certificate_allocate_credentials); MAKE_FUNCPTR(gnutls_certificate_free_credentials); MAKE_FUNCPTR(gnutls_certificate_get_peers); @@ -121,7 +122,6 @@ 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 @@ -566,6 +566,74 @@ static NTSTATUS schan_set_session_target( void *args ) return STATUS_SUCCESS; }
+static gnutls_alert_level_t map_alert_type(unsigned int type) +{ + switch (type) + { + case TLS1_ALERT_WARNING: return GNUTLS_AL_WARNING; + case TLS1_ALERT_FATAL: return GNUTLS_AL_FATAL; + default: + FIXME( "unknown type %u\n", type ); + return -1; + } +} + +static gnutls_alert_description_t map_alert_number(unsigned int number) +{ + switch (number) + { + case TLS1_ALERT_CLOSE_NOTIFY: return GNUTLS_A_CLOSE_NOTIFY; + case TLS1_ALERT_UNEXPECTED_MESSAGE: return GNUTLS_A_UNEXPECTED_MESSAGE; + case TLS1_ALERT_BAD_RECORD_MAC: return GNUTLS_A_BAD_RECORD_MAC; + case TLS1_ALERT_DECRYPTION_FAILED: return GNUTLS_A_DECRYPTION_FAILED; + case TLS1_ALERT_RECORD_OVERFLOW: return GNUTLS_A_RECORD_OVERFLOW; + case TLS1_ALERT_DECOMPRESSION_FAIL: return GNUTLS_A_DECOMPRESSION_FAILURE; + case TLS1_ALERT_HANDSHAKE_FAILURE: return GNUTLS_A_HANDSHAKE_FAILURE; + case TLS1_ALERT_BAD_CERTIFICATE: return GNUTLS_A_BAD_CERTIFICATE; + case TLS1_ALERT_UNSUPPORTED_CERT: return GNUTLS_A_UNSUPPORTED_CERTIFICATE; + case TLS1_ALERT_CERTIFICATE_REVOKED: return GNUTLS_A_CERTIFICATE_REVOKED; + case TLS1_ALERT_CERTIFICATE_EXPIRED: return GNUTLS_A_CERTIFICATE_EXPIRED; + case TLS1_ALERT_CERTIFICATE_UNKNOWN: return GNUTLS_A_CERTIFICATE_UNKNOWN; + case TLS1_ALERT_ILLEGAL_PARAMETER: return GNUTLS_A_ILLEGAL_PARAMETER; + case TLS1_ALERT_UNKNOWN_CA: return GNUTLS_A_UNKNOWN_CA; + case TLS1_ALERT_ACCESS_DENIED: return GNUTLS_A_ACCESS_DENIED; + case TLS1_ALERT_DECODE_ERROR: return GNUTLS_A_DECODE_ERROR; + case TLS1_ALERT_DECRYPT_ERROR: return GNUTLS_A_DECRYPT_ERROR; + case TLS1_ALERT_EXPORT_RESTRICTION: return GNUTLS_A_EXPORT_RESTRICTION; + case TLS1_ALERT_PROTOCOL_VERSION: return GNUTLS_A_PROTOCOL_VERSION; + case TLS1_ALERT_INSUFFIENT_SECURITY: return GNUTLS_A_INSUFFICIENT_SECURITY; + case TLS1_ALERT_INTERNAL_ERROR: return GNUTLS_A_INTERNAL_ERROR; + case TLS1_ALERT_USER_CANCELED: return GNUTLS_A_USER_CANCELED; + case TLS1_ALERT_NO_RENEGOTIATION: return GNUTLS_A_NO_RENEGOTIATION; + case TLS1_ALERT_UNSUPPORTED_EXT: return GNUTLS_A_UNSUPPORTED_EXTENSION; + case TLS1_ALERT_UNKNOWN_PSK_IDENTITY: return GNUTLS_A_UNKNOWN_PSK_IDENTITY; + case TLS1_ALERT_NO_APP_PROTOCOL: return GNUTLS_A_NO_APPLICATION_PROTOCOL; + default: + FIXME("unhandled alert %u\n", number); + return -1; + } +} + +static NTSTATUS send_alert(gnutls_session_t session, unsigned int type, unsigned int number) +{ + gnutls_alert_level_t level = map_alert_type(type); + gnutls_alert_description_t desc = map_alert_number(number); + int ret; + + do + { + ret = pgnutls_alert_send(session, level, desc); + } + while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN); + + if (ret < 0) + { + pgnutls_perror(ret); + return SEC_E_INTERNAL_ERROR; + } + return SEC_E_OK; +} + static NTSTATUS schan_handshake( void *args ) { const struct handshake_params *params = args; @@ -578,22 +646,9 @@ 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) + if (params->control_token) { - 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; - } + status = send_alert(s, params->alert_type, params->alert_number); goto done; }
@@ -1431,6 +1486,7 @@ static NTSTATUS process_attach( void *args )
LOAD_FUNCPTR(gnutls_alert_get) LOAD_FUNCPTR(gnutls_alert_get_name) + LOAD_FUNCPTR(gnutls_alert_send) LOAD_FUNCPTR(gnutls_certificate_allocate_credentials) LOAD_FUNCPTR(gnutls_certificate_free_credentials) LOAD_FUNCPTR(gnutls_certificate_get_peers) @@ -1468,7 +1524,6 @@ 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"))) @@ -1751,6 +1806,8 @@ static NTSTATUS wow64_schan_handshake( void *args ) PTR32 output_buffer_idx; PTR32 output_offset; enum control_token control_token; + unsigned int alert_type; + unsigned int alert_number; } const *params32 = args; struct handshake_params params = { @@ -1762,6 +1819,8 @@ static NTSTATUS wow64_schan_handshake( void *args ) ULongToPtr(params32->output_buffer_idx), ULongToPtr(params32->output_offset), params32->control_token, + params32->alert_type, + params32->alert_number, }; if (params32->input) { diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h index 258454560fe..1275a713896 100644 --- a/dlls/secur32/secur32_priv.h +++ b/dlls/secur32/secur32_priv.h @@ -147,8 +147,9 @@ struct get_unique_channel_binding_params
enum control_token { - control_token_none, - control_token_shutdown, + CONTROL_TOKEN_NONE, + CONTROL_TOKEN_SHUTDOWN, + CONTROL_TOKEN_ALERT, };
struct handshake_params @@ -161,6 +162,8 @@ struct handshake_params int *output_buffer_idx; ULONG *output_offset; enum control_token control_token; + unsigned int alert_type; + unsigned int alert_number; };
struct recv_params diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c index 5153c9b4a1e..1379c2eb969 100644 --- a/dlls/secur32/tests/schannel.c +++ b/dlls/secur32/tests/schannel.c @@ -1963,6 +1963,7 @@ static void test_connection_shutdown(void) CredHandle cred_handle; SCHANNEL_CRED cred; SecBuffer *buf; + SCHANNEL_ALERT_TOKEN alert; ULONG attrs; void *tmp;
@@ -2075,6 +2076,18 @@ static void test_connection_shutdown(void) ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); ok( !memcmp( buf->pvBuffer, message, sizeof(message) ), "message data mismatch.\n" );
+ alert.dwTokenType = SCHANNEL_ALERT; + alert.dwAlertType = TLS1_ALERT_FATAL; + alert.dwAlertNumber = TLS1_ALERT_BAD_CERTIFICATE; + memcpy(buf->pvBuffer, &alert, sizeof(alert)); + buf->cbBuffer = sizeof(alert); + status = ApplyControlToken( &context, buffers ); + ok( status == SEC_E_OK, "got %08lx.\n", status ); + + status = InitializeSecurityContextA( &cred_handle, &context, NULL, 0, 0, 0, + NULL, 0, NULL, &buffers[1], &attrs, NULL ); + ok( status == SEC_E_OK, "got %08lx.\n", status ); + free_buffers( &buffers[0] ); free_buffers( &buffers[1] ); DeleteSecurityContext( &context ); diff --git a/include/schannel.h b/include/schannel.h index 3aa85832fd3..cbe1a131381 100644 --- a/include/schannel.h +++ b/include/schannel.h @@ -88,6 +88,43 @@ static const WCHAR SCHANNEL_NAME_W[] = { 'S','c','h','a','n','n','e','l',0 }; #define SCHANNEL_ALERT 2 #define SCHANNEL_SESSION 3
+typedef struct _SCHANNEL_ALERT_TOKEN +{ + DWORD dwTokenType; + DWORD dwAlertType; + DWORD dwAlertNumber; +} SCHANNEL_ALERT_TOKEN; + +#define TLS1_ALERT_WARNING 1 +#define TLS1_ALERT_FATAL 2 + +#define TLS1_ALERT_CLOSE_NOTIFY 0 +#define TLS1_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS1_ALERT_BAD_RECORD_MAC 20 +#define TLS1_ALERT_DECRYPTION_FAILED 21 +#define TLS1_ALERT_RECORD_OVERFLOW 22 +#define TLS1_ALERT_DECOMPRESSION_FAIL 30 +#define TLS1_ALERT_HANDSHAKE_FAILURE 40 +#define TLS1_ALERT_BAD_CERTIFICATE 42 +#define TLS1_ALERT_UNSUPPORTED_CERT 43 +#define TLS1_ALERT_CERTIFICATE_REVOKED 44 +#define TLS1_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS1_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS1_ALERT_ILLEGAL_PARAMETER 47 +#define TLS1_ALERT_UNKNOWN_CA 48 +#define TLS1_ALERT_ACCESS_DENIED 49 +#define TLS1_ALERT_DECODE_ERROR 50 +#define TLS1_ALERT_DECRYPT_ERROR 51 +#define TLS1_ALERT_EXPORT_RESTRICTION 60 +#define TLS1_ALERT_PROTOCOL_VERSION 70 +#define TLS1_ALERT_INSUFFIENT_SECURITY 71 +#define TLS1_ALERT_INTERNAL_ERROR 80 +#define TLS1_ALERT_USER_CANCELED 90 +#define TLS1_ALERT_NO_RENEGOTIATION 100 +#define TLS1_ALERT_UNSUPPORTED_EXT 110 +#define TLS1_ALERT_UNKNOWN_PSK_IDENTITY 115 +#define TLS1_ALERT_NO_APP_PROTOCOL 120 + #define SP_PROT_ALL 0xffffffff #define SP_PROT_UNI_CLIENT 0x80000000 #define SP_PROT_UNI_SERVER 0x40000000
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=134076
Your paranoid android.
=== debian11 (32 bit report) ===
Report validation errors: quartz:vmr7 has no test summary line (early exit of the main process?) quartz:vmr7 has unaccounted for todo messages quartz:vmr7 has unaccounted for skip messages quartz:vmr9 has no test summary line (early exit of the main process?) quartz:vmr9 has unaccounted for todo messages