tls/s2n_alerts.c (235 lines of code) (raw):
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include "tls/s2n_alerts.h"
#include <stdint.h>
#include <sys/param.h>
#include "error/s2n_errno.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_record.h"
#include "tls/s2n_resume.h"
#include "tls/s2n_tls_parameters.h"
#include "utils/s2n_atomic.h"
#include "utils/s2n_blob.h"
#include "utils/s2n_safety.h"
#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);
S2N_ALERT_CASE(S2N_ERR_NO_VALID_SIGNATURE_SCHEME, S2N_TLS_ALERT_HANDSHAKE_FAILURE);
S2N_ALERT_CASE(S2N_ERR_MISSING_CLIENT_CERT, S2N_TLS_ALERT_CERTIFICATE_REQUIRED);
/* 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);
S2N_ALERT_CASE(S2N_ERR_UNEXPECTED_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE);
S2N_ALERT_CASE(S2N_ERR_MISSING_CERT_REQUEST, S2N_TLS_ALERT_UNEXPECTED_MESSAGE);
/* For errors involving secure renegotiation:
*= https://www.rfc-editor.org/rfc/rfc5746#3.4
*# Note: later in Section 3, "abort the handshake" is used as
*# shorthand for "send a fatal handshake_failure alert and
*# terminate the connection".
*/
S2N_ALERT_CASE(S2N_ERR_NO_RENEGOTIATION, S2N_TLS_ALERT_HANDSHAKE_FAILURE);
S2N_ALERT_CASE(S2N_ERR_KTLS_KEYUPDATE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE);
/* For errors involving certificates */
/* This error is used in several ways so make it a general certificate issue
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# certificate_unknown: Some other (unspecified) issue arose in
*# processing the certificate, rendering it unacceptable.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_UNTRUSTED, S2N_TLS_ALERT_CERTIFICATE_UNKNOWN);
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# certificate_revoked: A certificate was revoked by its signer.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_REVOKED, S2N_TLS_ALERT_CERTIFICATE_REVOKED);
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# certificate_expired: A certificate has expired or is not currently
*# valid.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_NOT_YET_VALID, S2N_TLS_ALERT_CERTIFICATE_EXPIRED);
S2N_ALERT_CASE(S2N_ERR_CERT_EXPIRED, S2N_TLS_ALERT_CERTIFICATE_EXPIRED);
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# unsupported_certificate: A certificate was of an unsupported type.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_TYPE_UNSUPPORTED, S2N_TLS_ALERT_UNSUPPORTED_CERTIFICATE);
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# access_denied: A valid certificate or PSK was received, but when
*# access control was applied, the sender decided not to proceed with
*# negotiation.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_REJECTED, S2N_TLS_ALERT_ACCESS_DENIED);
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*# bad_certificate: A certificate was corrupt, contained signatures
*# that did not verify correctly, etc.
*/
S2N_ALERT_CASE(S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED, S2N_TLS_ALERT_BAD_CERTIFICATE);
S2N_ALERT_CASE(S2N_ERR_CERT_INVALID, S2N_TLS_ALERT_BAD_CERTIFICATE);
S2N_ALERT_CASE(S2N_ERR_DECODE_CERTIFICATE, S2N_TLS_ALERT_BAD_CERTIFICATE);
/* 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_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_ECDHE_INVALID_PUBLIC_KEY);
S2N_NO_ALERT(S2N_ERR_ECDHE_INVALID_PUBLIC_KEY_FIPS);
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_CRL_LOOKUP_FAILED);
S2N_NO_ALERT(S2N_ERR_CRL_SIGNATURE);
S2N_NO_ALERT(S2N_ERR_CRL_ISSUER);
S2N_NO_ALERT(S2N_ERR_CRL_UNHANDLED_CRITICAL_EXTENSION);
S2N_NO_ALERT(S2N_ERR_CRL_INVALID_THIS_UPDATE);
S2N_NO_ALERT(S2N_ERR_CRL_INVALID_NEXT_UPDATE);
S2N_NO_ALERT(S2N_ERR_CRL_NOT_YET_VALID);
S2N_NO_ALERT(S2N_ERR_CRL_EXPIRED);
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.
* S2N should not send or receive alerts. */
return !s2n_connection_is_quic_enabled(conn);
}
/* In TLS1.3 all Alerts
*= https://www.rfc-editor.org/rfc/rfc8446#section-6
*# MUST be treated as error alerts when received
*# regardless of the AlertLevel in the message.
*/
static bool s2n_process_as_warning(struct s2n_connection *conn, uint8_t level, uint8_t type)
{
/* Only TLS1.2 considers the alert level. The alert level field is
* considered deprecated in TLS1.3. */
if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) {
return level == S2N_TLS_ALERT_LEVEL_WARNING
&& conn->config->alert_behavior == S2N_ALERT_IGNORE_WARNINGS;
}
/* user_canceled is the only alert currently treated as a warning in TLS1.3.
* We need to treat it as a warning regardless of alert_behavior to avoid marking
* correctly-closed connections as failed. */
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);
S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE);
S2N_ERROR_IF(s2n_stuffer_data_available(&conn->alert_in) == 2, S2N_ERR_ALERT_PRESENT);
POSIX_ENSURE(s2n_alerts_supported(conn), S2N_ERR_BAD_MESSAGE);
while (s2n_stuffer_data_available(&conn->in)) {
uint8_t bytes_required = 2;
/* Alerts are two bytes long, but can still be fragmented or coalesced */
if (s2n_stuffer_data_available(&conn->alert_in) == 1) {
bytes_required = 1;
}
int bytes_to_read = MIN(bytes_required, s2n_stuffer_data_available(&conn->in));
POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->alert_in, bytes_to_read));
if (s2n_stuffer_data_available(&conn->alert_in) == 2) {
/* Close notifications are handled as shutdowns */
if (conn->alert_in_data[1] == S2N_TLS_ALERT_CLOSE_NOTIFY) {
s2n_atomic_flag_set(&conn->read_closed);
s2n_atomic_flag_set(&conn->close_notify_received);
return 0;
}
/* Ignore warning-level alerts if we're in warning-tolerant mode */
if (s2n_process_as_warning(conn, conn->alert_in_data[0], conn->alert_in_data[1])) {
POSIX_GUARD(s2n_stuffer_wipe(&conn->alert_in));
return 0;
}
/* RFC 5077 5.1 - Expire any cached session on an error alert */
if (s2n_allowed_to_cache_connection(conn) && conn->session_id_len) {
conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len);
}
/* All other alerts are treated as fatal errors.
*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6
*# Unknown Alert types MUST be treated as error alerts.
*/
POSIX_GUARD_RESULT(s2n_connection_set_closed(conn));
s2n_atomic_flag_set(&conn->error_alert_received);
POSIX_BAIL(S2N_ERR_ALERT);
}
}
return 0;
}
static S2N_RESULT s2n_queue_reader_alert(struct s2n_connection *conn, s2n_tls_alert_code code)
{
RESULT_ENSURE_REF(conn);
if (!conn->reader_alert_out) {
conn->reader_alert_out = code;
}
return S2N_RESULT_OK;
}
int s2n_queue_reader_unsupported_protocol_version_alert(struct s2n_connection *conn)
{
POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_PROTOCOL_VERSION));
return S2N_SUCCESS;
}
int s2n_queue_reader_handshake_failure_alert(struct s2n_connection *conn)
{
POSIX_GUARD_RESULT(s2n_queue_reader_alert(conn, S2N_TLS_ALERT_HANDSHAKE_FAILURE));
return S2N_SUCCESS;
}
S2N_RESULT s2n_queue_reader_no_renegotiation_alert(struct s2n_connection *conn)
{
/**
*= https://www.rfc-editor.org/rfc/rfc5746#4.5
*# SSLv3 does not define the "no_renegotiation" alert (and does
*# not offer a way to indicate a refusal to renegotiate at a "warning"
*# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal
*# handshake_failure alert.
**/
if (s2n_connection_get_protocol_version(conn) == S2N_SSLv3) {
RESULT_GUARD_POSIX(s2n_queue_reader_handshake_failure_alert(conn));
RESULT_BAIL(S2N_ERR_BAD_MESSAGE);
}
if (!conn->reader_warning_out) {
conn->reader_warning_out = S2N_TLS_ALERT_NO_RENEGOTIATION;
}
return S2N_RESULT_OK;
}
S2N_RESULT s2n_alerts_write_error_or_close_notify(struct s2n_connection *conn)
{
if (!s2n_alerts_supported(conn)) {
return S2N_RESULT_OK;
}
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-6.2
*= type=exception
*= reason=Specific alerts could expose a side-channel attack vector.
*# The phrases "terminate the connection with an X
*# alert" and "abort the handshake with an X alert" mean that the
*# implementation MUST send alert X if it sends any alert.
*
* By default, s2n-tls sends a generic close_notify alert, even in
* response to fatal errors. This is done to avoid potential
* side-channel attacks since specific alerts could reveal information
* about why the error occured.
*/
uint8_t code = S2N_TLS_ALERT_CLOSE_NOTIFY;
uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING;
/* s2n-tls sends a very small subset of more specific error alerts.
* Since either the reader or the writer can produce one of these alerts,
* but only a single alert can be reported, we prioritize writer alerts.
*/
if (conn->writer_alert_out) {
code = conn->writer_alert_out;
level = S2N_TLS_ALERT_LEVEL_FATAL;
} else if (conn->reader_alert_out) {
code = conn->reader_alert_out;
level = S2N_TLS_ALERT_LEVEL_FATAL;
}
struct s2n_blob alert = { 0 };
uint8_t alert_bytes[] = { level, code };
RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes)));
RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert));
conn->alert_sent = true;
return S2N_RESULT_OK;
}
S2N_RESULT s2n_alerts_write_warning(struct s2n_connection *conn)
{
if (!s2n_alerts_supported(conn)) {
return S2N_RESULT_OK;
}
uint8_t code = conn->reader_warning_out;
uint8_t level = S2N_TLS_ALERT_LEVEL_WARNING;
struct s2n_blob alert = { 0 };
uint8_t alert_bytes[] = { level, code };
RESULT_GUARD_POSIX(s2n_blob_init(&alert, alert_bytes, sizeof(alert_bytes)));
RESULT_GUARD(s2n_record_write(conn, TLS_ALERT, &alert));
return S2N_RESULT_OK;
}