Skip to content

Commit

Permalink
Add API method to translate errors to alerts (#3171)
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart authored Jan 22, 2022
1 parent 11eaad9 commit 662ae1d
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 30 deletions.
10 changes: 10 additions & 0 deletions tests/s2n_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "error/s2n_errno.h"
#include "utils/s2n_safety.h"
#include "utils/s2n_result.h"
#include "tls/s2n_alerts.h"
#include "tls/s2n_tls13.h"

int test_count;
Expand Down Expand Up @@ -138,6 +139,15 @@ int test_count;
RESET_ERRNO(); \
} while(0)

#define EXPECT_FAILURE_WITH_ALERT( function_call, err, alert ) \
do { \
EXPECT_FAILURE_WITH_ERRNO_NO_RESET(function_call, err); \
uint8_t _alert_for_failure = 0; \
EXPECT_SUCCESS(s2n_error_get_alert(s2n_errno, &_alert_for_failure)); \
EXPECT_EQUAL(_alert_for_failure, (alert)); \
RESET_ERRNO(); \
} while(0)

/* for use with S2N_RESULT */
#define EXPECT_ERROR_WITH_ERRNO_NO_RESET( function_call, err ) \
do { \
Expand Down
61 changes: 61 additions & 0 deletions tests/unit/s2n_alerts_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,67 @@ int main(int argc, char **argv)
{
BEGIN_TEST();

/* Test s2n_error_get_alert */
{
uint8_t alert = 0;

/* Test S2N_ERR_T_OK */
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_OK, &alert), S2N_ERR_NO_ALERT);

/* Test S2N_ERR_T_CLOSED */
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_CLOSED, &alert), S2N_ERR_NO_ALERT);

/* Test S2N_ERR_T_ALERT */
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_ALERT, &alert), S2N_ERR_NO_ALERT);

/* Test S2N_ERR_T_BLOCKED */
for (size_t i = S2N_ERR_T_BLOCKED_START; i < S2N_ERR_T_BLOCKED_END; i++) {
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(i, &alert), S2N_ERR_NO_ALERT);
}

/* Test S2N_ERR_T_USAGE */
for (size_t i = S2N_ERR_T_USAGE_START; i < S2N_ERR_T_USAGE_END; i++) {
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(i, &alert), S2N_ERR_NO_ALERT);
}

/* Test S2N_ERR_T_PROTO */
{
/* Test all protocol errors are handled */
int ret_val;
for (size_t i = S2N_ERR_T_PROTO_START; i < S2N_ERR_T_PROTO_END; i++) {
ret_val = s2n_error_get_alert(i, &alert);
if (ret_val != S2N_SUCCESS && s2n_errno == S2N_ERR_UNIMPLEMENTED) {
fprintf(stdout, "\n\nNo alert mapping for protocol error %s\n\n", s2n_strerror_name(i));
FAIL_MSG("Missing alert mapping for protocol error.");
}
}

/* Test some known mappings */
{
EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_MISSING_EXTENSION, &alert));
EXPECT_EQUAL(S2N_TLS_ALERT_MISSING_EXTENSION, alert);

EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_BAD_MESSAGE, &alert));
EXPECT_EQUAL(S2N_TLS_ALERT_UNEXPECTED_MESSAGE, alert);
}

/* Test unknown mapping */
EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, &alert), S2N_ERR_NO_ALERT);
}

/* Test S2N_ERR_T_IO */
{
EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_IO, &alert));
EXPECT_EQUAL(alert, S2N_TLS_ALERT_INTERNAL_ERROR);
}

/* Test S2N_ERR_T_INTERNAL */
for (size_t i = S2N_ERR_T_INTERNAL_START; i < S2N_ERR_T_INTERNAL_END; i++) {
EXPECT_SUCCESS(s2n_error_get_alert(i, &alert));
EXPECT_EQUAL(alert, S2N_TLS_ALERT_INTERNAL_ERROR);
}
}

/* Test S2N_TLS_ALERT_CLOSE_NOTIFY and close_notify_received */
{
const uint8_t close_notify_alert[] = { 2 /* AlertLevel = fatal */,
Expand Down
11 changes: 9 additions & 2 deletions tests/unit/s2n_key_update_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ int main(int argc, char **argv)
}
/* s2n_key_update_recv */
{
/* Key update message not allowed when running with QUIC */
/* Key update message not allowed when running with QUIC
*
*= https://tools.ietf.org/rfc/rfc9001.txt#6
*= type=test
*# Endpoints MUST treat the receipt of a TLS KeyUpdate message as a connection error
*# of type 0x010a, equivalent to a fatal TLS alert of unexpected_message
**/
{
const size_t test_data_len = 10;
DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free);
Expand All @@ -88,7 +94,8 @@ int main(int argc, char **argv)
EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER));
EXPECT_SUCCESS(s2n_connection_set_config(conn, quic_config));

EXPECT_FAILURE_WITH_ERRNO(s2n_key_update_recv(conn, &input), S2N_ERR_BAD_MESSAGE);
EXPECT_FAILURE_WITH_ALERT(s2n_key_update_recv(conn, &input),
S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE);

/* Verify method was a no-op and the message was not read */
EXPECT_EQUAL(s2n_stuffer_data_available(&input), test_data_len);
Expand Down
13 changes: 11 additions & 2 deletions tests/unit/s2n_quic_transport_params_extension_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,18 @@ int main(int argc, char **argv)
/* Succeeds if quic not enabled (default) */
EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.if_missing(conn));

/* Fails if quic enabled */
/* Fails if quic enabled
*
*= https://tools.ietf.org/rfc/rfc9001.txt#8.2
*= type=test
*# Endpoints MUST send the quic_transport_parameters extension; endpoints that receive
*# ClientHello or EncryptedExtensions messages without the quic_transport_parameters
*# extension MUST close the connection with an error of type 0x016d (equivalent to a fatal
*# TLS missing_extension alert
**/
EXPECT_SUCCESS(s2n_config_enable_quic(config));
EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.if_missing(conn), S2N_ERR_MISSING_EXTENSION);
EXPECT_FAILURE_WITH_ALERT(s2n_quic_transport_parameters_extension.if_missing(conn),
S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION);

EXPECT_SUCCESS(s2n_connection_free(conn));
EXPECT_SUCCESS(s2n_config_free(config));
Expand Down
139 changes: 113 additions & 26 deletions tls/s2n_alerts.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,96 @@
#include "utils/s2n_safety.h"
#include "utils/s2n_blob.h"

#define S2N_TLS_ALERT_CLOSE_NOTIFY 0
#define S2N_TLS_ALERT_UNEXPECTED_MSG 10
#define S2N_TLS_ALERT_BAD_RECORD_MAC 20
#define S2N_TLS_ALERT_DECRYPT_FAILED 21
#define S2N_TLS_ALERT_RECORD_OVERFLOW 22
#define S2N_TLS_ALERT_DECOMP_FAILED 30
#define S2N_TLS_ALERT_HANDSHAKE_FAILURE 40
#define S2N_TLS_ALERT_NO_CERTIFICATE 41
#define S2N_TLS_ALERT_BAD_CERTIFICATE 42
#define S2N_TLS_ALERT_UNSUPPORTED_CERT 43
#define S2N_TLS_ALERT_CERT_REVOKED 44
#define S2N_TLS_ALERT_CERT_EXPIRED 45
#define S2N_TLS_ALERT_CERT_UNKNOWN 46
#define S2N_TLS_ALERT_ILLEGAL_PARAMETER 47
#define S2N_TLS_ALERT_UNKNOWN_CA 48
#define S2N_TLS_ALERT_ACCESS_DENIED 49
#define S2N_TLS_ALERT_DECODE_ERROR 50
#define S2N_TLS_ALERT_DECRYPT_ERROR 51
#define S2N_TLS_ALERT_EXPORT_RESTRICTED 60
#define S2N_TLS_ALERT_PROTOCOL_VERSION 70
#define S2N_TLS_ALERT_INSUFFICIENT_SECURITY 71
#define S2N_TLS_ALERT_INTERNAL_ERROR 80
#define S2N_TLS_ALERT_USER_CANCELED 90
#define S2N_TLS_ALERT_NO_RENEGOTIATION 100
#define S2N_TLS_ALERT_UNSUPPORTED_EXTENSION 110

#define S2N_TLS_ALERT_LEVEL_WARNING 1
#define S2N_TLS_ALERT_LEVEL_FATAL 2

#define S2N_ALERT_CASE(error, alert_code) \
case (error): \
*alert = (alert_code); \
return S2N_RESULT_OK

#define S2N_NO_ALERT(error) \
case (error): \
RESULT_BAIL(S2N_ERR_NO_ALERT)

static S2N_RESULT s2n_translate_protocol_error_to_alert(int error_code, uint8_t *alert)
{
RESULT_ENSURE_REF(alert);

switch(error_code) {
S2N_ALERT_CASE(S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION);

/* TODO: The ERR_BAD_MESSAGE -> ALERT_UNEXPECTED_MESSAGE mapping
* isn't always correct. Sometimes s2n-tls uses ERR_BAD_MESSAGE
* to indicate S2N_TLS_ALERT_ILLEGAL_PARAMETER instead.
* We'll want to add a new error to distinguish between the two usages:
* our errors should be equally or more specific than alerts, not less.
*/
S2N_ALERT_CASE(S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE);

/* TODO: Add mappings for other protocol errors.
*/
S2N_NO_ALERT(S2N_ERR_ENCRYPT);
S2N_NO_ALERT(S2N_ERR_DECRYPT);
S2N_NO_ALERT(S2N_ERR_KEY_INIT);
S2N_NO_ALERT(S2N_ERR_KEY_DESTROY);
S2N_NO_ALERT(S2N_ERR_DH_SERIALIZING);
S2N_NO_ALERT(S2N_ERR_DH_SHARED_SECRET);
S2N_NO_ALERT(S2N_ERR_DH_WRITING_PUBLIC_KEY);
S2N_NO_ALERT(S2N_ERR_DH_FAILED_SIGNING);
S2N_NO_ALERT(S2N_ERR_DH_COPYING_PARAMETERS);
S2N_NO_ALERT(S2N_ERR_DH_GENERATING_PARAMETERS);
S2N_NO_ALERT(S2N_ERR_CIPHER_NOT_SUPPORTED);
S2N_NO_ALERT(S2N_ERR_NO_APPLICATION_PROTOCOL);
S2N_NO_ALERT(S2N_ERR_FALLBACK_DETECTED);
S2N_NO_ALERT(S2N_ERR_HASH_DIGEST_FAILED);
S2N_NO_ALERT(S2N_ERR_HASH_INIT_FAILED);
S2N_NO_ALERT(S2N_ERR_HASH_UPDATE_FAILED);
S2N_NO_ALERT(S2N_ERR_HASH_COPY_FAILED);
S2N_NO_ALERT(S2N_ERR_HASH_WIPE_FAILED);
S2N_NO_ALERT(S2N_ERR_HASH_NOT_READY);
S2N_NO_ALERT(S2N_ERR_ALLOW_MD5_FOR_FIPS_FAILED);
S2N_NO_ALERT(S2N_ERR_DECODE_CERTIFICATE);
S2N_NO_ALERT(S2N_ERR_DECODE_PRIVATE_KEY);
S2N_NO_ALERT(S2N_ERR_INVALID_HELLO_RETRY);
S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_ALGORITHM);
S2N_NO_ALERT(S2N_ERR_INVALID_SIGNATURE_SCHEME);
S2N_NO_ALERT(S2N_ERR_CBC_VERIFY);
S2N_NO_ALERT(S2N_ERR_DH_COPYING_PUBLIC_KEY);
S2N_NO_ALERT(S2N_ERR_SIGN);
S2N_NO_ALERT(S2N_ERR_VERIFY_SIGNATURE);
S2N_NO_ALERT(S2N_ERR_ECDHE_GEN_KEY);
S2N_NO_ALERT(S2N_ERR_ECDHE_SHARED_SECRET);
S2N_NO_ALERT(S2N_ERR_ECDHE_UNSUPPORTED_CURVE);
S2N_NO_ALERT(S2N_ERR_ECDSA_UNSUPPORTED_CURVE);
S2N_NO_ALERT(S2N_ERR_ECDHE_SERIALIZING);
S2N_NO_ALERT(S2N_ERR_KEM_UNSUPPORTED_PARAMS);
S2N_NO_ALERT(S2N_ERR_SHUTDOWN_RECORD_TYPE);
S2N_NO_ALERT(S2N_ERR_SHUTDOWN_CLOSED);
S2N_NO_ALERT(S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO);
S2N_NO_ALERT(S2N_ERR_RECORD_LIMIT);
S2N_NO_ALERT(S2N_ERR_CERT_UNTRUSTED);
S2N_NO_ALERT(S2N_ERR_CERT_TYPE_UNSUPPORTED);
S2N_NO_ALERT(S2N_ERR_INVALID_MAX_FRAG_LEN);
S2N_NO_ALERT(S2N_ERR_MAX_FRAG_LEN_MISMATCH);
S2N_NO_ALERT(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
S2N_NO_ALERT(S2N_ERR_BAD_KEY_SHARE);
S2N_NO_ALERT(S2N_ERR_CANCELLED);
S2N_NO_ALERT(S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED);
S2N_NO_ALERT(S2N_ERR_MAX_INNER_PLAINTEXT_SIZE);
S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_SIZE);
S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE);
S2N_NO_ALERT(S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL);
S2N_NO_ALERT(S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING);
S2N_NO_ALERT(S2N_ERR_UNSUPPORTED_EXTENSION);
S2N_NO_ALERT(S2N_ERR_DUPLICATE_EXTENSION);
S2N_NO_ALERT(S2N_ERR_MAX_EARLY_DATA_SIZE);
S2N_NO_ALERT(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT);
}

RESULT_BAIL(S2N_ERR_UNIMPLEMENTED);
}

static bool s2n_alerts_supported(struct s2n_connection *conn)
{
/* If running in QUIC mode, QUIC handles alerting.
Expand All @@ -78,6 +139,32 @@ static bool s2n_handle_as_warning(struct s2n_connection *conn, uint8_t level, ui
return type == S2N_TLS_ALERT_USER_CANCELED;
}

int s2n_error_get_alert(int error, uint8_t *alert)
{
int error_type = s2n_error_get_type(error);

POSIX_ENSURE_REF(alert);

switch(error_type) {
case S2N_ERR_T_OK:
case S2N_ERR_T_CLOSED:
case S2N_ERR_T_BLOCKED:
case S2N_ERR_T_USAGE:
case S2N_ERR_T_ALERT:
POSIX_BAIL(S2N_ERR_NO_ALERT);
break;
case S2N_ERR_T_PROTO:
POSIX_GUARD_RESULT(s2n_translate_protocol_error_to_alert(error, alert));
break;
case S2N_ERR_T_IO:
case S2N_ERR_T_INTERNAL:
*alert = S2N_TLS_ALERT_INTERNAL_ERROR;
break;
}

return S2N_SUCCESS;
}

int s2n_process_alert_fragment(struct s2n_connection *conn)
{
POSIX_ENSURE_REF(conn);
Expand Down
78 changes: 78 additions & 0 deletions tls/s2n_alerts.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,84 @@

#include "tls/s2n_connection.h"

typedef enum {
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# enum {
*# close_notify(0),
*# unexpected_message(10),
*# bad_record_mac(20),
*# record_overflow(22),
*# handshake_failure(40),
*/
S2N_TLS_ALERT_CLOSE_NOTIFY = 0,
S2N_TLS_ALERT_UNEXPECTED_MESSAGE = 10,
S2N_TLS_ALERT_BAD_RECORD_MAC = 20,
S2N_TLS_ALERT_RECORD_OVERFLOW = 22,
S2N_TLS_ALERT_HANDSHAKE_FAILURE = 40,
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# bad_certificate(42),
*# unsupported_certificate(43),
*# certificate_revoked(44),
*# certificate_expired(45),
*# certificate_unknown(46),
*/
S2N_TLS_ALERT_BAD_CERTIFICATE = 42,
S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE = 43,
S2N_TLS_ALERT_CERTIFICATE_REVOKED = 44,
S2N_TLS_ALERT_CERTIFICATE_EXPIRED = 45,
S2N_TLS_ALERT_CERTIFICATE_UNKNOWN = 46,
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# illegal_parameter(47),
*# unknown_ca(48),
*# access_denied(49),
*# decode_error(50),
*# decrypt_error(51),
*/
S2N_TLS_ALERT_ILLEGAL_PARAMETER = 47,
S2N_TLS_ALERT_UNKNOWN_CA = 48,
S2N_TLS_ALERT_ACCESS_DENIED = 49,
S2N_TLS_ALERT_DECODE_ERROR = 50,
S2N_TLS_ALERT_DECRYPT_ERROR = 51,
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# protocol_version(70),
*# insufficient_security(71),
*# internal_error(80),
*# inappropriate_fallback(86),
*# user_canceled(90),
*/
S2N_TLS_ALERT_PROTOCOL_VERSION = 70,
S2N_TLS_ALERT_INSUFFICIENT_SECURITY = 71,
S2N_TLS_ALERT_INTERNAL_ERROR = 80,
S2N_TLS_ALERT_INAPPROPRIATE_FALLBACK = 86,
S2N_TLS_ALERT_USER_CANCELED = 90,
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# missing_extension(109),
*# unsupported_extension(110),
*# unrecognized_name(112),
*# bad_certificate_status_response(113),
*# unknown_psk_identity(115),
*/
S2N_TLS_ALERT_MISSING_EXTENSION = 109,
S2N_TLS_ALERT_UNSUPPORTED_EXTENSION = 110,
S2N_TLS_ALERT_UNRECOGNIZED_NAME = 112,
S2N_TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE = 113,
S2N_TLS_ALERT_UNKNOWN_PSK_IDENTITY = 115,
/*
*= https://tools.ietf.org/rfc/rfc8446#section-6
*# certificate_required(116),
*# no_application_protocol(120),
*# (255)
*# } AlertDescription;
*/
S2N_TLS_ALERT_CERTIFICATE_REQUIRED = 116,
S2N_TLS_ALERT_NO_APPLICATION_PROTOCOL = 120,
} s2n_tls_alert_code;

extern int s2n_process_alert_fragment(struct s2n_connection *conn);
extern int s2n_queue_writer_close_alert_warning(struct s2n_connection *conn);
extern int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn);
Expand Down
13 changes: 13 additions & 0 deletions tls/s2n_quic_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,16 @@ typedef int (*s2n_secret_cb) (void* context, struct s2n_connection *conn,
* used outside of a QUIC implementation.
*/
S2N_API int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx);

/*
* Return the TLS alert that S2N-TLS would send, if S2N-TLS sent specific alerts.
*
* S2N-TLS only sends generic close_notify alerts for security reasons, and TLS never
* sends alerts when used by QUIC. This method returns the alert that would have been
* sent if S2N-TLS sent specific alerts as defined in the protocol specifications.
*
* WARNING: this method is still considered experimental and will not always report
* the correct alert description. It may be used for testing and logging, but
* not relied on for production logic.
*/
S2N_API int s2n_error_get_alert(int error, uint8_t *alert);

0 comments on commit 662ae1d

Please sign in to comment.