*** empty log message ***
[gnutls.git] / lib / gnutls_db.c
bloba7405932ea7dccf886a66bc650c061b1dcac96b4
1 /*
2 * Copyright (C) 2000,2002 Nikos Mavroyanopoulos
4 * This file is part of GNUTLS.
6 * The GNUTLS library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 /* This file contains functions that manipulate a database
23 * for resumed sessions.
25 #include "gnutls_int.h"
26 #include "gnutls_errors.h"
27 #include "gnutls_session.h"
28 #include <gnutls_db.h>
29 #include "debug.h"
30 #include <gnutls_session_pack.h>
32 #define GNUTLS_DBNAME state->gnutls_internals.db_name
34 #ifdef HAVE_LIBGDBM
35 # define GNUTLS_DBF state->gnutls_internals.db_reader
36 # define GNUTLS_REOPEN_DB() if (GNUTLS_DBF!=NULL) \
37 gdbm_close( GNUTLS_DBF); \
38 GNUTLS_DBF = gdbm_open(GNUTLS_DBNAME, 0, GDBM_READER, 0600, NULL);
39 #endif
41 /**
42 * gnutls_db_set_retrieve_function - Sets the function that will be used to get data
43 * @state: is a &GNUTLS_STATE structure.
44 * @retr_func: is the function.
46 * Sets the function that will be used to retrieve data from the resumed
47 * sessions database. This function must return a gnutls_datum containing the
48 * data on success, or a gnutls_datum containing null and 0 on failure.
49 * This function should only be used if you do
50 * not plan to use the included gdbm backend.
52 * The first argument to store_func() will be null unless gnutls_db_set_ptr()
53 * has been called.
55 **/
56 void gnutls_db_set_retrieve_function( GNUTLS_STATE state, GNUTLS_DB_RETR_FUNC retr_func) {
57 state->gnutls_internals.db_retrieve_func = retr_func;
60 /**
61 * gnutls_db_set_remove_function - Sets the function that will be used to remove data
62 * @state: is a &GNUTLS_STATE structure.
63 * @rem_func: is the function.
65 * Sets the function that will be used to remove data from the resumed
66 * sessions database. This function must return 0 on success.
67 * This function should only be used if you do
68 * not plan to use the included gdbm backend.
70 * The first argument to rem_func() will be null unless gnutls_db_set_ptr()
71 * has been called.
73 **/
74 void gnutls_db_set_remove_function( GNUTLS_STATE state, GNUTLS_DB_REMOVE_FUNC rem_func) {
75 state->gnutls_internals.db_remove_func = rem_func;
78 /**
79 * gnutls_db_set_store_function - Sets the function that will be used to put data
80 * @state: is a &GNUTLS_STATE structure.
81 * @store_func: is the function
83 * Sets the function that will be used to store data from the resumed
84 * sessions database. This function must remove 0 on success.
85 * This function should only be used if you do
86 * not plan to use the included gdbm backend.
88 * The first argument to store_func() will be null unless gnutls_db_set_ptr()
89 * has been called.
91 **/
92 void gnutls_db_set_store_function( GNUTLS_STATE state, GNUTLS_DB_STORE_FUNC store_func) {
93 state->gnutls_internals.db_store_func = store_func;
96 /**
97 * gnutls_db_set_ptr - Sets a pointer to be sent to db functions
98 * @state: is a &GNUTLS_STATE structure.
99 * @ptr: is the pointer
101 * Sets the pointer that will be sent to db store, retrieve and delete functions, as
102 * the first argument. Should only be called if not using the gdbm backend.
105 void gnutls_db_set_ptr( GNUTLS_STATE state, void* ptr) {
106 state->gnutls_internals.db_ptr = ptr;
110 * gnutls_db_get_ptr - Returns the pointer which is sent to db functions
111 * @state: is a &GNUTLS_STATE structure.
113 * Returns the pointer that will be sent to db store, retrieve and delete functions, as
114 * the first argument. Should only be used if not using the default (gdbm) backend.
117 void* gnutls_db_get_ptr( GNUTLS_STATE state) {
118 return state->gnutls_internals.db_ptr;
122 * gnutls_db_set_cache_expiration - Sets the expiration time for resumed sessions.
123 * @state: is a &GNUTLS_STATE structure.
124 * @seconds: is the number of seconds.
126 * Sets the expiration time for resumed sessions. The default is 3600 (one hour)
127 * at the time writing this.
129 void gnutls_db_set_cache_expiration( GNUTLS_STATE state, int seconds) {
130 state->gnutls_internals.expire_time = seconds;
134 * gnutls_db_set_name - Sets the name of the database that holds TLS sessions.
135 * @state: is a &GNUTLS_STATE structure.
136 * @filename: is the filename for the database
138 * Sets the name of the (gdbm) database to be used to keep
139 * the sessions to be resumed. This function also creates the database
140 * - if it does not exist - and opens it for reading.
141 * You should not call this function if using an other backend
142 * than gdbm (ie. called function gnutls_db_set_store_func() etc.)
145 int gnutls_db_set_name( GNUTLS_STATE state, const char* filename) {
146 #ifdef HAVE_LIBGDBM
147 GDBM_FILE dbf;
149 if (filename==NULL) return 0;
151 /* deallocate previous name */
152 if (GNUTLS_DBNAME!=NULL)
153 gnutls_free(GNUTLS_DBNAME);
155 /* set name */
156 GNUTLS_DBNAME = gnutls_strdup(filename);
157 if (GNUTLS_DBNAME==NULL) return GNUTLS_E_MEMORY_ERROR;
159 /* open for reader */
160 GNUTLS_DBF = gdbm_open(GNUTLS_DBNAME, 0, GDBM_READER, 0600, NULL);
161 if (GNUTLS_DBF==NULL) {
162 /* maybe it does not exist - so try to
163 * create it.
165 dbf = gdbm_open( (char*)filename, 0, GDBM_WRCREAT, 0600, NULL);
166 if (dbf==NULL) return GNUTLS_E_DB_ERROR;
167 gdbm_close(dbf);
169 /* try to open again */
170 GNUTLS_DBF = gdbm_open(GNUTLS_DBNAME, 0, GDBM_READER, 0600, NULL);
172 if (GNUTLS_DBF==NULL)
173 return GNUTLS_E_DB_ERROR;
175 return 0;
176 #else
177 return GNUTLS_E_UNIMPLEMENTED_FEATURE;
178 #endif
182 * gnutls_db_check_entry - checks if the given db entry has expired
183 * @state: is a &GNUTLS_STATE structure.
184 * @session_entry: is the session data (not key)
186 * This function should only be used if not using the gdbm backend.
187 * This function returns GNUTLS_E_EXPIRED, if the database entry
188 * has expired or 0 otherwise. This function is to be used when
189 * you want to clear unnesessary session which occupy space in your
190 * backend.
193 int gnutls_db_check_entry( GNUTLS_STATE state, gnutls_datum session_entry) {
194 time_t timestamp;
196 timestamp = time(0);
198 if (session_entry.data != NULL)
199 if ( timestamp - ((SecurityParameters*)(session_entry.data))->timestamp <= state->gnutls_internals.expire_time || ((SecurityParameters*)(session_entry.data))->timestamp > timestamp|| ((SecurityParameters*)(session_entry.data))->timestamp == 0)
200 return GNUTLS_E_EXPIRED;
202 return 0;
206 * gnutls_db_clean - removes expired and invalid sessions from the database
207 * @state: is a &GNUTLS_STATE structure.
209 * This function Deletes all expired records in the resumed sessions' database.
210 * This database may become huge if this function is not called.
211 * This function is also quite expensive. This function should only
212 * be called if using the gdbm backend.
215 int gnutls_db_clean( GNUTLS_STATE state) {
216 #ifdef HAVE_LIBGDBM
217 GDBM_FILE dbf;
218 int ret;
219 datum key;
220 time_t timestamp;
221 gnutls_datum _key;
223 if (GNUTLS_DBF==NULL) return GNUTLS_E_DB_ERROR;
224 if (GNUTLS_DBNAME==NULL) return GNUTLS_E_DB_ERROR;
226 dbf = gdbm_open(GNUTLS_DBNAME, 0, GDBM_WRITER, 0600, NULL);
227 if (dbf==NULL) return GNUTLS_E_AGAIN;
228 key = gdbm_firstkey(dbf);
230 timestamp = time(0);
232 _key.data = key.dptr;
233 _key.size = key.dsize;
234 while( _key.data != NULL) {
236 if ( gnutls_db_check_entry( state, _key)==GNUTLS_E_EXPIRED) {
237 /* delete expired entry */
238 gdbm_delete( dbf, key);
241 free(key.dptr);
242 key = gdbm_nextkey(dbf, key);
244 ret = gdbm_reorganize(dbf);
246 gdbm_close(dbf);
247 GNUTLS_REOPEN_DB();
249 if (ret!=0) return GNUTLS_E_DB_ERROR;
251 return 0;
252 #else
253 return GNUTLS_E_UNIMPLEMENTED_FEATURE;
254 #endif
258 /* The format of storing data is:
259 * (forget it). Check gnutls_session_pack.c
261 int _gnutls_server_register_current_session( GNUTLS_STATE state)
263 gnutls_datum key = { state->security_parameters.session_id, state->security_parameters.session_id_size };
264 gnutls_datum content;
265 int ret = 0;
267 if (state->gnutls_internals.resumable==RESUME_FALSE) {
268 gnutls_assert();
269 return GNUTLS_E_INVALID_SESSION;
272 if (state->security_parameters.session_id==NULL || state->security_parameters.session_id_size==0) {
273 gnutls_assert();
274 return GNUTLS_E_INVALID_SESSION;
277 /* allocate space for data */
278 content.size = _gnutls_session_size( state);
279 if (content.size < 0) {
280 gnutls_assert();
281 return content.size;
284 content.data = gnutls_malloc( content.size);
285 if (content.data==NULL) {
286 gnutls_assert();
287 return GNUTLS_E_MEMORY_ERROR;
290 /* copy data */
291 ret = _gnutls_session_pack( state, &content);
292 if (ret < 0) {
293 gnutls_free( content.data);
294 gnutls_assert();
295 return ret;
298 ret = _gnutls_store_session( state, key, content);
300 gnutls_free( content.data);
302 return ret;
305 /* Checks if both db_store and db_retrieve functions have
306 * been set up.
308 static int _gnutls_db_func_is_ok( GNUTLS_STATE state) {
309 if (state->gnutls_internals.db_store_func!=NULL &&
310 state->gnutls_internals.db_retrieve_func!=NULL &&
311 state->gnutls_internals.db_remove_func!=NULL) return 0;
312 else return GNUTLS_E_DB_ERROR;
316 int _gnutls_server_restore_session( GNUTLS_STATE state, uint8* session_id, int session_id_size)
318 gnutls_datum data;
319 gnutls_datum key = { session_id, session_id_size };
320 int ret;
322 if (GNUTLS_DBNAME==NULL && _gnutls_db_func_is_ok(state)!=0) {
323 gnutls_assert();
324 return GNUTLS_E_INVALID_SESSION;
327 data = _gnutls_retrieve_session( state, key);
329 if (data.data==NULL) {
330 gnutls_assert();
331 return GNUTLS_E_INVALID_SESSION;
334 /* expiration check is performed inside */
335 ret = gnutls_session_set_data( state, data.data, data.size);
336 if (ret < 0) {
337 gnutls_assert();
340 /* Note: Data is not allocated with gnutls_malloc
342 free(data.data);
344 return 0;
347 int _gnutls_db_remove_session( GNUTLS_STATE state, uint8* session_id, int session_id_size)
349 gnutls_datum key = { session_id, session_id_size };
351 return _gnutls_remove_session( state, key);
355 /* Stores session data to the db backend.
357 int _gnutls_store_session( GNUTLS_STATE state, gnutls_datum session_id, gnutls_datum session_data)
359 #ifdef HAVE_LIBGDBM
360 GDBM_FILE dbf;
361 datum key = { session_id.data, session_id.size };
362 datum content = {session_data.data, session_data.size};
363 #endif
364 int ret = 0;
366 if (state->gnutls_internals.resumable==RESUME_FALSE) {
367 gnutls_assert();
368 return GNUTLS_E_INVALID_SESSION;
371 if (GNUTLS_DBNAME==NULL && _gnutls_db_func_is_ok(state)!=0) {
372 return GNUTLS_E_DB_ERROR;
375 if (session_id.data==NULL || session_id.size==0) {
376 gnutls_assert();
377 return GNUTLS_E_INVALID_SESSION;
380 if (session_data.data==NULL || session_data.size==0) {
381 gnutls_assert();
382 return GNUTLS_E_INVALID_SESSION;
384 /* if we can't read why bother writing? */
386 #ifdef HAVE_LIBGDBM
387 if (GNUTLS_DBF!=NULL) { /* use gdbm */
388 dbf = gdbm_open(GNUTLS_DBNAME, 0, GDBM_WRITER, 0600, NULL);
389 if (dbf==NULL) {
390 /* cannot open db for writing. This may happen if multiple
391 * instances try to write.
393 gnutls_assert();
394 return GNUTLS_E_AGAIN;
396 ret = gdbm_store( dbf, key, content, GDBM_INSERT);
397 if (ret<0) {
398 gnutls_assert();
400 gdbm_close(dbf);
402 return 0; /*GNUTLS_E_UNIMPLEMENTED_FEATURE;*/
404 else
405 #endif
406 if (state->gnutls_internals.db_store_func!=NULL)
407 ret = state->gnutls_internals.db_store_func( state->gnutls_internals.db_ptr, session_id, session_data);
410 return (ret == 0 ? ret : GNUTLS_E_DB_ERROR);
414 /* Retrieves session data from the db backend.
416 gnutls_datum _gnutls_retrieve_session( GNUTLS_STATE state, gnutls_datum session_id)
418 #ifdef HAVE_LIBGDBM
419 datum key = { session_id.data, session_id.size };
420 datum content;
421 #endif
422 gnutls_datum ret = { NULL, 0 };
424 if (session_id.data==NULL || session_id.size==0) {
425 gnutls_assert();
426 return ret;
429 /* if we can't read why bother writing? */
430 #ifdef HAVE_LIBGDBM
431 if (GNUTLS_DBF!=NULL) { /* use gdbm */
432 content = gdbm_fetch( GNUTLS_DBF, key);
433 ret.data = content.dptr;
434 ret.size = content.dsize;
435 } else
436 #endif
437 if (state->gnutls_internals.db_retrieve_func!=NULL)
438 ret = state->gnutls_internals.db_retrieve_func( state->gnutls_internals.db_ptr, session_id);
440 return ret;
444 /* Removes session data from the db backend.
446 int _gnutls_remove_session( GNUTLS_STATE state, gnutls_datum session_id)
448 #ifdef HAVE_LIBGDBM
449 GDBM_FILE dbf;
450 datum key = { session_id.data, session_id.size };
451 #endif
452 int ret = 0;
454 if (GNUTLS_DBNAME==NULL && _gnutls_db_func_is_ok(state)!=0) {
455 return GNUTLS_E_DB_ERROR;
458 if (session_id.data==NULL || session_id.size==0)
459 return GNUTLS_E_INVALID_SESSION;
461 /* if we can't read why bother writing? */
462 #ifdef HAVE_LIBGDBM
463 if (GNUTLS_DBF!=NULL) { /* use gdbm */
465 dbf = gdbm_open(GNUTLS_DBNAME, 0, GDBM_WRITER, 0600, NULL);
466 if (dbf==NULL) {
467 /* cannot open db for writing. This may happen if multiple
468 * instances try to write.
470 return GNUTLS_E_AGAIN;
472 ret = gdbm_delete( dbf, key);
474 gdbm_close(dbf);
475 } else
476 #endif
477 if (state->gnutls_internals.db_remove_func!=NULL)
478 ret = state->gnutls_internals.db_remove_func( state->gnutls_internals.db_ptr, session_id);
481 return (ret == 0 ? ret : GNUTLS_E_DB_ERROR);
486 * gnutls_db_remove_session - This function will remove the current session data from the db
487 * @state: is a &GNUTLS_STATE structure.
489 * This function will remove the current session data from the session
490 * database. This will prevent future handshakes reusing these session
491 * data. This function should be called if a session was terminated
492 * abnormaly.
495 void gnutls_db_remove_session(GNUTLS_STATE state) {
496 /* if the session has failed abnormally it has
497 * to be removed from the db
499 _gnutls_db_remove_session( state, state->security_parameters.session_id, state->security_parameters.session_id_size);