Implemented a TLS session cache
This commit is contained in:
parent
703c0db894
commit
ca5767621b
|
@ -11,6 +11,7 @@ libtls_la_SOURCES = \
|
|||
tls_prf.h tls_prf.c \
|
||||
tls_socket.h tls_socket.c \
|
||||
tls_eap.h tls_eap.c \
|
||||
tls_cache.h tls_cache.c \
|
||||
tls_peer.h tls_peer.c \
|
||||
tls_server.h tls_server.c \
|
||||
tls_handshake.h tls_application.h tls.h tls.c
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Martin Willi
|
||||
* Copyright (C) 2011 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "tls_cache.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <utils/linked_list.h>
|
||||
#include <utils/hashtable.h>
|
||||
#include <threading/rwlock.h>
|
||||
|
||||
typedef struct private_tls_cache_t private_tls_cache_t;
|
||||
|
||||
/**
|
||||
* Private data of an tls_cache_t object.
|
||||
*/
|
||||
struct private_tls_cache_t {
|
||||
|
||||
/**
|
||||
* Public tls_cache_t interface.
|
||||
*/
|
||||
tls_cache_t public;
|
||||
|
||||
/**
|
||||
* Mapping session => entry_t, fast lookup by session
|
||||
*/
|
||||
hashtable_t *table;
|
||||
|
||||
/**
|
||||
* List containing all entries
|
||||
*/
|
||||
linked_list_t *list;
|
||||
|
||||
/**
|
||||
* Lock to list and table
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
|
||||
/**
|
||||
* Session limit
|
||||
*/
|
||||
u_int max_sessions;
|
||||
|
||||
/**
|
||||
* maximum age of a session, in seconds
|
||||
*/
|
||||
u_int max_age;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hashtable entry
|
||||
*/
|
||||
typedef struct {
|
||||
/** session identifier */
|
||||
chunk_t session;
|
||||
/** master secret */
|
||||
chunk_t master;
|
||||
/** TLS cipher suite */
|
||||
tls_cipher_suite_t suite;
|
||||
/** optional identity this entry is bound to */
|
||||
identification_t *id;
|
||||
/** time of add */
|
||||
time_t t;
|
||||
} entry_t;
|
||||
|
||||
/**
|
||||
* Destroy an entry
|
||||
*/
|
||||
static void entry_destroy(entry_t *entry)
|
||||
{
|
||||
chunk_clear(&entry->session);
|
||||
chunk_clear(&entry->master);
|
||||
DESTROY_IF(entry->id);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashtable hash function
|
||||
*/
|
||||
static u_int hash(chunk_t *key)
|
||||
{
|
||||
return chunk_hash(*key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashtable equals function
|
||||
*/
|
||||
static bool equals(chunk_t *a, chunk_t *b)
|
||||
{
|
||||
return chunk_equals(*a, *b);
|
||||
}
|
||||
|
||||
METHOD(tls_cache_t, create_, void,
|
||||
private_tls_cache_t *this, chunk_t session, identification_t *id,
|
||||
chunk_t master, tls_cipher_suite_t suite)
|
||||
{
|
||||
entry_t *entry;
|
||||
|
||||
INIT(entry,
|
||||
.session = chunk_clone(session),
|
||||
.master = chunk_clone(master),
|
||||
.suite = suite,
|
||||
.id = id ? id->clone(id) : NULL,
|
||||
.t = time_monotonic(NULL),
|
||||
);
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
this->list->insert_first(this->list, entry);
|
||||
this->table->put(this->table, &entry->session, entry);
|
||||
if (this->list->get_count(this->list) > this->max_sessions &&
|
||||
this->list->remove_last(this->list, (void**)&entry) == SUCCESS)
|
||||
{
|
||||
DBG2(DBG_TLS, "session limit of %u reached, deleting %#B",
|
||||
this->max_sessions, &entry->session);
|
||||
this->table->remove(this->table, &entry->session);
|
||||
entry_destroy(entry);
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
DBG2(DBG_TLS, "created TLS session %#B, %d sessions",
|
||||
&session, this->list->get_count(this->list));
|
||||
}
|
||||
|
||||
METHOD(tls_cache_t, lookup, tls_cipher_suite_t,
|
||||
private_tls_cache_t *this, chunk_t session, identification_t *id,
|
||||
chunk_t* master)
|
||||
{
|
||||
tls_cipher_suite_t suite = 0;
|
||||
entry_t *entry;
|
||||
time_t now;
|
||||
u_int age;
|
||||
|
||||
now = time_monotonic(NULL);
|
||||
|
||||
this->lock->write_lock(this->lock);
|
||||
entry = this->table->get(this->table, &session);
|
||||
if (entry)
|
||||
{
|
||||
age = now - entry->t;
|
||||
if (age <= this->max_age)
|
||||
{
|
||||
if (!id || !entry->id || id->equals(id, entry->id))
|
||||
{
|
||||
*master = chunk_clone(entry->master);
|
||||
suite = entry->suite;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG2(DBG_TLS, "TLS session %#B expired: %u seconds", &session, age);
|
||||
}
|
||||
}
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
if (suite)
|
||||
{
|
||||
DBG2(DBG_TLS, "resuming TLS session %#B, age %u seconds", &session, age);
|
||||
}
|
||||
return suite;
|
||||
}
|
||||
|
||||
METHOD(tls_cache_t, check, chunk_t,
|
||||
private_tls_cache_t *this, identification_t *id)
|
||||
{
|
||||
chunk_t session = chunk_empty;
|
||||
enumerator_t *enumerator;
|
||||
entry_t *entry;
|
||||
time_t now;
|
||||
|
||||
now = time_monotonic(NULL);
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->list->create_enumerator(this->list);
|
||||
while (enumerator->enumerate(enumerator, &entry))
|
||||
{
|
||||
if (entry->t + this->max_age >= now &&
|
||||
entry->id && id->equals(id, entry->id))
|
||||
{
|
||||
session = chunk_clone(entry->session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
METHOD(tls_cache_t, destroy, void,
|
||||
private_tls_cache_t *this)
|
||||
{
|
||||
entry_t *entry;
|
||||
|
||||
while (this->list->remove_last(this->list, (void**)&entry) == SUCCESS)
|
||||
{
|
||||
entry_destroy(entry);
|
||||
}
|
||||
this->list->destroy(this->list);
|
||||
this->table->destroy(this->table);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
tls_cache_t *tls_cache_create(u_int max_sessions, u_int max_age)
|
||||
{
|
||||
private_tls_cache_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.create = _create_,
|
||||
.lookup = _lookup,
|
||||
.check = _check,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.table = hashtable_create((hashtable_hash_t)hash,
|
||||
(hashtable_equals_t)equals, 8),
|
||||
.list = linked_list_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
.max_sessions = max_sessions,
|
||||
.max_age = max_age,
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Martin Willi
|
||||
* Copyright (C) 2011 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tls_cache tls_cache
|
||||
* @{ @ingroup libtls
|
||||
*/
|
||||
|
||||
#ifndef TLS_CACHE_H_
|
||||
#define TLS_CACHE_H_
|
||||
|
||||
typedef struct tls_cache_t tls_cache_t;
|
||||
|
||||
#include "tls_crypto.h"
|
||||
|
||||
/**
|
||||
* TLS session cache facility.
|
||||
*/
|
||||
struct tls_cache_t {
|
||||
|
||||
/**
|
||||
* Create a new TLS session entry.
|
||||
*
|
||||
* @param session session identifier
|
||||
* @param id identity the session is bound to
|
||||
* @param master TLS master secret
|
||||
* @param suite TLS cipher suite of the session
|
||||
*/
|
||||
void (*create)(tls_cache_t *this, chunk_t session, identification_t *id,
|
||||
chunk_t master, tls_cipher_suite_t suite);
|
||||
|
||||
/**
|
||||
* Look up a TLS session entry.
|
||||
*
|
||||
* @param session session ID to find
|
||||
* @param id identity the session is bound to
|
||||
* @param master gets allocated master secret, if session found
|
||||
* @return TLS suite of session, 0 if none found
|
||||
*/
|
||||
tls_cipher_suite_t (*lookup)(tls_cache_t *this, chunk_t session,
|
||||
identification_t *id, chunk_t* master);
|
||||
|
||||
/**
|
||||
* Check if we have a session for a given identity.
|
||||
*
|
||||
* @param id identity to check
|
||||
* @return allocated session ID, or chunk_empty
|
||||
*/
|
||||
chunk_t (*check)(tls_cache_t *this, identification_t *id);
|
||||
|
||||
/**
|
||||
* Destroy a tls_cache_t.
|
||||
*/
|
||||
void (*destroy)(tls_cache_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tls_cache instance.
|
||||
*
|
||||
* @param max_sessions maximum number of sessions to store
|
||||
* @param max_age maximum age of a session, in seconds
|
||||
* @return tls cache
|
||||
*/
|
||||
tls_cache_t *tls_cache_create(u_int max_sessions, u_int max_age);
|
||||
|
||||
#endif /** TLS_CACHE_H_ @}*/
|
Loading…
Reference in New Issue