1 /*
2  * Copyright (C) 2012 INRIA Paris-Rocquencourt
3  *
4  * Author: Alfredo Pironti
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 #include "gnutls_int.h"
24 #include "errors.h"
25 #include "algorithms.h"
26 #include "constate.h"
27 #include "record.h"
28 
29 static void
_gnutls_set_range(gnutls_range_st * dst,const size_t low,const size_t high)30 _gnutls_set_range(gnutls_range_st * dst, const size_t low,
31 		  const size_t high)
32 {
33 	dst->low = low;
34 	dst->high = high;
35 	return;
36 }
37 
38 /*
39  * Returns how much LH pad we can put in this fragment, given we'll
40  * put at least data_length bytes of user data.
41  */
42 static ssize_t
_gnutls_range_max_lh_pad(gnutls_session_t session,ssize_t data_length,ssize_t max_frag)43 _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
44 			 ssize_t max_frag)
45 {
46 	int ret;
47 	ssize_t max_pad;
48 	unsigned int fixed_pad;
49 	record_parameters_st *record_params;
50 	ssize_t this_pad;
51 	ssize_t block_size;
52 	ssize_t tag_size, overflow;
53 	const version_entry_st *vers = get_version(session);
54 
55 	if (unlikely(vers == NULL))
56 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
57 
58 	ret =
59 	    _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
60 			      &record_params);
61 	if (ret < 0) {
62 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
63 	}
64 
65 	if (!vers->tls13_sem && record_params->write.is_aead) /* not yet ready */
66 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
67 
68 	if (vers->tls13_sem) {
69 		max_pad = max_record_send_size(session, record_params);
70 		fixed_pad = 2;
71 	} else {
72 		max_pad = MAX_PAD_SIZE;
73 		fixed_pad = 1;
74 	}
75 
76 	this_pad = MIN(max_pad, max_frag - data_length);
77 
78 	block_size = _gnutls_cipher_get_block_size(record_params->cipher);
79 	tag_size =
80 	    _gnutls_auth_cipher_tag_len(&record_params->write.
81 					ctx.tls12);
82 	switch (_gnutls_cipher_type(record_params->cipher)) {
83 	case CIPHER_AEAD:
84 	case CIPHER_STREAM:
85 		return this_pad;
86 
87 	case CIPHER_BLOCK:
88 		overflow =
89 		    (data_length + this_pad + tag_size +
90 		     fixed_pad) % block_size;
91 		if (overflow > this_pad) {
92 			return this_pad;
93 		} else {
94 			return this_pad - overflow;
95 		}
96 	default:
97 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
98 	}
99 }
100 
101 /**
102  * gnutls_record_can_use_length_hiding:
103  * @session: is a #gnutls_session_t type.
104  *
105  * If the session supports length-hiding padding, you can
106  * invoke gnutls_record_send_range() to send a message whose
107  * length is hidden in the given range. If the session does not
108  * support length hiding padding, you can use the standard
109  * gnutls_record_send() function, or gnutls_record_send_range()
110  * making sure that the range is the same as the length of the
111  * message you are trying to send.
112  *
113  * Returns: true (1) if the current session supports length-hiding
114  * padding, false (0) if the current session does not.
115  **/
gnutls_record_can_use_length_hiding(gnutls_session_t session)116 unsigned gnutls_record_can_use_length_hiding(gnutls_session_t session)
117 {
118 	int ret;
119 	record_parameters_st *record_params;
120 	const version_entry_st *vers = get_version(session);
121 
122 	if (unlikely(vers == NULL))
123 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
124 
125 	if (vers->tls13_sem)
126 		return 1;
127 
128 #ifdef ENABLE_SSL3
129 	if (vers->id == GNUTLS_SSL3)
130 		return 0;
131 #endif
132 
133 	ret =
134 	    _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
135 			      &record_params);
136 	if (ret < 0) {
137 		return 0;
138 	}
139 
140 	switch (_gnutls_cipher_type(record_params->cipher)) {
141 	case CIPHER_BLOCK:
142 		return 1;
143 	case CIPHER_STREAM:
144 	case CIPHER_AEAD:
145 	default:
146 		return 0;
147 	}
148 }
149 
150 /**
151  * gnutls_range_split:
152  * @session: is a #gnutls_session_t type
153  * @orig: is the original range provided by the user
154  * @next: is the returned range that can be conveyed in a TLS record
155  * @remainder: is the returned remaining range
156  *
157  * This function should be used when it is required to hide the length
158  * of very long data that cannot be directly provided to gnutls_record_send_range().
159  * In that case this function should be called with the desired length
160  * hiding range in @orig. The returned @next value should then be used in
161  * the next call to gnutls_record_send_range() with the partial data.
162  * That process should be repeated until @remainder is (0,0).
163  *
164  * Returns: 0 in case splitting succeeds, non zero in case of error.
165  * Note that @orig is not changed, while the values of @next
166  * and @remainder are modified to store the resulting values.
167  */
168 int
gnutls_range_split(gnutls_session_t session,const gnutls_range_st * orig,gnutls_range_st * next,gnutls_range_st * remainder)169 gnutls_range_split(gnutls_session_t session,
170 		   const gnutls_range_st * orig,
171 		   gnutls_range_st * next, gnutls_range_st * remainder)
172 {
173 	int ret;
174 	ssize_t max_frag;
175 	ssize_t orig_low = (ssize_t) orig->low;
176 	ssize_t orig_high = (ssize_t) orig->high;
177 	record_parameters_st *record_params;
178 
179 	ret =
180 	    _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
181 			      &record_params);
182 	if (ret < 0)
183 		return gnutls_assert_val(ret);
184 
185 	max_frag = max_record_send_size(session, record_params);
186 
187 	if (orig_high == orig_low) {
188 		int length = MIN(orig_high, max_frag);
189 		int rem = orig_high - length;
190 		_gnutls_set_range(next, length, length);
191 		_gnutls_set_range(remainder, rem, rem);
192 
193 		return 0;
194 	} else {
195 		if (orig_low >= max_frag) {
196 			_gnutls_set_range(next, max_frag, max_frag);
197 			_gnutls_set_range(remainder, orig_low - max_frag,
198 					  orig_high - max_frag);
199 		} else {
200 			ret =
201 			    _gnutls_range_max_lh_pad(session, orig_low,
202 						     max_frag);
203 			if (ret < 0)
204 				return gnutls_assert_val(ret);
205 
206 			ssize_t this_pad = MIN(ret, orig_high - orig_low);
207 
208 			_gnutls_set_range(next, orig_low,
209 					  orig_low + this_pad);
210 			_gnutls_set_range(remainder, 0,
211 					  orig_high - (orig_low +
212 						       this_pad));
213 		}
214 
215 		return 0;
216 	}
217 }
218 
219 static size_t
_gnutls_range_fragment(size_t data_size,gnutls_range_st cur,gnutls_range_st next)220 _gnutls_range_fragment(size_t data_size, gnutls_range_st cur,
221 		       gnutls_range_st next)
222 {
223 	return MIN(cur.high, data_size - next.low);
224 }
225 
226 /**
227  * gnutls_record_send_range:
228  * @session: is a #gnutls_session_t type.
229  * @data: contains the data to send.
230  * @data_size: is the length of the data.
231  * @range: is the range of lengths in which the real data length must be hidden.
232  *
233  * This function operates like gnutls_record_send() but, while
234  * gnutls_record_send() adds minimal padding to each TLS record,
235  * this function uses the TLS extra-padding feature to conceal the real
236  * data size within the range of lengths provided.
237  * Some TLS sessions do not support extra padding (e.g. stream ciphers in standard
238  * TLS or SSL3 sessions). To know whether the current session supports extra
239  * padding, and hence length hiding, use the gnutls_record_can_use_length_hiding()
240  * function.
241  *
242  * Note: This function currently is limited to blocking sockets.
243  *
244  * Returns: The number of bytes sent (that is data_size in a successful invocation),
245  * or a negative error code.
246  **/
247 ssize_t
gnutls_record_send_range(gnutls_session_t session,const void * data,size_t data_size,const gnutls_range_st * range)248 gnutls_record_send_range(gnutls_session_t session, const void *data,
249 			 size_t data_size, const gnutls_range_st * range)
250 {
251 	size_t sent = 0;
252 	size_t next_fragment_length;
253 	ssize_t ret;
254 	gnutls_range_st cur_range, next_range;
255 
256 	/* sanity check on range and data size */
257 	if (range->low > range->high ||
258 	    data_size < range->low || data_size > range->high) {
259 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
260 	}
261 
262 	ret = gnutls_record_can_use_length_hiding(session);
263 	if (ret == 0)
264 		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
265 
266 	_gnutls_set_range(&cur_range, range->low, range->high);
267 
268 	_gnutls_record_log
269 	    ("RANGE: Preparing message with size %d, range (%d,%d)\n",
270 	     (int) data_size, (int) range->low, (int) range->high);
271 
272 	while (cur_range.high != 0) {
273 		ret =
274 		    gnutls_range_split(session, &cur_range, &cur_range,
275 				       &next_range);
276 		if (ret < 0) {
277 			return ret;	/* already gnutls_assert_val'd */
278 		}
279 
280 		next_fragment_length =
281 		    _gnutls_range_fragment(data_size, cur_range,
282 					   next_range);
283 
284 		_gnutls_record_log
285 		    ("RANGE: Next fragment size: %d (%d,%d); remaining range: (%d,%d)\n",
286 		     (int) next_fragment_length, (int) cur_range.low,
287 		     (int) cur_range.high, (int) next_range.low,
288 		     (int) next_range.high);
289 
290 		ret =
291 		    _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
292 					  -1, EPOCH_WRITE_CURRENT,
293 					  &(((char *) data)[sent]),
294 					  next_fragment_length,
295 					  cur_range.high -
296 					  next_fragment_length,
297 					  MBUFFER_FLUSH);
298 
299 		while (ret == GNUTLS_E_AGAIN
300 		       || ret == GNUTLS_E_INTERRUPTED) {
301 			ret =
302 			    _gnutls_send_tlen_int(session,
303 						  GNUTLS_APPLICATION_DATA,
304 						  -1, EPOCH_WRITE_CURRENT,
305 						  NULL, 0, 0,
306 						  MBUFFER_FLUSH);
307 		}
308 
309 		if (ret < 0) {
310 			return gnutls_assert_val(ret);
311 		}
312 		if (ret != (ssize_t) next_fragment_length) {
313 			_gnutls_record_log
314 			    ("RANGE: ERROR: ret = %d; next_fragment_length = %d\n",
315 			     (int) ret, (int) next_fragment_length);
316 			return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
317 		}
318 		sent += next_fragment_length;
319 		data_size -= next_fragment_length;
320 		_gnutls_set_range(&cur_range, next_range.low,
321 				  next_range.high);
322 	}
323 
324 	return sent;
325 }
326