aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/s2n/tls/s2n_renegotiate.c
blob: f8bf49ddf81902a1014b421f15fcc6d5fd1686c0 (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
/*
 * 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_renegotiate.h"

#include "error/s2n_errno.h"
#include "stuffer/s2n_stuffer.h"
#include "tls/s2n_connection.h"
#include "utils/s2n_safety.h"

/*
 * Prepare a connection to be reused for a second handshake.
 *
 * s2n-tls was not originally designed to support renegotiation.
 * s2n_connection is a very large structure with configuration fields set by the application
 * mixed in with internal state fields set by the handshake.
 * Ensuring all existing internal state fields (and any new fields added) are safe to reuse for
 * a renegotiated handshake would be extremely prone to errors.
 * For safety, we instead wipe the entire connection and only restore fields we know we need
 * in order to continue sending and receiving encrypted data.
 *
 * Any configuration fields set by the application will need to be set by the application again.
 */
int s2n_renegotiate_wipe(struct s2n_connection *conn)
{
    POSIX_ENSURE_REF(conn);

    /* We use this method to reset both clients and servers when testing.
     * However, outside of tests, it should only be called for client connections
     * because we only support renegotiation for clients.
     */
    POSIX_ENSURE(conn->mode == S2N_CLIENT || s2n_in_unit_test(), S2N_ERR_NO_RENEGOTIATION);

    /* Best effort check for pending input or output data.
     * This method should not be called until the application has stopped sending and receiving.
     * Saving partial read or parital write state would complicate this problem.
     */
    POSIX_ENSURE(s2n_stuffer_data_available(&conn->header_in) == 0, S2N_ERR_INVALID_STATE);
    POSIX_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_INVALID_STATE);
    POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_INVALID_STATE);

    /* Save the crypto parameters.
     * We need to continue encrypting / decrypting with the old secure parameters.
     */
    DEFER_CLEANUP(struct s2n_crypto_parameters *secure_crypto_params = conn->secure,
            s2n_crypto_parameters_free);
    conn->secure = NULL;

    /* Save the fragment length so we continue properly fragmenting our records
     * until a new fragment length is chosen.
     */
    uint16_t max_frag_len = conn->max_outgoing_fragment_length;

    /* Save the protocol versions.
     * Various checks when sending and receiving records rely on the protocol version. */
    uint8_t actual_protocol_version = conn->actual_protocol_version;
    uint8_t server_protocol_version = conn->server_protocol_version;
    uint8_t client_protocol_version = conn->client_protocol_version;
    POSIX_ENSURE(actual_protocol_version < S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);

    /* Save byte tracking.
     * This isn't strictly necessary, but potentially useful. */
    uint64_t wire_bytes_in = conn->wire_bytes_in;
    uint64_t wire_bytes_out = conn->wire_bytes_out;

    /* Save io settings */
    bool send_managed = conn->managed_send_io;
    s2n_send_fn *send_fn = conn->send;
    void *send_ctx = conn->send_io_context;
    bool recv_managed = conn->managed_recv_io;
    s2n_recv_fn *recv_fn = conn->recv;
    void *recv_ctx = conn->recv_io_context;
    /* Treat IO as unmanaged, since we don't want to clean it up yet */
    conn->managed_send_io = false;
    conn->managed_recv_io = false;

    /* Save the secure_renegotiation flag.
     * This flag should always be true, since we don't support insecure renegotiation,
     * but copying its value seems safer than just setting it to 'true'.
     */
    bool secure_renegotiation = conn->secure_renegotiation;
    POSIX_ENSURE(secure_renegotiation, S2N_ERR_NO_RENEGOTIATION);

    POSIX_GUARD(s2n_connection_wipe(conn));

    /* Setup the new crypto parameters.
     * The new handshake will negotiate new secure crypto parameters,
     * so the current secure crypto parameters become the initial crypto parameters.
     */
    POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial));
    conn->initial = secure_crypto_params;
    ZERO_TO_DISABLE_DEFER_CLEANUP(secure_crypto_params);
    conn->client = conn->initial;
    conn->server = conn->initial;

    /* Restore saved values */
    POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, max_frag_len));
    conn->actual_protocol_version = actual_protocol_version;
    conn->server_protocol_version = server_protocol_version;
    conn->client_protocol_version = client_protocol_version;
    conn->wire_bytes_in = wire_bytes_in;
    conn->wire_bytes_out = wire_bytes_out;
    conn->managed_send_io = send_managed;
    conn->send = send_fn;
    conn->send_io_context = send_ctx;
    conn->managed_recv_io = recv_managed;
    conn->recv = recv_fn;
    conn->recv_io_context = recv_ctx;
    conn->secure_renegotiation = secure_renegotiation;

    conn->handshake.renegotiation = true;
    return S2N_SUCCESS;
}