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