1 | /* |
2 | * SSL session cache implementation |
3 | * |
4 | * Copyright The Mbed TLS Contributors |
5 | * SPDX-License-Identifier: Apache-2.0 |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); you may |
8 | * not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
15 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | */ |
19 | /* |
20 | * These session callbacks use a simple chained list |
21 | * to store and retrieve the session information. |
22 | */ |
23 | |
24 | #include "common.h" |
25 | |
26 | #if defined(MBEDTLS_SSL_CACHE_C) |
27 | |
28 | #include "mbedtls/platform.h" |
29 | |
30 | #include "mbedtls/ssl_cache.h" |
31 | #include "mbedtls/ssl_internal.h" |
32 | |
33 | #include <string.h> |
34 | |
35 | void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache) |
36 | { |
37 | memset(cache, 0, sizeof(mbedtls_ssl_cache_context)); |
38 | |
39 | cache->timeout = MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT; |
40 | cache->max_entries = MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES; |
41 | |
42 | #if defined(MBEDTLS_THREADING_C) |
43 | mbedtls_mutex_init(&cache->mutex); |
44 | #endif |
45 | } |
46 | |
47 | int mbedtls_ssl_cache_get(void *data, mbedtls_ssl_session *session) |
48 | { |
49 | int ret = 1; |
50 | #if defined(MBEDTLS_HAVE_TIME) |
51 | mbedtls_time_t t = mbedtls_time(NULL); |
52 | #endif |
53 | mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data; |
54 | mbedtls_ssl_cache_entry *cur, *entry; |
55 | |
56 | #if defined(MBEDTLS_THREADING_C) |
57 | if (mbedtls_mutex_lock(&cache->mutex) != 0) { |
58 | return 1; |
59 | } |
60 | #endif |
61 | |
62 | cur = cache->chain; |
63 | entry = NULL; |
64 | |
65 | while (cur != NULL) { |
66 | entry = cur; |
67 | cur = cur->next; |
68 | |
69 | #if defined(MBEDTLS_HAVE_TIME) |
70 | if (cache->timeout != 0 && |
71 | (int) (t - entry->timestamp) > cache->timeout) { |
72 | continue; |
73 | } |
74 | #endif |
75 | |
76 | if (session->id_len != entry->session.id_len || |
77 | memcmp(session->id, entry->session.id, |
78 | entry->session.id_len) != 0) { |
79 | continue; |
80 | } |
81 | |
82 | ret = mbedtls_ssl_session_copy(session, &entry->session); |
83 | if (ret != 0) { |
84 | ret = 1; |
85 | goto exit; |
86 | } |
87 | |
88 | #if defined(MBEDTLS_X509_CRT_PARSE_C) && \ |
89 | defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) |
90 | /* |
91 | * Restore peer certificate (without rest of the original chain) |
92 | */ |
93 | if (entry->peer_cert.p != NULL) { |
94 | /* `session->peer_cert` is NULL after the call to |
95 | * mbedtls_ssl_session_copy(), because cache entries |
96 | * have the `peer_cert` field set to NULL. */ |
97 | |
98 | if ((session->peer_cert = mbedtls_calloc(1, |
99 | sizeof(mbedtls_x509_crt))) == NULL) { |
100 | ret = 1; |
101 | goto exit; |
102 | } |
103 | |
104 | mbedtls_x509_crt_init(session->peer_cert); |
105 | if (mbedtls_x509_crt_parse(session->peer_cert, entry->peer_cert.p, |
106 | entry->peer_cert.len) != 0) { |
107 | mbedtls_free(session->peer_cert); |
108 | session->peer_cert = NULL; |
109 | ret = 1; |
110 | goto exit; |
111 | } |
112 | } |
113 | #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ |
114 | |
115 | ret = 0; |
116 | goto exit; |
117 | } |
118 | |
119 | exit: |
120 | #if defined(MBEDTLS_THREADING_C) |
121 | if (mbedtls_mutex_unlock(&cache->mutex) != 0) { |
122 | ret = 1; |
123 | } |
124 | #endif |
125 | |
126 | return ret; |
127 | } |
128 | |
129 | int mbedtls_ssl_cache_set(void *data, const mbedtls_ssl_session *session) |
130 | { |
131 | int ret = 1; |
132 | #if defined(MBEDTLS_HAVE_TIME) |
133 | mbedtls_time_t t = mbedtls_time(NULL), oldest = 0; |
134 | mbedtls_ssl_cache_entry *old = NULL; |
135 | #endif |
136 | mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data; |
137 | mbedtls_ssl_cache_entry *cur, *prv; |
138 | int count = 0; |
139 | |
140 | #if defined(MBEDTLS_THREADING_C) |
141 | if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) { |
142 | return ret; |
143 | } |
144 | #endif |
145 | |
146 | cur = cache->chain; |
147 | prv = NULL; |
148 | |
149 | while (cur != NULL) { |
150 | count++; |
151 | |
152 | #if defined(MBEDTLS_HAVE_TIME) |
153 | if (cache->timeout != 0 && |
154 | (int) (t - cur->timestamp) > cache->timeout) { |
155 | cur->timestamp = t; |
156 | break; /* expired, reuse this slot, update timestamp */ |
157 | } |
158 | #endif |
159 | |
160 | if (memcmp(session->id, cur->session.id, cur->session.id_len) == 0) { |
161 | break; /* client reconnected, keep timestamp for session id */ |
162 | |
163 | } |
164 | #if defined(MBEDTLS_HAVE_TIME) |
165 | if (oldest == 0 || cur->timestamp < oldest) { |
166 | oldest = cur->timestamp; |
167 | old = cur; |
168 | } |
169 | #endif |
170 | |
171 | prv = cur; |
172 | cur = cur->next; |
173 | } |
174 | |
175 | if (cur == NULL) { |
176 | #if defined(MBEDTLS_HAVE_TIME) |
177 | /* |
178 | * Reuse oldest entry if max_entries reached |
179 | */ |
180 | if (count >= cache->max_entries) { |
181 | if (old == NULL) { |
182 | ret = 1; |
183 | goto exit; |
184 | } |
185 | |
186 | cur = old; |
187 | } |
188 | #else /* MBEDTLS_HAVE_TIME */ |
189 | /* |
190 | * Reuse first entry in chain if max_entries reached, |
191 | * but move to last place |
192 | */ |
193 | if (count >= cache->max_entries) { |
194 | if (cache->chain == NULL) { |
195 | ret = 1; |
196 | goto exit; |
197 | } |
198 | |
199 | cur = cache->chain; |
200 | cache->chain = cur->next; |
201 | cur->next = NULL; |
202 | prv->next = cur; |
203 | } |
204 | #endif /* MBEDTLS_HAVE_TIME */ |
205 | else { |
206 | /* |
207 | * max_entries not reached, create new entry |
208 | */ |
209 | cur = mbedtls_calloc(1, sizeof(mbedtls_ssl_cache_entry)); |
210 | if (cur == NULL) { |
211 | ret = 1; |
212 | goto exit; |
213 | } |
214 | |
215 | if (prv == NULL) { |
216 | cache->chain = cur; |
217 | } else { |
218 | prv->next = cur; |
219 | } |
220 | } |
221 | |
222 | #if defined(MBEDTLS_HAVE_TIME) |
223 | cur->timestamp = t; |
224 | #endif |
225 | } |
226 | |
227 | #if defined(MBEDTLS_X509_CRT_PARSE_C) && \ |
228 | defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) |
229 | /* |
230 | * If we're reusing an entry, free its certificate first |
231 | */ |
232 | if (cur->peer_cert.p != NULL) { |
233 | mbedtls_free(cur->peer_cert.p); |
234 | memset(&cur->peer_cert, 0, sizeof(mbedtls_x509_buf)); |
235 | } |
236 | #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ |
237 | |
238 | /* Copy the entire session; this temporarily makes a copy of the |
239 | * X.509 CRT structure even though we only want to store the raw CRT. |
240 | * This inefficiency will go away as soon as we implement on-demand |
241 | * parsing of CRTs, in which case there's no need for the `peer_cert` |
242 | * field anymore in the first place, and we're done after this call. */ |
243 | ret = mbedtls_ssl_session_copy(&cur->session, session); |
244 | if (ret != 0) { |
245 | ret = 1; |
246 | goto exit; |
247 | } |
248 | |
249 | #if defined(MBEDTLS_X509_CRT_PARSE_C) && \ |
250 | defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) |
251 | /* If present, free the X.509 structure and only store the raw CRT data. */ |
252 | if (cur->session.peer_cert != NULL) { |
253 | cur->peer_cert.p = |
254 | mbedtls_calloc(1, cur->session.peer_cert->raw.len); |
255 | if (cur->peer_cert.p == NULL) { |
256 | ret = 1; |
257 | goto exit; |
258 | } |
259 | |
260 | memcpy(cur->peer_cert.p, |
261 | cur->session.peer_cert->raw.p, |
262 | cur->session.peer_cert->raw.len); |
263 | cur->peer_cert.len = session->peer_cert->raw.len; |
264 | |
265 | mbedtls_x509_crt_free(cur->session.peer_cert); |
266 | mbedtls_free(cur->session.peer_cert); |
267 | cur->session.peer_cert = NULL; |
268 | } |
269 | #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ |
270 | |
271 | ret = 0; |
272 | |
273 | exit: |
274 | #if defined(MBEDTLS_THREADING_C) |
275 | if (mbedtls_mutex_unlock(&cache->mutex) != 0) { |
276 | ret = 1; |
277 | } |
278 | #endif |
279 | |
280 | return ret; |
281 | } |
282 | |
283 | #if defined(MBEDTLS_HAVE_TIME) |
284 | void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout) |
285 | { |
286 | if (timeout < 0) { |
287 | timeout = 0; |
288 | } |
289 | |
290 | cache->timeout = timeout; |
291 | } |
292 | #endif /* MBEDTLS_HAVE_TIME */ |
293 | |
294 | void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max) |
295 | { |
296 | if (max < 0) { |
297 | max = 0; |
298 | } |
299 | |
300 | cache->max_entries = max; |
301 | } |
302 | |
303 | void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache) |
304 | { |
305 | mbedtls_ssl_cache_entry *cur, *prv; |
306 | |
307 | cur = cache->chain; |
308 | |
309 | while (cur != NULL) { |
310 | prv = cur; |
311 | cur = cur->next; |
312 | |
313 | mbedtls_ssl_session_free(&prv->session); |
314 | |
315 | #if defined(MBEDTLS_X509_CRT_PARSE_C) && \ |
316 | defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) |
317 | mbedtls_free(prv->peer_cert.p); |
318 | #endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ |
319 | |
320 | mbedtls_free(prv); |
321 | } |
322 | |
323 | #if defined(MBEDTLS_THREADING_C) |
324 | mbedtls_mutex_free(&cache->mutex); |
325 | #endif |
326 | cache->chain = NULL; |
327 | } |
328 | |
329 | #endif /* MBEDTLS_SSL_CACHE_C */ |
330 | |