1
0
mirror of https://github.com/wine-mirror/wine synced 2024-06-29 06:14:34 +00:00

secur32: Add support for sending TLS alerts.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55086
This commit is contained in:
Hans Leidekker 2023-06-20 14:34:57 +02:00 committed by Alexandre Julliard
parent aaf051ffff
commit 11afc2da02
5 changed files with 166 additions and 30 deletions

View File

@ -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, &params );
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 = {

View File

@ -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)
{

View File

@ -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

View File

@ -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 );

View File

@ -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