blob: 772cb8fdfe6ddbbfe4f78f5b4b3ed69f6b1e33c7 [file] [log] [blame]
Jens Wiklander817466c2018-05-22 13:49:31 +02001/*
2 * SSL session cache implementation
3 *
Jerome Forissier79013242021-07-28 10:24:04 +02004 * Copyright The Mbed TLS Contributors
Tom Van Eyckc1633172024-04-09 18:44:13 +02005 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
Jens Wiklander817466c2018-05-22 13:49:31 +02006 */
7/*
8 * These session callbacks use a simple chained list
9 * to store and retrieve the session information.
10 */
11
Jerome Forissier79013242021-07-28 10:24:04 +020012#include "common.h"
Jens Wiklander817466c2018-05-22 13:49:31 +020013
14#if defined(MBEDTLS_SSL_CACHE_C)
15
Jens Wiklander817466c2018-05-22 13:49:31 +020016#include "mbedtls/platform.h"
Jens Wiklander817466c2018-05-22 13:49:31 +020017
18#include "mbedtls/ssl_cache.h"
Jens Wiklander32b31802023-10-06 16:59:46 +020019#include "ssl_misc.h"
Tom Van Eyckc1633172024-04-09 18:44:13 +020020#include "mbedtls/error.h"
Jens Wiklander817466c2018-05-22 13:49:31 +020021
22#include <string.h>
23
Jens Wiklander32b31802023-10-06 16:59:46 +020024void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache)
Jens Wiklander817466c2018-05-22 13:49:31 +020025{
Jens Wiklander32b31802023-10-06 16:59:46 +020026 memset(cache, 0, sizeof(mbedtls_ssl_cache_context));
Jens Wiklander817466c2018-05-22 13:49:31 +020027
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)
Jens Wiklander32b31802023-10-06 16:59:46 +020032 mbedtls_mutex_init(&cache->mutex);
Jens Wiklander817466c2018-05-22 13:49:31 +020033#endif
34}
35
Jens Wiklander32b31802023-10-06 16:59:46 +020036MBEDTLS_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)
Jens Wiklander817466c2018-05-22 13:49:31 +020041{
Tom Van Eyckc1633172024-04-09 18:44:13 +020042 int ret = MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND;
Jens Wiklander817466c2018-05-22 13:49:31 +020043#if defined(MBEDTLS_HAVE_TIME)
Jens Wiklander32b31802023-10-06 16:59:46 +020044 mbedtls_time_t t = mbedtls_time(NULL);
Jens Wiklander817466c2018-05-22 13:49:31 +020045#endif
Jens Wiklander32b31802023-10-06 16:59:46 +020046 mbedtls_ssl_cache_entry *cur;
Jens Wiklander817466c2018-05-22 13:49:31 +020047
Jens Wiklander32b31802023-10-06 16:59:46 +020048 for (cur = cache->chain; cur != NULL; cur = cur->next) {
Jens Wiklander817466c2018-05-22 13:49:31 +020049#if defined(MBEDTLS_HAVE_TIME)
Jens Wiklander32b31802023-10-06 16:59:46 +020050 if (cache->timeout != 0 &&
51 (int) (t - cur->timestamp) > cache->timeout) {
Jens Wiklander817466c2018-05-22 13:49:31 +020052 continue;
Jens Wiklander32b31802023-10-06 16:59:46 +020053 }
Jens Wiklander817466c2018-05-22 13:49:31 +020054#endif
55
Jens Wiklander32b31802023-10-06 16:59:46 +020056 if (session_id_len != cur->session_id_len ||
57 memcmp(session_id, cur->session_id,
58 cur->session_id_len) != 0) {
Jens Wiklander817466c2018-05-22 13:49:31 +020059 continue;
Jerome Forissier79013242021-07-28 10:24:04 +020060 }
Jens Wiklander817466c2018-05-22 13:49:31 +020061
Jens Wiklander32b31802023-10-06 16:59:46 +020062 break;
63 }
Jens Wiklander817466c2018-05-22 13:49:31 +020064
Jens Wiklander32b31802023-10-06 16:59:46 +020065 if (cur != NULL) {
66 *dst = cur;
Jens Wiklander817466c2018-05-22 13:49:31 +020067 ret = 0;
Jens Wiklander817466c2018-05-22 13:49:31 +020068 }
69
Jens Wiklander32b31802023-10-06 16:59:46 +020070 return ret;
Jens Wiklander817466c2018-05-22 13:49:31 +020071}
72
Jens Wiklander32b31802023-10-06 16:59:46 +020073
74int mbedtls_ssl_cache_get(void *data,
75 unsigned char const *session_id,
76 size_t session_id_len,
77 mbedtls_ssl_session *session)
Jens Wiklander817466c2018-05-22 13:49:31 +020078{
Tom Van Eyckc1633172024-04-09 18:44:13 +020079 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Jens Wiklander817466c2018-05-22 13:49:31 +020080 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
Jens Wiklander32b31802023-10-06 16:59:46 +020081 mbedtls_ssl_cache_entry *entry;
Jens Wiklander817466c2018-05-22 13:49:31 +020082
83#if defined(MBEDTLS_THREADING_C)
Jens Wiklander32b31802023-10-06 16:59:46 +020084 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
85 return ret;
Jens Wiklander817466c2018-05-22 13:49:31 +020086 }
Jens Wiklander817466c2018-05-22 13:49:31 +020087#endif
Jens Wiklander817466c2018-05-22 13:49:31 +020088
Jens Wiklander32b31802023-10-06 16:59:46 +020089 ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry);
90 if (ret != 0) {
Jerome Forissier11fa71b2020-04-20 17:17:56 +020091 goto exit;
92 }
93
Jens Wiklander32b31802023-10-06 16:59:46 +020094 ret = mbedtls_ssl_session_load(session,
95 entry->session,
96 entry->session_len);
97 if (ret != 0) {
98 goto exit;
Jens Wiklander817466c2018-05-22 13:49:31 +020099 }
Jens Wiklander817466c2018-05-22 13:49:31 +0200100
101 ret = 0;
102
103exit:
104#if defined(MBEDTLS_THREADING_C)
Jens Wiklander32b31802023-10-06 16:59:46 +0200105 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
106 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
107 }
Jens Wiklander817466c2018-05-22 13:49:31 +0200108#endif
109
Jens Wiklander32b31802023-10-06 16:59:46 +0200110 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) {
Tom Van Eyckc1633172024-04-09 18:44:13 +0200122 mbedtls_zeroize_and_free(entry->session, entry->session_len);
Jens Wiklander32b31802023-10-06 16:59:46 +0200123 }
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) {
Tom Van Eyckc1633172024-04-09 18:44:13 +0200188 return MBEDTLS_ERR_SSL_ALLOC_FAILED;
Jens Wiklander32b31802023-10-06 16:59:46 +0200189 }
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. */
Tom Van Eyckc1633172024-04-09 18:44:13 +0200209 return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
Jens Wiklander32b31802023-10-06 16:59:46 +0200210 }
211#else /* MBEDTLS_HAVE_TIME */
212 /* Reuse first entry in chain, but move to last place. */
213 if (cache->chain == NULL) {
Tom Van Eyckc1633172024-04-09 18:44:13 +0200214 /* This should never happen */
215 return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Jens Wiklander32b31802023-10-06 16:59:46 +0200216 }
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{
Tom Van Eyckc1633172024-04-09 18:44:13 +0200251 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Jens Wiklander32b31802023-10-06 16:59:46 +0200252 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
253 mbedtls_ssl_cache_entry *cur;
254
Tom Van Eyckc1633172024-04-09 18:44:13 +0200255 size_t session_serialized_len = 0;
Jens Wiklander32b31802023-10-06 16:59:46 +0200256 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) {
Jens Wiklander32b31802023-10-06 16:59:46 +0200275 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)) {
Tom Van Eyckc1633172024-04-09 18:44:13 +0200294 ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
Jens Wiklander32b31802023-10-06 16:59:46 +0200295 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) {
Tom Van Eyckc1633172024-04-09 18:44:13 +0200314 mbedtls_zeroize_and_free(session_serialized, session_serialized_len);
Jens Wiklander32b31802023-10-06 16:59:46 +0200315 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{
Tom Van Eyckc1633172024-04-09 18:44:13 +0200325 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
Jens Wiklander32b31802023-10-06 16:59:46 +0200326 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;
Jens Wiklander817466c2018-05-22 13:49:31 +0200368}
369
370#if defined(MBEDTLS_HAVE_TIME)
Jens Wiklander32b31802023-10-06 16:59:46 +0200371void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout)
Jens Wiklander817466c2018-05-22 13:49:31 +0200372{
Jens Wiklander32b31802023-10-06 16:59:46 +0200373 if (timeout < 0) {
374 timeout = 0;
375 }
Jens Wiklander817466c2018-05-22 13:49:31 +0200376
377 cache->timeout = timeout;
378}
379#endif /* MBEDTLS_HAVE_TIME */
380
Jens Wiklander32b31802023-10-06 16:59:46 +0200381void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max)
Jens Wiklander817466c2018-05-22 13:49:31 +0200382{
Jens Wiklander32b31802023-10-06 16:59:46 +0200383 if (max < 0) {
384 max = 0;
385 }
Jens Wiklander817466c2018-05-22 13:49:31 +0200386
387 cache->max_entries = max;
388}
389
Jens Wiklander32b31802023-10-06 16:59:46 +0200390void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache)
Jens Wiklander817466c2018-05-22 13:49:31 +0200391{
392 mbedtls_ssl_cache_entry *cur, *prv;
393
394 cur = cache->chain;
395
Jens Wiklander32b31802023-10-06 16:59:46 +0200396 while (cur != NULL) {
Jens Wiklander817466c2018-05-22 13:49:31 +0200397 prv = cur;
398 cur = cur->next;
399
Jens Wiklander32b31802023-10-06 16:59:46 +0200400 ssl_cache_entry_zeroize(prv);
401 mbedtls_free(prv);
Jens Wiklander817466c2018-05-22 13:49:31 +0200402 }
403
404#if defined(MBEDTLS_THREADING_C)
Jens Wiklander32b31802023-10-06 16:59:46 +0200405 mbedtls_mutex_free(&cache->mutex);
Jens Wiklander817466c2018-05-22 13:49:31 +0200406#endif
Jens Wiklander3d3b0592019-03-20 15:30:29 +0100407 cache->chain = NULL;
Jens Wiklander817466c2018-05-22 13:49:31 +0200408}
409
410#endif /* MBEDTLS_SSL_CACHE_C */