1 /*
2  * Copyright (C) 2018 Red Hat, Inc.
3  *
4  * Author: Daiki Ueno
5  *
6  * This file is part of GnuTLS.
7  *
8  * The GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>
20  *
21  */
22 
23 /* This file contains the code for the Record Size Limit TLS extension.
24  */
25 
26 #include "gnutls_int.h"
27 #include "errors.h"
28 #include "num.h"
29 #include <hello_ext.h>
30 #include <ext/record_size_limit.h>
31 
32 static int _gnutls_record_size_limit_recv_params(gnutls_session_t session,
33 						 const uint8_t * data,
34 						 size_t data_size);
35 static int _gnutls_record_size_limit_send_params(gnutls_session_t session,
36 						 gnutls_buffer_st * extdata);
37 
38 const hello_ext_entry_st ext_mod_record_size_limit = {
39 	.name = "Record Size Limit",
40 	.tls_id = 28,
41 	.gid = GNUTLS_EXTENSION_RECORD_SIZE_LIMIT,
42 	.client_parse_point = GNUTLS_EXT_MANDATORY,
43 	.server_parse_point = GNUTLS_EXT_MANDATORY,
44 	.validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
45 		    GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO,
46 	.recv_func = _gnutls_record_size_limit_recv_params,
47 	.send_func = _gnutls_record_size_limit_send_params
48 };
49 
50 static int
_gnutls_record_size_limit_recv_params(gnutls_session_t session,const uint8_t * data,size_t data_size)51 _gnutls_record_size_limit_recv_params(gnutls_session_t session,
52 				      const uint8_t * data, size_t data_size)
53 {
54 	ssize_t new_size;
55 	const version_entry_st *vers;
56 
57 	DECR_LEN(data_size, 2);
58 	if (data_size != 0)
59 		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
60 	new_size = _gnutls_read_uint16(data);
61 
62 	/* protocol error */
63 	if (new_size < 64)
64 		return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
65 
66 	session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_RECEIVED;
67 
68 	/* we do not want to accept sizes outside of our supported range */
69 	if (new_size <
70 	    (session->internals.allow_small_records ?
71 	     MIN_RECORD_SIZE_SMALL : MIN_RECORD_SIZE)) {
72 		/* for server, reject it by omitting the extension in the reply */
73 		if (session->security_parameters.entity == GNUTLS_SERVER) {
74 			_gnutls_handshake_log("EXT[%p]: client requested too small record_size_limit %u; ignoring\n",
75 					      session, (unsigned)new_size);
76 			return gnutls_assert_val(0);
77 		} else {
78 			_gnutls_handshake_log("EXT[%p]: server requested too small record_size_limit %u; closing the connection\n",
79 					      session, (unsigned)new_size);
80 			return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
81 		}
82 	}
83 
84 	session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_NEGOTIATED;
85 
86 	/* client uses the reception of this extension as an
87 	 * indication of the request was accepted by the server */
88 	if (session->security_parameters.entity == GNUTLS_CLIENT)
89 		session->security_parameters.max_record_recv_size =
90 			session->security_parameters.max_user_record_recv_size;
91 
92 	_gnutls_handshake_log("EXT[%p]: record_size_limit %u negotiated\n",
93 			      session, (unsigned)new_size);
94 
95 	/* subtract 1 octet for content type */
96 	vers = get_version(session);
97 	if (unlikely(vers == NULL))
98 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
99 
100 	session->security_parameters.max_record_send_size =
101 		MIN(new_size - vers->tls13_sem,
102 		    session->security_parameters.max_user_record_send_size);
103 
104 	return 0;
105 }
106 
107 /* returns data_size or a negative number on failure
108  */
109 static int
_gnutls_record_size_limit_send_params(gnutls_session_t session,gnutls_buffer_st * extdata)110 _gnutls_record_size_limit_send_params(gnutls_session_t session,
111 				      gnutls_buffer_st * extdata)
112 {
113 	int ret;
114 	uint16_t send_size;
115 
116 	assert(session->security_parameters.max_user_record_recv_size >= 64 &&
117 	       session->security_parameters.max_user_record_recv_size <=
118 	       DEFAULT_MAX_RECORD_SIZE);
119 
120 	send_size = session->security_parameters.max_user_record_recv_size;
121 
122 	if (session->security_parameters.entity == GNUTLS_SERVER) {
123 		const version_entry_st *vers;
124 
125 		/* if we had received the extension and rejected, don't send it */
126 		if (session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_RECEIVED &&
127 		    !(session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_NEGOTIATED))
128 			return gnutls_assert_val(0);
129 
130 		/* add 1 octet for content type */
131 		vers = get_version(session);
132 		if (unlikely(vers == NULL))
133 			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
134 
135 		session->security_parameters.max_record_recv_size =
136 			send_size;
137 
138 		send_size += vers->tls13_sem;
139 	} else {
140 		const version_entry_st *vers;
141 
142 		/* add 1 octet for content type */
143 		vers = _gnutls_version_max(session);
144 		if (unlikely(vers == NULL))
145 			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
146 
147 		send_size += vers->tls13_sem;
148 	}
149 
150 	ret = _gnutls_buffer_append_prefix(extdata, 16, send_size);
151 	if (ret < 0)
152 		return gnutls_assert_val(ret);
153 
154 	session->internals.hsk_flags |= HSK_RECORD_SIZE_LIMIT_SENT;
155 
156 	return 2;
157 }
158