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