1 /*
2  * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3  *
4  * Author: Nikos Mavrogiannopoulos
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 functions that manipulate a database backend for
24  * resumed sessions.
25  */
26 
27 #include "gnutls_int.h"
28 #include "errors.h"
29 #include <db.h>
30 #include <session_pack.h>
31 #include <datum.h>
32 #include "ext/server_name.h"
33 #include <intprops.h>
34 
35 /**
36  * gnutls_db_set_retrieve_function:
37  * @session: is a #gnutls_session_t type.
38  * @retr_func: is the function.
39  *
40  * Sets the function that will be used to retrieve data from the
41  * resumed sessions database.  This function must return a
42  * gnutls_datum_t containing the data on success, or a gnutls_datum_t
43  * containing null and 0 on failure.
44  *
45  * The datum's data must be allocated using the function
46  * gnutls_malloc().
47  *
48  * The first argument to @retr_func will be null unless
49  * gnutls_db_set_ptr() has been called.
50  **/
51 void
gnutls_db_set_retrieve_function(gnutls_session_t session,gnutls_db_retr_func retr_func)52 gnutls_db_set_retrieve_function(gnutls_session_t session,
53 				gnutls_db_retr_func retr_func)
54 {
55 	session->internals.db_retrieve_func = retr_func;
56 }
57 
58 /**
59  * gnutls_db_set_remove_function:
60  * @session: is a #gnutls_session_t type.
61  * @rem_func: is the function.
62  *
63  * Sets the function that will be used to remove data from the
64  * resumed sessions database. This function must return 0 on success.
65  *
66  * The first argument to @rem_func will be null unless
67  * gnutls_db_set_ptr() has been called.
68  **/
69 void
gnutls_db_set_remove_function(gnutls_session_t session,gnutls_db_remove_func rem_func)70 gnutls_db_set_remove_function(gnutls_session_t session,
71 			      gnutls_db_remove_func rem_func)
72 {
73 	session->internals.db_remove_func = rem_func;
74 }
75 
76 /**
77  * gnutls_db_set_store_function:
78  * @session: is a #gnutls_session_t type.
79  * @store_func: is the function
80  *
81  * Sets the function that will be used to store data in the resumed
82  * sessions database. This function must return 0 on success.
83  *
84  * The first argument to @store_func will be null unless
85  * gnutls_db_set_ptr() has been called.
86  **/
87 void
gnutls_db_set_store_function(gnutls_session_t session,gnutls_db_store_func store_func)88 gnutls_db_set_store_function(gnutls_session_t session,
89 			     gnutls_db_store_func store_func)
90 {
91 	session->internals.db_store_func = store_func;
92 }
93 
94 /**
95  * gnutls_db_set_ptr:
96  * @session: is a #gnutls_session_t type.
97  * @ptr: is the pointer
98  *
99  * Sets the pointer that will be provided to db store, retrieve and
100  * delete functions, as the first argument.
101  **/
gnutls_db_set_ptr(gnutls_session_t session,void * ptr)102 void gnutls_db_set_ptr(gnutls_session_t session, void *ptr)
103 {
104 	session->internals.db_ptr = ptr;
105 }
106 
107 /**
108  * gnutls_db_get_ptr:
109  * @session: is a #gnutls_session_t type.
110  *
111  * Get db function pointer.
112  *
113  * Returns: the pointer that will be sent to db store, retrieve and
114  *   delete functions, as the first argument.
115  **/
gnutls_db_get_ptr(gnutls_session_t session)116 void *gnutls_db_get_ptr(gnutls_session_t session)
117 {
118 	return session->internals.db_ptr;
119 }
120 
121 /**
122  * gnutls_db_set_cache_expiration:
123  * @session: is a #gnutls_session_t type.
124  * @seconds: is the number of seconds.
125  *
126  * Set the expiration time for resumed sessions. The default is 21600
127  * (6 hours) at the time of writing.
128  *
129  * The maximum value that can be set using this function is 604800
130  * (7 days).
131  *
132  **/
gnutls_db_set_cache_expiration(gnutls_session_t session,int seconds)133 void gnutls_db_set_cache_expiration(gnutls_session_t session, int seconds)
134 {
135 	session->internals.expire_time = seconds;
136 	if (session->internals.expire_time > 604800)
137 		session->internals.expire_time = 604800;
138 }
139 
140 /**
141  * gnutls_db_get_default_cache_expiration:
142  *
143  * Returns the expiration time (in seconds) of stored sessions for resumption.
144  **/
gnutls_db_get_default_cache_expiration(void)145 unsigned gnutls_db_get_default_cache_expiration(void)
146 {
147 	return DEFAULT_EXPIRE_TIME;
148 }
149 
150 /**
151  * gnutls_db_check_entry:
152  * @session: is a #gnutls_session_t type.
153  * @session_entry: is the session data (not key)
154  *
155  * This function has no effect.
156  *
157  * Returns: Returns %GNUTLS_E_EXPIRED, if the database entry has
158  *   expired or 0 otherwise.
159  *
160  * Deprecated: This function is deprecated.
161  **/
162 int
gnutls_db_check_entry(gnutls_session_t session,gnutls_datum_t session_entry)163 gnutls_db_check_entry(gnutls_session_t session,
164 		      gnutls_datum_t session_entry)
165 {
166 	return 0;
167 }
168 
169 /**
170  * gnutls_db_check_entry_time:
171  * @entry: is a pointer to a #gnutls_datum_t type.
172  *
173  * This function returns the time that this entry was active.
174  * It can be used for database entry expiration.
175  *
176  * Returns: The time this entry was created, or zero on error.
177  **/
gnutls_db_check_entry_time(gnutls_datum_t * entry)178 time_t gnutls_db_check_entry_time(gnutls_datum_t * entry)
179 {
180 	uint32_t t;
181 	uint32_t magic;
182 
183 	if (entry->size < 8)
184 		return gnutls_assert_val(0);
185 
186 	magic = _gnutls_read_uint32(entry->data);
187 
188 	if (magic != PACKED_SESSION_MAGIC)
189 		return gnutls_assert_val(0);
190 
191 	t = _gnutls_read_uint32(&entry->data[4]);
192 
193 	return t;
194 }
195 
196 /**
197  * gnutls_db_check_entry_expire_time:
198  * @entry: is a pointer to a #gnutls_datum_t type.
199  *
200  * This function returns the time that this entry will expire.
201  * It can be used for database entry expiration.
202  *
203  * Returns: The time this entry will expire, or zero on error.
204  *
205  * Since: 3.6.5
206  **/
gnutls_db_check_entry_expire_time(gnutls_datum_t * entry)207 time_t gnutls_db_check_entry_expire_time(gnutls_datum_t *entry)
208 {
209 	uint32_t t;
210 	uint32_t e;
211 	uint32_t magic;
212 
213 	if (entry->size < 12)
214 		return gnutls_assert_val(0);
215 
216 	magic = _gnutls_read_uint32(entry->data);
217 
218 	if (magic != PACKED_SESSION_MAGIC)
219 		return gnutls_assert_val(0);
220 
221 	t = _gnutls_read_uint32(&entry->data[4]);
222 	e = _gnutls_read_uint32(&entry->data[8]);
223 
224 	if (INT_ADD_OVERFLOW(t, e))
225 		return gnutls_assert_val(0);
226 
227 	return t + e;
228 }
229 
230 /* Checks if both db_store and db_retrieve functions have
231  * been set up.
232  */
db_func_is_ok(gnutls_session_t session)233 static int db_func_is_ok(gnutls_session_t session)
234 {
235 	if (session->internals.db_store_func != NULL &&
236 	    session->internals.db_retrieve_func != NULL)
237 		return 0;
238 	else
239 		return GNUTLS_E_DB_ERROR;
240 }
241 
242 /* Stores session data to the db backend.
243  */
244 static int
store_session(gnutls_session_t session,gnutls_datum_t session_id,gnutls_datum_t session_data)245 store_session(gnutls_session_t session,
246 	      gnutls_datum_t session_id, gnutls_datum_t session_data)
247 {
248 	int ret = 0;
249 
250 	if (db_func_is_ok(session) != 0) {
251 		return GNUTLS_E_DB_ERROR;
252 	}
253 
254 	if (session_data.data == NULL || session_data.size == 0) {
255 		gnutls_assert();
256 		return GNUTLS_E_INVALID_SESSION;
257 	}
258 
259 	/* if we can't read why bother writing? */
260 	ret = session->internals.db_store_func(session->internals.db_ptr,
261 					       session_id, session_data);
262 
263 	return (ret == 0 ? ret : GNUTLS_E_DB_ERROR);
264 }
265 
_gnutls_server_register_current_session(gnutls_session_t session)266 int _gnutls_server_register_current_session(gnutls_session_t session)
267 {
268 	gnutls_datum_t key;
269 	gnutls_datum_t content;
270 	int ret = 0;
271 
272 	key.data = session->security_parameters.session_id;
273 	key.size = session->security_parameters.session_id_size;
274 
275 	if (session->internals.resumable == RESUME_FALSE) {
276 		gnutls_assert();
277 		return GNUTLS_E_INVALID_SESSION;
278 	}
279 
280 	if (session->security_parameters.session_id_size == 0) {
281 		gnutls_assert();
282 		return GNUTLS_E_INVALID_SESSION;
283 	}
284 
285 	ret = _gnutls_session_pack(session, &content);
286 	if (ret < 0) {
287 		gnutls_assert();
288 		return ret;
289 	}
290 
291 	ret = store_session(session, key, content);
292 	_gnutls_free_datum(&content);
293 
294 	return ret;
295 }
296 
_gnutls_check_resumed_params(gnutls_session_t session)297 int _gnutls_check_resumed_params(gnutls_session_t session)
298 {
299 	time_t timestamp = gnutls_time(0);
300 	const version_entry_st *vers;
301 
302 	/* check whether the session is expired */
303 	if (timestamp -
304 	    session->internals.resumed_security_parameters.timestamp >
305 	    session->internals.expire_time
306 	    || session->internals.resumed_security_parameters.timestamp >
307 	    timestamp)
308 		return gnutls_assert_val(GNUTLS_E_EXPIRED);
309 
310 	/* check various parameters applicable to resumption in TLS1.2 or earlier
311 	 */
312 	vers = get_version(session);
313 	if (!vers || !vers->tls13_sem) {
314 		if (session->internals.resumed_security_parameters.ext_master_secret !=
315 		    session->security_parameters.ext_master_secret)
316 			return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
317 
318 		if (!_gnutls_server_name_matches_resumed(session))
319 			return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
320 	}
321 
322 	return 0;
323 }
324 
325 int
_gnutls_server_restore_session(gnutls_session_t session,uint8_t * session_id,int session_id_size)326 _gnutls_server_restore_session(gnutls_session_t session,
327 			       uint8_t * session_id, int session_id_size)
328 {
329 	gnutls_datum_t data;
330 	gnutls_datum_t key;
331 	int ret;
332 
333 	if (session_id == NULL || session_id_size == 0) {
334 		gnutls_assert();
335 		return GNUTLS_E_INVALID_REQUEST;
336 	}
337 
338 	if (session->internals.premaster_set != 0) {	/* hack for CISCO's DTLS-0.9 */
339 		if (session_id_size ==
340 		    session->internals.resumed_security_parameters.
341 		    session_id_size
342 		    && memcmp(session_id,
343 			      session->internals.
344 			      resumed_security_parameters.session_id,
345 			      session_id_size) == 0)
346 			return 0;
347 	}
348 
349 	key.data = session_id;
350 	key.size = session_id_size;
351 
352 	if (db_func_is_ok(session) != 0) {
353 		gnutls_assert();
354 		return GNUTLS_E_INVALID_SESSION;
355 	}
356 
357 	data =
358 	    session->internals.db_retrieve_func(session->internals.db_ptr,
359 						key);
360 
361 	if (data.data == NULL) {
362 		gnutls_assert();
363 		return GNUTLS_E_INVALID_SESSION;
364 	}
365 
366 	ret = gnutls_session_set_data(session, data.data, data.size);
367 	gnutls_free(data.data);
368 
369 	if (ret < 0) {
370 		gnutls_assert();
371 		return ret;
372 	}
373 
374 	/* expiration check is performed inside */
375 	ret = _gnutls_check_resumed_params(session);
376 	if (ret < 0)
377 		return gnutls_assert_val(ret);
378 
379 	return 0;
380 }
381 
382 /**
383  * gnutls_db_remove_session:
384  * @session: is a #gnutls_session_t type.
385  *
386  * This function will remove the current session data from the
387  * session database.  This will prevent future handshakes reusing
388  * these session data.  This function should be called if a session
389  * was terminated abnormally, and before gnutls_deinit() is called.
390  *
391  * Normally gnutls_deinit() will remove abnormally terminated
392  * sessions.
393  **/
gnutls_db_remove_session(gnutls_session_t session)394 void gnutls_db_remove_session(gnutls_session_t session)
395 {
396 	gnutls_datum_t session_id;
397 	int ret = 0;
398 
399 	session_id.data = session->security_parameters.session_id;
400 	session_id.size = session->security_parameters.session_id_size;
401 
402 	if (session->internals.db_remove_func == NULL) {
403 		gnutls_assert();
404 		return /* GNUTLS_E_DB_ERROR */ ;
405 	}
406 
407 	if (session_id.data == NULL || session_id.size == 0) {
408 		gnutls_assert();
409 		return /* GNUTLS_E_INVALID_SESSION */ ;
410 	}
411 
412 	/* if we can't read why bother writing? */
413 	ret = session->internals.db_remove_func(session->internals.db_ptr,
414 						session_id);
415 	if (ret != 0)
416 		gnutls_assert();
417 }
418