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>
30 #include <gnutls_session_pack.h>
32 #define GNUTLS_DBNAME state->gnutls_internals.db_name
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);
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()
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
;
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()
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
;
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()
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
;
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
) {
149 if (filename
==NULL
) return 0;
151 /* deallocate previous name */
152 if (GNUTLS_DBNAME
!=NULL
)
153 gnutls_free(GNUTLS_DBNAME
);
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
165 dbf
= gdbm_open( (char*)filename
, 0, GDBM_WRCREAT
, 0600, NULL
);
166 if (dbf
==NULL
) return GNUTLS_E_DB_ERROR
;
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
;
177 return GNUTLS_E_UNIMPLEMENTED_FEATURE
;
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
193 int gnutls_db_check_entry( GNUTLS_STATE state
, gnutls_datum session_entry
) {
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
;
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
) {
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
);
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
);
242 key
= gdbm_nextkey(dbf
, key
);
244 ret
= gdbm_reorganize(dbf
);
249 if (ret
!=0) return GNUTLS_E_DB_ERROR
;
253 return GNUTLS_E_UNIMPLEMENTED_FEATURE
;
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
;
267 if (state
->gnutls_internals
.resumable
==RESUME_FALSE
) {
269 return GNUTLS_E_INVALID_SESSION
;
272 if (state
->security_parameters
.session_id
==NULL
|| state
->security_parameters
.session_id_size
==0) {
274 return GNUTLS_E_INVALID_SESSION
;
277 /* allocate space for data */
278 content
.size
= _gnutls_session_size( state
);
279 if (content
.size
< 0) {
284 content
.data
= gnutls_malloc( content
.size
);
285 if (content
.data
==NULL
) {
287 return GNUTLS_E_MEMORY_ERROR
;
291 ret
= _gnutls_session_pack( state
, &content
);
293 gnutls_free( content
.data
);
298 ret
= _gnutls_store_session( state
, key
, content
);
300 gnutls_free( content
.data
);
305 /* Checks if both db_store and db_retrieve functions have
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
)
319 gnutls_datum key
= { session_id
, session_id_size
};
322 if (GNUTLS_DBNAME
==NULL
&& _gnutls_db_func_is_ok(state
)!=0) {
324 return GNUTLS_E_INVALID_SESSION
;
327 data
= _gnutls_retrieve_session( state
, key
);
329 if (data
.data
==NULL
) {
331 return GNUTLS_E_INVALID_SESSION
;
334 /* expiration check is performed inside */
335 ret
= gnutls_session_set_data( state
, data
.data
, data
.size
);
340 /* Note: Data is not allocated with gnutls_malloc
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
)
361 datum key
= { session_id
.data
, session_id
.size
};
362 datum content
= {session_data
.data
, session_data
.size
};
366 if (state
->gnutls_internals
.resumable
==RESUME_FALSE
) {
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) {
377 return GNUTLS_E_INVALID_SESSION
;
380 if (session_data
.data
==NULL
|| session_data
.size
==0) {
382 return GNUTLS_E_INVALID_SESSION
;
384 /* if we can't read why bother writing? */
387 if (GNUTLS_DBF
!=NULL
) { /* use gdbm */
388 dbf
= gdbm_open(GNUTLS_DBNAME
, 0, GDBM_WRITER
, 0600, NULL
);
390 /* cannot open db for writing. This may happen if multiple
391 * instances try to write.
394 return GNUTLS_E_AGAIN
;
396 ret
= gdbm_store( dbf
, key
, content
, GDBM_INSERT
);
402 return 0; /*GNUTLS_E_UNIMPLEMENTED_FEATURE;*/
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
)
419 datum key
= { session_id
.data
, session_id
.size
};
422 gnutls_datum ret
= { NULL
, 0 };
424 if (session_id
.data
==NULL
|| session_id
.size
==0) {
429 /* if we can't read why bother writing? */
431 if (GNUTLS_DBF
!=NULL
) { /* use gdbm */
432 content
= gdbm_fetch( GNUTLS_DBF
, key
);
433 ret
.data
= content
.dptr
;
434 ret
.size
= content
.dsize
;
437 if (state
->gnutls_internals
.db_retrieve_func
!=NULL
)
438 ret
= state
->gnutls_internals
.db_retrieve_func( state
->gnutls_internals
.db_ptr
, session_id
);
444 /* Removes session data from the db backend.
446 int _gnutls_remove_session( GNUTLS_STATE state
, gnutls_datum session_id
)
450 datum key
= { session_id
.data
, session_id
.size
};
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? */
463 if (GNUTLS_DBF
!=NULL
) { /* use gdbm */
465 dbf
= gdbm_open(GNUTLS_DBNAME
, 0, GDBM_WRITER
, 0600, 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
);
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
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
);