summaryrefslogtreecommitdiff
path: root/src/libtls/tls_cache.c
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
committerYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
commitb34738ed08c2227300d554b139e2495ca5da97d6 (patch)
tree62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libtls/tls_cache.c
parent0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff)
downloadvyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz
vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libtls/tls_cache.c')
-rw-r--r--src/libtls/tls_cache.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/libtls/tls_cache.c b/src/libtls/tls_cache.c
new file mode 100644
index 000000000..a89201ad7
--- /dev/null
+++ b/src/libtls/tls_cache.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;
+}