aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/s2n/tls/s2n_shutdown.c
blob: 6086322a15edaa0c86f5528a1a90f9d37f641cc3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * 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;
}