Moved debug.[ch] to utils folder
[strongswan.git] / src / libtls / tls_cache.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tls_cache.h"
17
18 #include <utils/debug.h>
19 #include <collections/linked_list.h>
20 #include <collections/hashtable.h>
21 #include <threading/rwlock.h>
22
23 typedef struct private_tls_cache_t private_tls_cache_t;
24
25 /**
26 * Private data of an tls_cache_t object.
27 */
28 struct private_tls_cache_t {
29
30 /**
31 * Public tls_cache_t interface.
32 */
33 tls_cache_t public;
34
35 /**
36 * Mapping session => entry_t, fast lookup by session
37 */
38 hashtable_t *table;
39
40 /**
41 * List containing all entries
42 */
43 linked_list_t *list;
44
45 /**
46 * Lock to list and table
47 */
48 rwlock_t *lock;
49
50 /**
51 * Session limit
52 */
53 u_int max_sessions;
54
55 /**
56 * maximum age of a session, in seconds
57 */
58 u_int max_age;
59 };
60
61 /**
62 * Hashtable entry
63 */
64 typedef struct {
65 /** session identifier */
66 chunk_t session;
67 /** master secret */
68 chunk_t master;
69 /** TLS cipher suite */
70 tls_cipher_suite_t suite;
71 /** optional identity this entry is bound to */
72 identification_t *id;
73 /** time of add */
74 time_t t;
75 } entry_t;
76
77 /**
78 * Destroy an entry
79 */
80 static void entry_destroy(entry_t *entry)
81 {
82 chunk_clear(&entry->session);
83 chunk_clear(&entry->master);
84 DESTROY_IF(entry->id);
85 free(entry);
86 }
87
88 /**
89 * Hashtable hash function
90 */
91 static u_int hash(chunk_t *key)
92 {
93 return chunk_hash(*key);
94 }
95
96 /**
97 * Hashtable equals function
98 */
99 static bool equals(chunk_t *a, chunk_t *b)
100 {
101 return chunk_equals(*a, *b);
102 }
103
104 METHOD(tls_cache_t, create_, void,
105 private_tls_cache_t *this, chunk_t session, identification_t *id,
106 chunk_t master, tls_cipher_suite_t suite)
107 {
108 entry_t *entry;
109
110 INIT(entry,
111 .session = chunk_clone(session),
112 .master = chunk_clone(master),
113 .suite = suite,
114 .id = id ? id->clone(id) : NULL,
115 .t = time_monotonic(NULL),
116 );
117
118 this->lock->write_lock(this->lock);
119 this->list->insert_first(this->list, entry);
120 this->table->put(this->table, &entry->session, entry);
121 if (this->list->get_count(this->list) > this->max_sessions &&
122 this->list->remove_last(this->list, (void**)&entry) == SUCCESS)
123 {
124 DBG2(DBG_TLS, "session limit of %u reached, deleting %#B",
125 this->max_sessions, &entry->session);
126 this->table->remove(this->table, &entry->session);
127 entry_destroy(entry);
128 }
129 this->lock->unlock(this->lock);
130
131 DBG2(DBG_TLS, "created TLS session %#B, %d sessions",
132 &session, this->list->get_count(this->list));
133 }
134
135 METHOD(tls_cache_t, lookup, tls_cipher_suite_t,
136 private_tls_cache_t *this, chunk_t session, identification_t *id,
137 chunk_t* master)
138 {
139 tls_cipher_suite_t suite = 0;
140 entry_t *entry;
141 time_t now;
142 u_int age;
143
144 now = time_monotonic(NULL);
145
146 this->lock->write_lock(this->lock);
147 entry = this->table->get(this->table, &session);
148 if (entry)
149 {
150 age = now - entry->t;
151 if (age <= this->max_age)
152 {
153 if (!id || !entry->id || id->equals(id, entry->id))
154 {
155 *master = chunk_clone(entry->master);
156 suite = entry->suite;
157 }
158 }
159 else
160 {
161 DBG2(DBG_TLS, "TLS session %#B expired: %u seconds", &session, age);
162 }
163 }
164 this->lock->unlock(this->lock);
165
166 if (suite)
167 {
168 DBG2(DBG_TLS, "resuming TLS session %#B, age %u seconds", &session, age);
169 }
170 return suite;
171 }
172
173 METHOD(tls_cache_t, check, chunk_t,
174 private_tls_cache_t *this, identification_t *id)
175 {
176 chunk_t session = chunk_empty;
177 enumerator_t *enumerator;
178 entry_t *entry;
179 time_t now;
180
181 now = time_monotonic(NULL);
182 this->lock->read_lock(this->lock);
183 enumerator = this->list->create_enumerator(this->list);
184 while (enumerator->enumerate(enumerator, &entry))
185 {
186 if (entry->t + this->max_age >= now &&
187 entry->id && id->equals(id, entry->id))
188 {
189 session = chunk_clone(entry->session);
190 break;
191 }
192 }
193 enumerator->destroy(enumerator);
194 this->lock->unlock(this->lock);
195
196 return session;
197 }
198
199 METHOD(tls_cache_t, destroy, void,
200 private_tls_cache_t *this)
201 {
202 entry_t *entry;
203
204 while (this->list->remove_last(this->list, (void**)&entry) == SUCCESS)
205 {
206 entry_destroy(entry);
207 }
208 this->list->destroy(this->list);
209 this->table->destroy(this->table);
210 this->lock->destroy(this->lock);
211 free(this);
212 }
213
214 /**
215 * See header
216 */
217 tls_cache_t *tls_cache_create(u_int max_sessions, u_int max_age)
218 {
219 private_tls_cache_t *this;
220
221 INIT(this,
222 .public = {
223 .create = _create_,
224 .lookup = _lookup,
225 .check = _check,
226 .destroy = _destroy,
227 },
228 .table = hashtable_create((hashtable_hash_t)hash,
229 (hashtable_equals_t)equals, 8),
230 .list = linked_list_create(),
231 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
232 .max_sessions = max_sessions,
233 .max_age = max_age,
234 );
235
236 return &this->public;
237 }