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