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 #include "mbedtls/error.h"
18
19 #include "mbedtls/ssl_cache.h"
20 #include "mbedtls/ssl_internal.h"
21
22 #include <string.h>
23
mbedtls_ssl_cache_init(mbedtls_ssl_cache_context * cache)24 void 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
mbedtls_ssl_cache_get(void * data,mbedtls_ssl_session * session)36 int mbedtls_ssl_cache_get(void *data, mbedtls_ssl_session *session)
37 {
38 int ret = MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND;
39 #if defined(MBEDTLS_HAVE_TIME)
40 mbedtls_time_t t = mbedtls_time(NULL);
41 #endif
42 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
43 mbedtls_ssl_cache_entry *cur, *entry;
44
45 #if defined(MBEDTLS_THREADING_C)
46 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
47 return ret;
48 }
49 #endif
50
51 cur = cache->chain;
52 entry = NULL;
53
54 while (cur != NULL) {
55 entry = cur;
56 cur = cur->next;
57
58 #if defined(MBEDTLS_HAVE_TIME)
59 if (cache->timeout != 0 &&
60 (int) (t - entry->timestamp) > cache->timeout) {
61 continue;
62 }
63 #endif
64
65 if (session->id_len != entry->session.id_len ||
66 memcmp(session->id, entry->session.id,
67 entry->session.id_len) != 0) {
68 continue;
69 }
70
71 ret = mbedtls_ssl_session_copy(session, &entry->session);
72 if (ret != 0) {
73 goto exit;
74 }
75
76 #if defined(MBEDTLS_X509_CRT_PARSE_C) && \
77 defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
78 /*
79 * Restore peer certificate (without rest of the original chain)
80 */
81 if (entry->peer_cert.p != NULL) {
82 /* `session->peer_cert` is NULL after the call to
83 * mbedtls_ssl_session_copy(), because cache entries
84 * have the `peer_cert` field set to NULL. */
85
86 if ((session->peer_cert = mbedtls_calloc(1,
87 sizeof(mbedtls_x509_crt))) == NULL) {
88 ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
89 goto exit;
90 }
91
92 mbedtls_x509_crt_init(session->peer_cert);
93 if ((ret = mbedtls_x509_crt_parse(session->peer_cert, entry->peer_cert.p,
94 entry->peer_cert.len)) != 0) {
95 mbedtls_free(session->peer_cert);
96 session->peer_cert = NULL;
97 goto exit;
98 }
99 }
100 #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
101
102 ret = 0;
103 goto exit;
104 }
105
106 exit:
107 #if defined(MBEDTLS_THREADING_C)
108 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
109 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
110 }
111 #endif
112
113 return ret;
114 }
115
mbedtls_ssl_cache_set(void * data,const mbedtls_ssl_session * session)116 int mbedtls_ssl_cache_set(void *data, const mbedtls_ssl_session *session)
117 {
118 int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
119 #if defined(MBEDTLS_HAVE_TIME)
120 mbedtls_time_t t = mbedtls_time(NULL), oldest = 0;
121 mbedtls_ssl_cache_entry *old = NULL;
122 #endif
123 mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
124 mbedtls_ssl_cache_entry *cur, *prv;
125 int count = 0;
126
127 #if defined(MBEDTLS_THREADING_C)
128 if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
129 return ret;
130 }
131 #endif
132
133 cur = cache->chain;
134 prv = NULL;
135
136 while (cur != NULL) {
137 count++;
138
139 #if defined(MBEDTLS_HAVE_TIME)
140 if (cache->timeout != 0 &&
141 (int) (t - cur->timestamp) > cache->timeout) {
142 cur->timestamp = t;
143 break; /* expired, reuse this slot, update timestamp */
144 }
145 #endif
146
147 if (memcmp(session->id, cur->session.id, cur->session.id_len) == 0) {
148 break; /* client reconnected, keep timestamp for session id */
149
150 }
151 #if defined(MBEDTLS_HAVE_TIME)
152 if (oldest == 0 || cur->timestamp < oldest) {
153 oldest = cur->timestamp;
154 old = cur;
155 }
156 #endif
157
158 prv = cur;
159 cur = cur->next;
160 }
161
162 if (cur == NULL) {
163 #if defined(MBEDTLS_HAVE_TIME)
164 /*
165 * Reuse oldest entry if max_entries reached
166 */
167 if (count >= cache->max_entries) {
168 if (old == NULL) {
169 /* This should only happen on an ill-configured cache
170 * with max_entries == 0. */
171 ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
172 goto exit;
173 }
174
175 cur = old;
176 }
177 #else /* MBEDTLS_HAVE_TIME */
178 /*
179 * Reuse first entry in chain if max_entries reached,
180 * but move to last place
181 */
182 if (count >= cache->max_entries) {
183 if (cache->chain == NULL) {
184 ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR;
185 goto exit;
186 }
187
188 cur = cache->chain;
189 cache->chain = cur->next;
190 cur->next = NULL;
191 prv->next = cur;
192 }
193 #endif /* MBEDTLS_HAVE_TIME */
194 else {
195 /*
196 * max_entries not reached, create new entry
197 */
198 cur = mbedtls_calloc(1, sizeof(mbedtls_ssl_cache_entry));
199 if (cur == NULL) {
200 ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
201 goto exit;
202 }
203
204 if (prv == NULL) {
205 cache->chain = cur;
206 } else {
207 prv->next = cur;
208 }
209 }
210
211 #if defined(MBEDTLS_HAVE_TIME)
212 cur->timestamp = t;
213 #endif
214 }
215
216 #if defined(MBEDTLS_X509_CRT_PARSE_C) && \
217 defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
218 /*
219 * If we're reusing an entry, free its certificate first
220 */
221 if (cur->peer_cert.p != NULL) {
222 mbedtls_free(cur->peer_cert.p);
223 memset(&cur->peer_cert, 0, sizeof(mbedtls_x509_buf));
224 }
225 #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
226
227 /* Copy the entire session; this temporarily makes a copy of the
228 * X.509 CRT structure even though we only want to store the raw CRT.
229 * This inefficiency will go away as soon as we implement on-demand
230 * parsing of CRTs, in which case there's no need for the `peer_cert`
231 * field anymore in the first place, and we're done after this call. */
232 ret = mbedtls_ssl_session_copy(&cur->session, session);
233 if (ret != 0) {
234 goto exit;
235 }
236
237 #if defined(MBEDTLS_X509_CRT_PARSE_C) && \
238 defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
239 /* If present, free the X.509 structure and only store the raw CRT data. */
240 if (cur->session.peer_cert != NULL) {
241 cur->peer_cert.p =
242 mbedtls_calloc(1, cur->session.peer_cert->raw.len);
243 if (cur->peer_cert.p == NULL) {
244 ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
245 goto exit;
246 }
247
248 memcpy(cur->peer_cert.p,
249 cur->session.peer_cert->raw.p,
250 cur->session.peer_cert->raw.len);
251 cur->peer_cert.len = session->peer_cert->raw.len;
252
253 mbedtls_x509_crt_free(cur->session.peer_cert);
254 mbedtls_free(cur->session.peer_cert);
255 cur->session.peer_cert = NULL;
256 }
257 #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
258
259 ret = 0;
260
261 exit:
262 #if defined(MBEDTLS_THREADING_C)
263 if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
264 ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
265 }
266 #endif
267
268 return ret;
269 }
270
271 #if defined(MBEDTLS_HAVE_TIME)
mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context * cache,int timeout)272 void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout)
273 {
274 if (timeout < 0) {
275 timeout = 0;
276 }
277
278 cache->timeout = timeout;
279 }
280 #endif /* MBEDTLS_HAVE_TIME */
281
mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context * cache,int max)282 void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max)
283 {
284 if (max < 0) {
285 max = 0;
286 }
287
288 cache->max_entries = max;
289 }
290
mbedtls_ssl_cache_free(mbedtls_ssl_cache_context * cache)291 void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache)
292 {
293 mbedtls_ssl_cache_entry *cur, *prv;
294
295 cur = cache->chain;
296
297 while (cur != NULL) {
298 prv = cur;
299 cur = cur->next;
300
301 mbedtls_ssl_session_free(&prv->session);
302
303 #if defined(MBEDTLS_X509_CRT_PARSE_C) && \
304 defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
305 mbedtls_free(prv->peer_cert.p);
306 #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */
307
308 mbedtls_free(prv);
309 }
310
311 #if defined(MBEDTLS_THREADING_C)
312 mbedtls_mutex_free(&cache->mutex);
313 #endif
314 cache->chain = NULL;
315 }
316
317 #endif /* MBEDTLS_SSL_CACHE_C */
318