Push alert handling down into the record functions.
Alert handling is more-or-less identical across all contexts. Push it down from
read_bytes into the low-level record functions. This also deduplicates the code
shared between TLS and DTLS.
Now the only type mismatch managed by read_bytes is if we get handshake data in
read_app_data.
Change-Id: Ia8331897b304566e66d901899cfbf31d2870194e
Reviewed-on: https://boringssl-review.googlesource.com/8124
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/tls_record.c b/ssl/tls_record.c
index 7036c87..869831c 100644
--- a/ssl/tls_record.c
+++ b/ssl/tls_record.c
@@ -113,6 +113,7 @@
#include <openssl/bytestring.h>
#include <openssl/err.h>
+#include <openssl/mem.h>
#include "internal.h"
@@ -123,6 +124,10 @@
* forever. */
static const uint8_t kMaxEmptyRecords = 32;
+/* kMaxWarningAlerts is the number of consecutive warning alerts that will be
+ * processed. */
+static const uint8_t kMaxWarningAlerts = 4;
+
/* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
* state needs record-splitting and zero otherwise. */
static int ssl_needs_record_splitting(const SSL *ssl) {
@@ -191,6 +196,8 @@
SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len,
size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in,
size_t in_len) {
+ *out_consumed = 0;
+
CBS cbs;
CBS_init(&cbs, in, in_len);
@@ -238,6 +245,8 @@
*out_alert = SSL_AD_BAD_RECORD_MAC;
return ssl_open_record_error;
}
+ *out_consumed = in_len - CBS_len(&cbs);
+
if (!ssl_record_sequence_update(ssl->s3->read_sequence, 8)) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return ssl_open_record_error;
@@ -281,9 +290,14 @@
ssl->s3->empty_record_count = 0;
}
+ if (type == SSL3_RT_ALERT) {
+ return ssl_process_alert(ssl, out_alert, out, plaintext_len);
+ }
+
+ ssl->s3->warning_alert_count = 0;
+
*out_type = type;
*out_len = plaintext_len;
- *out_consumed = in_len - CBS_len(&cbs);
return ssl_open_record_success;
}
@@ -417,3 +431,51 @@
SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
ssl->s3->aead_write_ctx = aead_ctx;
}
+
+enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
+ const uint8_t *in, size_t in_len) {
+ /* Alerts records may not contain fragmented or multiple alerts. */
+ if (in_len != 2) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
+ return ssl_open_record_error;
+ }
+
+ ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_ALERT, in, in_len);
+
+ const uint8_t alert_level = in[0];
+ const uint8_t alert_descr = in[1];
+
+ uint16_t alert = (alert_level << 8) | alert_descr;
+ ssl_do_info_callback(ssl, SSL_CB_READ_ALERT, alert);
+
+ if (alert_level == SSL3_AL_WARNING) {
+ if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+ return ssl_open_record_close_notify;
+ }
+
+ ssl->s3->warning_alert_count++;
+ if (ssl->s3->warning_alert_count > kMaxWarningAlerts) {
+ *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS);
+ return ssl_open_record_error;
+ }
+ return ssl_open_record_discard;
+ }
+
+ if (alert_level == SSL3_AL_FATAL) {
+ ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
+ SSL_CTX_remove_session(ssl->ctx, ssl->session);
+
+ char tmp[16];
+ OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
+ BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
+ ERR_add_error_data(2, "SSL alert number ", tmp);
+ return ssl_open_record_fatal_alert;
+ }
+
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE);
+ return ssl_open_record_error;
+}