v2 / thirdparty / mbedtls / library / ssl_cache.c
410 lines · 336 sloc · 10.56 KB · 1274cdc3447be8e83616e8512872455e8720c2fd
Raw
1/*
2 * SSL session cache implementation
3 *
4 * Copyright The Mbed TLS Contributors
5 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6 */
7/*
8 * These session callbacks use a simple chained list
9 * to store and retrieve the session information.
10 */
11
12#include "common.h"
13
14#if defined(MBEDTLS_SSL_CACHE_C)
15
16#include "mbedtls/platform.h"
17
18#include "mbedtls/ssl_cache.h"
19#include "ssl_misc.h"
20#include "mbedtls/error.h"
21
22#include <string.h>
23
24void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache)
25{
26 memset(cache, 0, sizeof(mbedtls_ssl_cache_context));
27
28 cache->timeout = MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT;
29 cache->max_entries = MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES;
30
31#if defined(MBEDTLS_THREADING_C)
32 mbedtls_mutex_init(&cache->mutex);
33#endif
34}
35
36MBEDTLS_CHECK_RETURN_CRITICAL
37static int ssl_cache_find_entry(mbedtls_ssl_cache_context *cache,
38 unsigned char const *session_id,
39 size_t session_id_len,
40 mbedtls_ssl_cache_entry **dst)
41{
42 int ret = MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND;
43#if defined(MBEDTLS_HAVE_TIME)
44 mbedtls_time_t t = mbedtls_time(NULL);
45#endif
46 mbedtls_ssl_cache_entry *cur;
47
48 for (cur = cache->chain; cur != NULL; cur = cur->next) {
49#if defined(MBEDTLS_HAVE_TIME)
50 if (cache->timeout != 0 &&
51 (int) (t - cur->timestamp) > cache->timeout) {
52 continue;
53 }
54#endif
55
56 if (session_id_len != cur->session_id_len ||
57 memcmp(session_id, cur->session_id,
58 cur->session_id_len) != 0) {
59 continue;
60 }
61
62 break;
63 }
64
65 if (cur != NULL) {
66 *dst = cur;
67 ret = 0;
68 }
69
70 return ret;
71}
72
73
74int mbedtls_ssl_cache_get(void *data,
75 unsigned char const *session_id,
76 size_t session_id_len,
77 mbedtls_ssl_session *session)
78{
79 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
80 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
81 mbedtls_ssl_cache_entry *entry;
82
83#if defined(MBEDTLS_THREADING_C)
84 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
85 return ret;
86 }
87#endif
88
89 ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry);
90 if (ret != 0) {
91 goto exit;
92 }
93
94 ret = mbedtls_ssl_session_load(session,
95 entry->session,
96 entry->session_len);
97 if (ret != 0) {
98 goto exit;
99 }
100
101 ret = 0;
102
103exit:
104#if defined(MBEDTLS_THREADING_C)
105 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
106 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
107 }
108#endif
109
110 return ret;
111}
112
113/* zeroize a cache entry */
114static void ssl_cache_entry_zeroize(mbedtls_ssl_cache_entry *entry)
115{
116 if (entry == NULL) {
117 return;
118 }
119
120 /* zeroize and free session structure */
121 if (entry->session != NULL) {
122 mbedtls_zeroize_and_free(entry->session, entry->session_len);
123 }
124
125 /* zeroize the whole entry structure */
126 mbedtls_platform_zeroize(entry, sizeof(mbedtls_ssl_cache_entry));
127}
128
129MBEDTLS_CHECK_RETURN_CRITICAL
130static int ssl_cache_pick_writing_slot(mbedtls_ssl_cache_context *cache,
131 unsigned char const *session_id,
132 size_t session_id_len,
133 mbedtls_ssl_cache_entry **dst)
134{
135#if defined(MBEDTLS_HAVE_TIME)
136 mbedtls_time_t t = mbedtls_time(NULL), oldest = 0;
137#endif /* MBEDTLS_HAVE_TIME */
138
139 mbedtls_ssl_cache_entry *old = NULL;
140 int count = 0;
141 mbedtls_ssl_cache_entry *cur, *last;
142
143 /* Check 1: Is there already an entry with the given session ID?
144 *
145 * If yes, overwrite it.
146 *
147 * If not, `count` will hold the size of the session cache
148 * at the end of this loop, and `last` will point to the last
149 * entry, both of which will be used later. */
150
151 last = NULL;
152 for (cur = cache->chain; cur != NULL; cur = cur->next) {
153 count++;
154 if (session_id_len == cur->session_id_len &&
155 memcmp(session_id, cur->session_id, cur->session_id_len) == 0) {
156 goto found;
157 }
158 last = cur;
159 }
160
161 /* Check 2: Is there an outdated entry in the cache?
162 *
163 * If so, overwrite it.
164 *
165 * If not, remember the oldest entry in `old` for later.
166 */
167
168#if defined(MBEDTLS_HAVE_TIME)
169 for (cur = cache->chain; cur != NULL; cur = cur->next) {
170 if (cache->timeout != 0 &&
171 (int) (t - cur->timestamp) > cache->timeout) {
172 goto found;
173 }
174
175 if (oldest == 0 || cur->timestamp < oldest) {
176 oldest = cur->timestamp;
177 old = cur;
178 }
179 }
180#endif /* MBEDTLS_HAVE_TIME */
181
182 /* Check 3: Is there free space in the cache? */
183
184 if (count < cache->max_entries) {
185 /* Create new entry */
186 cur = mbedtls_calloc(1, sizeof(mbedtls_ssl_cache_entry));
187 if (cur == NULL) {
188 return MBEDTLS_ERR_SSL_ALLOC_FAILED;
189 }
190
191 /* Append to the end of the linked list. */
192 if (last == NULL) {
193 cache->chain = cur;
194 } else {
195 last->next = cur;
196 }
197
198 goto found;
199 }
200
201 /* Last resort: The cache is full and doesn't contain any outdated
202 * elements. In this case, we evict the oldest one, judged by timestamp
203 * (if present) or cache-order. */
204
205#if defined(MBEDTLS_HAVE_TIME)
206 if (old == NULL) {
207 /* This should only happen on an ill-configured cache
208 * with max_entries == 0. */
209 return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
210 }
211#else /* MBEDTLS_HAVE_TIME */
212 /* Reuse first entry in chain, but move to last place. */
213 if (cache->chain == NULL) {
214 /* This should never happen */
215 return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
216 }
217
218 old = cache->chain;
219 cache->chain = old->next;
220 old->next = NULL;
221 last->next = old;
222#endif /* MBEDTLS_HAVE_TIME */
223
224 /* Now `old` points to the oldest entry to be overwritten. */
225 cur = old;
226
227found:
228
229 /* If we're reusing an entry, free it first. */
230 if (cur->session != NULL) {
231 /* `ssl_cache_entry_zeroize` would break the chain,
232 * so we reuse `old` to record `next` temporarily. */
233 old = cur->next;
234 ssl_cache_entry_zeroize(cur);
235 cur->next = old;
236 }
237
238#if defined(MBEDTLS_HAVE_TIME)
239 cur->timestamp = t;
240#endif
241
242 *dst = cur;
243 return 0;
244}
245
246int mbedtls_ssl_cache_set(void *data,
247 unsigned char const *session_id,
248 size_t session_id_len,
249 const mbedtls_ssl_session *session)
250{
251 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
252 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
253 mbedtls_ssl_cache_entry *cur;
254
255 size_t session_serialized_len = 0;
256 unsigned char *session_serialized = NULL;
257
258#if defined(MBEDTLS_THREADING_C)
259 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
260 return ret;
261 }
262#endif
263
264 ret = ssl_cache_pick_writing_slot(cache,
265 session_id, session_id_len,
266 &cur);
267 if (ret != 0) {
268 goto exit;
269 }
270
271 /* Check how much space we need to serialize the session
272 * and allocate a sufficiently large buffer. */
273 ret = mbedtls_ssl_session_save(session, NULL, 0, &session_serialized_len);
274 if (ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) {
275 goto exit;
276 }
277
278 session_serialized = mbedtls_calloc(1, session_serialized_len);
279 if (session_serialized == NULL) {
280 ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
281 goto exit;
282 }
283
284 /* Now serialize the session into the allocated buffer. */
285 ret = mbedtls_ssl_session_save(session,
286 session_serialized,
287 session_serialized_len,
288 &session_serialized_len);
289 if (ret != 0) {
290 goto exit;
291 }
292
293 if (session_id_len > sizeof(cur->session_id)) {
294 ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
295 goto exit;
296 }
297 cur->session_id_len = session_id_len;
298 memcpy(cur->session_id, session_id, session_id_len);
299
300 cur->session = session_serialized;
301 cur->session_len = session_serialized_len;
302 session_serialized = NULL;
303
304 ret = 0;
305
306exit:
307#if defined(MBEDTLS_THREADING_C)
308 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
309 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
310 }
311#endif
312
313 if (session_serialized != NULL) {
314 mbedtls_zeroize_and_free(session_serialized, session_serialized_len);
315 session_serialized = NULL;
316 }
317
318 return ret;
319}
320
321int mbedtls_ssl_cache_remove(void *data,
322 unsigned char const *session_id,
323 size_t session_id_len)
324{
325 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
326 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
327 mbedtls_ssl_cache_entry *entry;
328 mbedtls_ssl_cache_entry *prev;
329
330#if defined(MBEDTLS_THREADING_C)
331 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
332 return ret;
333 }
334#endif
335
336 ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry);
337 /* No valid entry found, exit with success */
338 if (ret != 0) {
339 ret = 0;
340 goto exit;
341 }
342
343 /* Now we remove the entry from the chain */
344 if (entry == cache->chain) {
345 cache->chain = entry->next;
346 goto free;
347 }
348 for (prev = cache->chain; prev->next != NULL; prev = prev->next) {
349 if (prev->next == entry) {
350 prev->next = entry->next;
351 break;
352 }
353 }
354
355free:
356 ssl_cache_entry_zeroize(entry);
357 mbedtls_free(entry);
358 ret = 0;
359
360exit:
361#if defined(MBEDTLS_THREADING_C)
362 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
363 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
364 }
365#endif
366
367 return ret;
368}
369
370#if defined(MBEDTLS_HAVE_TIME)
371void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout)
372{
373 if (timeout < 0) {
374 timeout = 0;
375 }
376
377 cache->timeout = timeout;
378}
379#endif /* MBEDTLS_HAVE_TIME */
380
381void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max)
382{
383 if (max < 0) {
384 max = 0;
385 }
386
387 cache->max_entries = max;
388}
389
390void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache)
391{
392 mbedtls_ssl_cache_entry *cur, *prv;
393
394 cur = cache->chain;
395
396 while (cur != NULL) {
397 prv = cur;
398 cur = cur->next;
399
400 ssl_cache_entry_zeroize(prv);
401 mbedtls_free(prv);
402 }
403
404#if defined(MBEDTLS_THREADING_C)
405 mbedtls_mutex_free(&cache->mutex);
406#endif
407 cache->chain = NULL;
408}
409
410#endif /* MBEDTLS_SSL_CACHE_C */
411