Commit 11afc2da authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

secur32: Add support for sending TLS alerts.

parent aaf051ff
...@@ -63,7 +63,9 @@ struct schan_context ...@@ -63,7 +63,9 @@ struct schan_context
ULONG req_ctx_attr; ULONG req_ctx_attr;
const CERT_CONTEXT *cert; const CERT_CONTEXT *cert;
SIZE_T header_size; 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; static struct schan_handle *schan_handle_table;
...@@ -865,9 +867,9 @@ static SECURITY_STATUS establish_context( ...@@ -865,9 +867,9 @@ static SECURITY_STATUS establish_context(
unsigned char *ptr; unsigned char *ptr;
if (phContext && !(ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX))) return SEC_E_INVALID_HANDLE; 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 (!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; 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( ...@@ -940,8 +942,10 @@ static SECURITY_STATUS establish_context(
params.input_offset = &input_offset; params.input_offset = &input_offset;
params.output_buffer_idx = &output_buffer_idx; params.output_buffer_idx = &output_buffer_idx;
params.output_offset = &output_offset; params.output_offset = &output_offset;
params.control_token = ctx->shutdown_requested ? control_token_shutdown : control_token_none; params.control_token = ctx->control_token;
ctx->shutdown_requested = FALSE; params.alert_type = ctx->alert_type;
params.alert_number = ctx->alert_number;
ctx->control_token = CONTROL_TOKEN_NONE;
ret = GNUTLS_CALL( handshake, &params ); ret = GNUTLS_CALL( handshake, &params );
if (output_buffer_idx != -1) if (output_buffer_idx != -1)
...@@ -1588,23 +1592,43 @@ static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context ...@@ -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) static SECURITY_STATUS SEC_ENTRY schan_ApplyControlToken(PCtxtHandle context_handle, PSecBufferDesc input)
{ {
struct schan_context *ctx; struct schan_context *ctx;
DWORD type;
TRACE("%p %p\n", context_handle, input); TRACE("%p %p\n", context_handle, input);
dump_buffer_desc(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) return SEC_E_INTERNAL_ERROR;
if (input->cBuffers != 1) return SEC_E_INVALID_TOKEN; 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].BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN;
if (input->pBuffers[0].cbBuffer < sizeof(DWORD)) return SEC_E_UNSUPPORTED_FUNCTION; if (input->pBuffers[0].cbBuffer < sizeof(type)) return SEC_E_UNSUPPORTED_FUNCTION;
if (*(DWORD *)input->pBuffers[0].pvBuffer != SCHANNEL_SHUTDOWN) return SEC_E_UNSUPPORTED_FUNCTION; type = *(DWORD *)input->pBuffers[0].pvBuffer;
ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX); switch (type)
ctx->shutdown_requested = TRUE; {
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;
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; return SEC_E_OK;
}
default:
FIXME("token type %lu not supported\n", type);
return SEC_E_UNSUPPORTED_FUNCTION;
}
} }
static const SecurityFunctionTableA schanTableA = { static const SecurityFunctionTableA schanTableA = {
......
...@@ -84,6 +84,7 @@ static void *libgnutls_handle; ...@@ -84,6 +84,7 @@ static void *libgnutls_handle;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f #define MAKE_FUNCPTR(f) static typeof(f) * p##f
MAKE_FUNCPTR(gnutls_alert_get); MAKE_FUNCPTR(gnutls_alert_get);
MAKE_FUNCPTR(gnutls_alert_get_name); MAKE_FUNCPTR(gnutls_alert_get_name);
MAKE_FUNCPTR(gnutls_alert_send);
MAKE_FUNCPTR(gnutls_certificate_allocate_credentials); MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
MAKE_FUNCPTR(gnutls_certificate_free_credentials); MAKE_FUNCPTR(gnutls_certificate_free_credentials);
MAKE_FUNCPTR(gnutls_certificate_get_peers); MAKE_FUNCPTR(gnutls_certificate_get_peers);
...@@ -121,7 +122,6 @@ MAKE_FUNCPTR(gnutls_x509_crt_deinit); ...@@ -121,7 +122,6 @@ MAKE_FUNCPTR(gnutls_x509_crt_deinit);
MAKE_FUNCPTR(gnutls_x509_crt_import); MAKE_FUNCPTR(gnutls_x509_crt_import);
MAKE_FUNCPTR(gnutls_x509_crt_init); MAKE_FUNCPTR(gnutls_x509_crt_init);
MAKE_FUNCPTR(gnutls_x509_privkey_deinit); MAKE_FUNCPTR(gnutls_x509_privkey_deinit);
MAKE_FUNCPTR(gnutls_alert_send);
#undef MAKE_FUNCPTR #undef MAKE_FUNCPTR
#if GNUTLS_VERSION_MAJOR < 3 #if GNUTLS_VERSION_MAJOR < 3
...@@ -566,6 +566,74 @@ static NTSTATUS schan_set_session_target( void *args ) ...@@ -566,6 +566,74 @@ static NTSTATUS schan_set_session_target( void *args )
return STATUS_SUCCESS; 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 ) static NTSTATUS schan_handshake( void *args )
{ {
const struct handshake_params *params = args; const struct handshake_params *params = args;
...@@ -578,22 +646,9 @@ static NTSTATUS schan_handshake( void *args ) ...@@ -578,22 +646,9 @@ static NTSTATUS schan_handshake( void *args )
t->in.limit = params->input_size; t->in.limit = params->input_size;
init_schan_buffers(&t->out, params->output); 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 = send_alert(s, params->alert_type, params->alert_number);
status = SEC_E_INTERNAL_ERROR;
}
goto done; goto done;
} }
...@@ -1431,6 +1486,7 @@ static NTSTATUS process_attach( void *args ) ...@@ -1431,6 +1486,7 @@ static NTSTATUS process_attach( void *args )
LOAD_FUNCPTR(gnutls_alert_get) LOAD_FUNCPTR(gnutls_alert_get)
LOAD_FUNCPTR(gnutls_alert_get_name) LOAD_FUNCPTR(gnutls_alert_get_name)
LOAD_FUNCPTR(gnutls_alert_send)
LOAD_FUNCPTR(gnutls_certificate_allocate_credentials) LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
LOAD_FUNCPTR(gnutls_certificate_free_credentials) LOAD_FUNCPTR(gnutls_certificate_free_credentials)
LOAD_FUNCPTR(gnutls_certificate_get_peers) LOAD_FUNCPTR(gnutls_certificate_get_peers)
...@@ -1468,7 +1524,6 @@ static NTSTATUS process_attach( void *args ) ...@@ -1468,7 +1524,6 @@ static NTSTATUS process_attach( void *args )
LOAD_FUNCPTR(gnutls_x509_crt_import) LOAD_FUNCPTR(gnutls_x509_crt_import)
LOAD_FUNCPTR(gnutls_x509_crt_init) LOAD_FUNCPTR(gnutls_x509_crt_init)
LOAD_FUNCPTR(gnutls_x509_privkey_deinit) LOAD_FUNCPTR(gnutls_x509_privkey_deinit)
LOAD_FUNCPTR(gnutls_alert_send)
#undef LOAD_FUNCPTR #undef LOAD_FUNCPTR
if (!(pgnutls_cipher_get_block_size = dlsym(libgnutls_handle, "gnutls_cipher_get_block_size"))) 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 ) ...@@ -1751,6 +1806,8 @@ static NTSTATUS wow64_schan_handshake( void *args )
PTR32 output_buffer_idx; PTR32 output_buffer_idx;
PTR32 output_offset; PTR32 output_offset;
enum control_token control_token; enum control_token control_token;
unsigned int alert_type;
unsigned int alert_number;
} const *params32 = args; } const *params32 = args;
struct handshake_params params = struct handshake_params params =
{ {
...@@ -1762,6 +1819,8 @@ static NTSTATUS wow64_schan_handshake( void *args ) ...@@ -1762,6 +1819,8 @@ static NTSTATUS wow64_schan_handshake( void *args )
ULongToPtr(params32->output_buffer_idx), ULongToPtr(params32->output_buffer_idx),
ULongToPtr(params32->output_offset), ULongToPtr(params32->output_offset),
params32->control_token, params32->control_token,
params32->alert_type,
params32->alert_number,
}; };
if (params32->input) if (params32->input)
{ {
......
...@@ -147,8 +147,9 @@ struct get_unique_channel_binding_params ...@@ -147,8 +147,9 @@ struct get_unique_channel_binding_params
enum control_token enum control_token
{ {
control_token_none, CONTROL_TOKEN_NONE,
control_token_shutdown, CONTROL_TOKEN_SHUTDOWN,
CONTROL_TOKEN_ALERT,
}; };
struct handshake_params struct handshake_params
...@@ -161,6 +162,8 @@ struct handshake_params ...@@ -161,6 +162,8 @@ struct handshake_params
int *output_buffer_idx; int *output_buffer_idx;
ULONG *output_offset; ULONG *output_offset;
enum control_token control_token; enum control_token control_token;
unsigned int alert_type;
unsigned int alert_number;
}; };
struct recv_params struct recv_params
......
...@@ -1963,6 +1963,7 @@ static void test_connection_shutdown(void) ...@@ -1963,6 +1963,7 @@ static void test_connection_shutdown(void)
CredHandle cred_handle; CredHandle cred_handle;
SCHANNEL_CRED cred; SCHANNEL_CRED cred;
SecBuffer *buf; SecBuffer *buf;
SCHANNEL_ALERT_TOKEN alert;
ULONG attrs; ULONG attrs;
void *tmp; void *tmp;
...@@ -2075,6 +2076,18 @@ static void test_connection_shutdown(void) ...@@ -2075,6 +2076,18 @@ static void test_connection_shutdown(void)
ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer ); ok( buf->cbBuffer == sizeof(message), "got cbBuffer %#lx.\n", buf->cbBuffer );
ok( !memcmp( buf->pvBuffer, message, sizeof(message) ), "message data mismatch.\n" ); 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[0] );
free_buffers( &buffers[1] ); free_buffers( &buffers[1] );
DeleteSecurityContext( &context ); DeleteSecurityContext( &context );
......
...@@ -88,6 +88,43 @@ static const WCHAR SCHANNEL_NAME_W[] = { 'S','c','h','a','n','n','e','l',0 }; ...@@ -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_ALERT 2
#define SCHANNEL_SESSION 3 #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_ALL 0xffffffff
#define SP_PROT_UNI_CLIENT 0x80000000 #define SP_PROT_UNI_CLIENT 0x80000000
#define SP_PROT_UNI_SERVER 0x40000000 #define SP_PROT_UNI_SERVER 0x40000000
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment