Implemented a TLS session cache

This commit is contained in:
Martin Willi 2011-12-31 01:39:17 +01:00
parent 703c0db894
commit ca5767621b
3 changed files with 316 additions and 0 deletions

View File

@ -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

237
src/libtls/tls_cache.c Normal file
View File

@ -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;
}

78
src/libtls/tls_cache.h Normal file
View File

@ -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_ @}*/