LCOV - code coverage report
Current view: top level - tls - s2n_renegotiate.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 93 93 100.0 %
Date: 2025-08-15 07:28:39 Functions: 4 4 100.0 %
Branches: 52 110 47.3 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License").
       5                 :            :  * You may not use this file except in compliance with the License.
       6                 :            :  * A copy of the License is located at
       7                 :            :  *
       8                 :            :  *  http://aws.amazon.com/apache2.0
       9                 :            :  *
      10                 :            :  * or in the "license" file accompanying this file. This file is distributed
      11                 :            :  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
      12                 :            :  * express or implied. See the License for the specific language governing
      13                 :            :  * permissions and limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "tls/s2n_renegotiate.h"
      17                 :            : 
      18                 :            : #include "error/s2n_errno.h"
      19                 :            : #include "stuffer/s2n_stuffer.h"
      20                 :            : #include "tls/s2n_connection.h"
      21                 :            : #include "utils/s2n_safety.h"
      22                 :            : 
      23                 :            : /* We don't want to introduce a new blocked status for renegotiation
      24                 :            :  * because that would potentially require applications to update their
      25                 :            :  * blocked status handling logic for no reason.
      26                 :            :  *
      27                 :            :  * It is impossible to use both early data and renegotiation for the same handshake,
      28                 :            :  * and both are just application data received during the handshake.
      29                 :            :  * Therefore, we will alias S2N_BLOCKED_ON_EARLY_DATA for reuse with renegotiation.
      30                 :            :  */
      31                 :            : const s2n_blocked_status S2N_BLOCKED_ON_APPLICATION_DATA = S2N_BLOCKED_ON_EARLY_DATA;
      32                 :            : 
      33                 :            : S2N_RESULT s2n_renegotiate_validate(struct s2n_connection *conn)
      34                 :        103 : {
      35 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE_REF(conn);
      36 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_NO_RENEGOTIATION);
      37 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE(conn->secure_renegotiation, S2N_ERR_NO_RENEGOTIATION);
      38 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE(conn->handshake.renegotiation, S2N_ERR_INVALID_STATE);
      39 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE(!conn->ktls_send_enabled, S2N_ERR_KTLS_RENEG);
      40 [ -  + ][ #  # ]:        103 :     RESULT_ENSURE(!conn->ktls_recv_enabled, S2N_ERR_KTLS_RENEG);
      41                 :        103 :     return S2N_RESULT_OK;
      42                 :        103 : }
      43                 :            : 
      44                 :            : /*
      45                 :            :  * Prepare a connection to be reused for a second handshake.
      46                 :            :  *
      47                 :            :  * s2n-tls was not originally designed to support renegotiation.
      48                 :            :  * s2n_connection is a very large structure with configuration fields set by the application
      49                 :            :  * mixed in with internal state fields set by the handshake.
      50                 :            :  * Ensuring all existing internal state fields (and any new fields added) are safe to reuse for
      51                 :            :  * a renegotiated handshake would be extremely prone to errors.
      52                 :            :  * For safety, we instead wipe the entire connection and only restore fields we know we need
      53                 :            :  * in order to continue sending and receiving encrypted data.
      54                 :            :  *
      55                 :            :  * Any configuration fields set by the application will need to be set by the application again.
      56                 :            :  */
      57                 :            : int s2n_renegotiate_wipe(struct s2n_connection *conn)
      58                 :        525 : {
      59 [ -  + ][ #  # ]:        525 :     POSIX_ENSURE_REF(conn);
      60                 :            : 
      61                 :            :     /* We use this method to reset both clients and servers when testing.
      62                 :            :      * However, outside of tests, it should only be called for client connections
      63                 :            :      * because we only support renegotiation for clients.
      64                 :            :      */
      65 [ #  # ][ +  + ]:        525 :     POSIX_ENSURE(conn->mode == S2N_CLIENT || s2n_in_unit_test(), S2N_ERR_NO_RENEGOTIATION);
                 [ +  - ]
      66                 :            : 
      67                 :            :     /* Best effort check for pending input or output data.
      68                 :            :      * This method should not be called until the application has stopped sending and receiving.
      69                 :            :      * Saving partial read or parital write state would complicate this problem.
      70                 :            :      */
      71 [ +  + ][ +  - ]:        525 :     POSIX_ENSURE(s2n_stuffer_data_available(&conn->header_in) == 0, S2N_ERR_INVALID_STATE);
      72 [ -  + ][ #  # ]:        524 :     POSIX_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_INVALID_STATE);
      73 [ +  + ][ +  - ]:        524 :     POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_INVALID_STATE);
      74                 :            : 
      75                 :            :     /* buffer_in might contain data needed to read the next records. */
      76                 :        523 :     DEFER_CLEANUP(struct s2n_stuffer buffer_in = conn->buffer_in, s2n_stuffer_free);
      77                 :        523 :     conn->buffer_in = (struct s2n_stuffer){ 0 };
      78         [ -  + ]:        523 :     POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->buffer_in, 0));
      79                 :            : 
      80                 :            :     /* Save the crypto parameters.
      81                 :            :      * We need to continue encrypting / decrypting with the old secure parameters.
      82                 :            :      */
      83                 :        523 :     DEFER_CLEANUP(struct s2n_crypto_parameters *secure_crypto_params = conn->secure,
      84                 :        523 :             s2n_crypto_parameters_free);
      85                 :        523 :     conn->secure = NULL;
      86                 :            : 
      87                 :            :     /* Save the fragment length so we continue properly fragmenting our records
      88                 :            :      * until a new fragment length is chosen.
      89                 :            :      */
      90                 :        523 :     uint16_t max_frag_len = conn->max_outgoing_fragment_length;
      91                 :            : 
      92                 :            :     /* Save the protocol versions.
      93                 :            :      * Various checks when sending and receiving records rely on the protocol version. */
      94                 :        523 :     uint8_t actual_protocol_version = conn->actual_protocol_version;
      95                 :        523 :     uint8_t server_protocol_version = conn->server_protocol_version;
      96                 :        523 :     uint8_t client_protocol_version = conn->client_protocol_version;
      97 [ +  + ][ +  - ]:        523 :     POSIX_ENSURE(actual_protocol_version < S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
      98                 :            : 
      99                 :            :     /* Save byte tracking.
     100                 :            :      * This isn't strictly necessary, but potentially useful. */
     101                 :        522 :     uint64_t wire_bytes_in = conn->wire_bytes_in;
     102                 :        522 :     uint64_t wire_bytes_out = conn->wire_bytes_out;
     103                 :            : 
     104                 :            :     /* Save io settings */
     105                 :        522 :     bool send_managed = conn->managed_send_io;
     106                 :        522 :     s2n_send_fn *send_fn = conn->send;
     107                 :        522 :     void *send_ctx = conn->send_io_context;
     108                 :        522 :     bool recv_managed = conn->managed_recv_io;
     109                 :        522 :     s2n_recv_fn *recv_fn = conn->recv;
     110                 :        522 :     void *recv_ctx = conn->recv_io_context;
     111                 :            :     /* Treat IO as unmanaged, since we don't want to clean it up yet */
     112                 :        522 :     conn->managed_send_io = false;
     113                 :        522 :     conn->managed_recv_io = false;
     114                 :            : 
     115                 :            :     /* Save the secure_renegotiation flag.
     116                 :            :      * This flag should always be true, since we don't support insecure renegotiation,
     117                 :            :      * but copying its value seems safer than just setting it to 'true'.
     118                 :            :      */
     119                 :        522 :     bool secure_renegotiation = conn->secure_renegotiation;
     120 [ +  + ][ +  - ]:        522 :     POSIX_ENSURE(secure_renegotiation, S2N_ERR_NO_RENEGOTIATION);
     121                 :            : 
     122                 :            :     /* Save the finished data.
     123                 :            :      * This is required for the renegotiate_info extension on the new handshake.
     124                 :            :      */
     125                 :        521 :     uint8_t finished_len = conn->handshake.finished_len;
     126                 :        521 :     uint8_t client_finished[sizeof(conn->handshake.client_finished)] = { 0 };
     127 [ #  # ][ -  + ]:        521 :     POSIX_CHECKED_MEMCPY(client_finished, conn->handshake.client_finished, finished_len);
                 [ +  + ]
     128                 :        521 :     uint8_t server_finished[sizeof(conn->handshake.server_finished)] = { 0 };
     129 [ -  + ][ #  # ]:        521 :     POSIX_CHECKED_MEMCPY(server_finished, conn->handshake.server_finished, finished_len);
                 [ +  + ]
     130                 :            : 
     131         [ -  + ]:        521 :     POSIX_GUARD(s2n_connection_wipe(conn));
     132                 :            : 
     133                 :            :     /* Setup the new crypto parameters.
     134                 :            :      * The new handshake will negotiate new secure crypto parameters,
     135                 :            :      * so the current secure crypto parameters become the initial crypto parameters.
     136                 :            :      */
     137         [ -  + ]:        521 :     POSIX_GUARD_RESULT(s2n_crypto_parameters_free(&conn->initial));
     138                 :        521 :     conn->initial = secure_crypto_params;
     139                 :        521 :     ZERO_TO_DISABLE_DEFER_CLEANUP(secure_crypto_params);
     140                 :        521 :     conn->client = conn->initial;
     141                 :        521 :     conn->server = conn->initial;
     142                 :            : 
     143                 :            :     /* Restore saved values */
     144         [ -  + ]:        521 :     POSIX_GUARD_RESULT(s2n_connection_set_max_fragment_length(conn, max_frag_len));
     145 [ -  + ][ #  # ]:        521 :     POSIX_CHECKED_MEMCPY(conn->handshake.client_finished, client_finished, finished_len);
                 [ +  + ]
     146 [ -  + ][ #  # ]:        521 :     POSIX_CHECKED_MEMCPY(conn->handshake.server_finished, server_finished, finished_len);
                 [ +  + ]
     147                 :        521 :     conn->handshake.finished_len = finished_len;
     148                 :        521 :     conn->actual_protocol_version = actual_protocol_version;
     149                 :        521 :     conn->server_protocol_version = server_protocol_version;
     150                 :        521 :     conn->client_protocol_version = client_protocol_version;
     151                 :        521 :     conn->wire_bytes_in = wire_bytes_in;
     152                 :        521 :     conn->wire_bytes_out = wire_bytes_out;
     153                 :        521 :     conn->managed_send_io = send_managed;
     154                 :        521 :     conn->send = send_fn;
     155                 :        521 :     conn->send_io_context = send_ctx;
     156                 :        521 :     conn->managed_recv_io = recv_managed;
     157                 :        521 :     conn->recv = recv_fn;
     158                 :        521 :     conn->recv_io_context = recv_ctx;
     159                 :        521 :     conn->secure_renegotiation = secure_renegotiation;
     160                 :        521 :     conn->buffer_in = buffer_in;
     161                 :        521 :     ZERO_TO_DISABLE_DEFER_CLEANUP(buffer_in);
     162                 :            : 
     163                 :        521 :     conn->handshake.renegotiation = true;
     164                 :        521 :     return S2N_SUCCESS;
     165                 :        521 : }
     166                 :            : 
     167                 :            : static S2N_RESULT s2n_renegotiate_read_app_data(struct s2n_connection *conn, uint8_t *app_data_buf, ssize_t app_data_buf_size,
     168                 :            :         ssize_t *app_data_size, s2n_blocked_status *blocked)
     169                 :         21 : {
     170 [ -  + ][ #  # ]:         21 :     RESULT_ENSURE_REF(blocked);
     171                 :            : 
     172                 :         21 :     ssize_t r = s2n_recv(conn, app_data_buf, app_data_buf_size, blocked);
     173         [ -  + ]:         21 :     RESULT_GUARD_POSIX(r);
     174                 :         21 :     *app_data_size = r;
     175                 :            : 
     176                 :         21 :     *blocked = S2N_BLOCKED_ON_APPLICATION_DATA;
     177         [ +  - ]:         21 :     RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED);
     178                 :         21 : }
     179                 :            : 
     180                 :            : int s2n_renegotiate(struct s2n_connection *conn, uint8_t *app_data_buf, ssize_t app_data_buf_size,
     181                 :            :         ssize_t *app_data_size, s2n_blocked_status *blocked)
     182                 :         78 : {
     183         [ -  + ]:         78 :     POSIX_GUARD_RESULT(s2n_renegotiate_validate(conn));
     184                 :            : 
     185 [ -  + ][ #  # ]:         78 :     POSIX_ENSURE_REF(app_data_size);
     186                 :         78 :     *app_data_size = 0;
     187                 :            : 
     188                 :            :     /* If there is outstanding application data, pass it back to the application.
     189                 :            :      * We can't read the next handshake record until we drain the buffer.
     190                 :            :      */
     191         [ +  + ]:         78 :     if (s2n_peek(conn) > 0) {
     192         [ +  - ]:          1 :         POSIX_GUARD_RESULT(s2n_renegotiate_read_app_data(conn,
     193                 :          1 :                 app_data_buf, app_data_buf_size, app_data_size, blocked));
     194                 :          1 :     }
     195                 :            : 
     196                 :         77 :     int result = s2n_negotiate(conn, blocked);
     197                 :            : 
     198                 :            :     /* If we encounter application data while reading handshake records,
     199                 :            :      * pass it back to the application.
     200                 :            :      */
     201 [ +  + ][ +  + ]:         77 :     if (result != S2N_SUCCESS && s2n_errno == S2N_ERR_APP_DATA_BLOCKED) {
     202         [ +  - ]:         20 :         POSIX_GUARD_RESULT(s2n_renegotiate_read_app_data(conn,
     203                 :         20 :                 app_data_buf, app_data_buf_size, app_data_size, blocked));
     204                 :         20 :     }
     205                 :            : 
     206                 :         57 :     return result;
     207                 :         77 : }

Generated by: LCOV version 1.14