123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- /*
- * 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 "api/s2n.h"
- #include "tls/s2n_alerts.h"
- #include "tls/s2n_connection.h"
- #include "tls/s2n_tls.h"
- #include "utils/s2n_atomic.h"
- #include "utils/s2n_safety.h"
- static bool s2n_shutdown_expect_close_notify(struct s2n_connection *conn)
- {
- /* No close_notify expected if we already received an error instead */
- if (s2n_atomic_flag_test(&conn->error_alert_received)) {
- return false;
- }
- /* No close_notify expected if we sent an error instead of a close_notify */
- if (conn->writer_alert_out || conn->reader_alert_out) {
- return false;
- }
- /* The purpose of the peer responding to our close_notify
- * with its own close_notify is to prevent application data truncation.
- * However, application data is not a concern during the handshake.
- *
- * Additionally, decrypting alerts sent during the handshake can be error prone
- * due to different encryption keys and may lead to unnecessary error reporting
- * and unnecessary blinding.
- */
- if (!s2n_handshake_is_complete(conn)) {
- return false;
- }
- /* QUIC does not use TLS alerts */
- if (conn->quic_enabled) {
- return false;
- }
- return true;
- }
- int s2n_shutdown_send(struct s2n_connection *conn, s2n_blocked_status *blocked)
- {
- POSIX_ENSURE_REF(conn);
- POSIX_ENSURE_REF(blocked);
- *blocked = S2N_NOT_BLOCKED;
- /* Treat this call as a no-op if already wiped.
- * This should probably be an error, but wasn't in the past so is left as-is
- * for backwards compatibility.
- */
- if (conn->send == NULL && conn->recv == NULL) {
- return S2N_SUCCESS;
- }
- /* Flush any outstanding data */
- s2n_atomic_flag_set(&conn->write_closed);
- POSIX_GUARD(s2n_flush(conn, blocked));
- /* For a connection closed due to receiving an alert, we don't send anything. */
- if (s2n_atomic_flag_test(&conn->error_alert_received)) {
- return S2N_SUCCESS;
- }
- /* If we've already sent an alert, don't send another. */
- if (conn->alert_sent) {
- return S2N_SUCCESS;
- }
- /* Enforce blinding.
- * If an application is using self-service blinding, ensure that they have
- * waited the required time before triggering any alerts.
- */
- uint64_t elapsed = 0;
- POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed));
- S2N_ERROR_IF(elapsed < conn->delay, S2N_ERR_SHUTDOWN_PAUSED);
- /**
- *= https://tools.ietf.org/rfc/rfc8446#section-6.1
- *# Each party MUST send a "close_notify" alert before closing its write
- *# side of the connection, unless it has already sent some error alert.
- */
- POSIX_GUARD_RESULT(s2n_alerts_write_error_or_close_notify(conn));
- POSIX_GUARD(s2n_flush(conn, blocked));
- return S2N_SUCCESS;
- }
- int s2n_shutdown(struct s2n_connection *conn, s2n_blocked_status *blocked)
- {
- POSIX_ENSURE_REF(conn);
- POSIX_ENSURE_REF(blocked);
- *blocked = S2N_NOT_BLOCKED;
- /* If necessary, send an alert to indicate shutdown. */
- POSIX_GUARD(s2n_shutdown_send(conn, blocked));
- /* If we don't expect a close_notify from our peer,
- * just ensure that the connection is marked closed.
- */
- if (!s2n_shutdown_expect_close_notify(conn)) {
- POSIX_GUARD_RESULT(s2n_connection_set_closed(conn));
- *blocked = S2N_NOT_BLOCKED;
- return S2N_SUCCESS;
- }
- /* Wait for the peer's close_notify. */
- uint8_t record_type = 0;
- int isSSLv2 = false;
- *blocked = S2N_BLOCKED_ON_READ;
- while (!s2n_atomic_flag_test(&conn->close_notify_received)) {
- POSIX_GUARD(s2n_read_full_record(conn, &record_type, &isSSLv2));
- POSIX_ENSURE(!isSSLv2, S2N_ERR_BAD_MESSAGE);
- if (record_type == TLS_ALERT) {
- POSIX_GUARD(s2n_process_alert_fragment(conn));
- }
- /* Wipe and keep trying */
- POSIX_GUARD(s2n_stuffer_wipe(&conn->header_in));
- POSIX_GUARD(s2n_stuffer_wipe(&conn->in));
- conn->in_status = ENCRYPTED;
- }
- *blocked = S2N_NOT_BLOCKED;
- return S2N_SUCCESS;
- }
|