mirror of
https://github.com/ARMmbed/mbedtls.git
synced 2025-05-09 00:21:18 +08:00

The reference session cache implementation may end up storing multiple sessions associated to the same session ID if the set()-call for the second session finds an outdated cache entry prior to noticing the entry with the matching session ID. While this logically overwrites the existing entry since we always search the cache in order, this is at least a waste of resources. This commit fixes this by always checking first whether the given ID is already present in the cache. It also restructures the code for easier readability. Fixes #4509. Signed-off-by: Hanno Becker <hanno.becker@arm.com>
365 lines
9.2 KiB
C
365 lines
9.2 KiB
C
/*
|
|
* SSL session cache implementation
|
|
*
|
|
* Copyright The Mbed TLS Contributors
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/*
|
|
* These session callbacks use a simple chained list
|
|
* to store and retrieve the session information.
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#if defined(MBEDTLS_SSL_CACHE_C)
|
|
|
|
#if defined(MBEDTLS_PLATFORM_C)
|
|
#include "mbedtls/platform.h"
|
|
#else
|
|
#include <stdlib.h>
|
|
#define mbedtls_calloc calloc
|
|
#define mbedtls_free free
|
|
#endif
|
|
|
|
#include "mbedtls/ssl_cache.h"
|
|
#include "ssl_misc.h"
|
|
|
|
#include <string.h>
|
|
|
|
void mbedtls_ssl_cache_init( mbedtls_ssl_cache_context *cache )
|
|
{
|
|
memset( cache, 0, sizeof( mbedtls_ssl_cache_context ) );
|
|
|
|
cache->timeout = MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT;
|
|
cache->max_entries = MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES;
|
|
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
mbedtls_mutex_init( &cache->mutex );
|
|
#endif
|
|
}
|
|
|
|
static int ssl_cache_find_entry( mbedtls_ssl_cache_context *cache,
|
|
unsigned char const *session_id,
|
|
size_t session_id_len,
|
|
mbedtls_ssl_cache_entry **dst )
|
|
{
|
|
int ret = 1;
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
mbedtls_time_t t = mbedtls_time( NULL );
|
|
#endif
|
|
mbedtls_ssl_cache_entry *cur;
|
|
|
|
for( cur = cache->chain; cur != NULL; cur = cur->next )
|
|
{
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
if( cache->timeout != 0 &&
|
|
(int) ( t - cur->timestamp ) > cache->timeout )
|
|
continue;
|
|
#endif
|
|
|
|
if( session_id_len != cur->session_id_len ||
|
|
memcmp( session_id, cur->session_id,
|
|
cur->session_id_len ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if( cur != NULL )
|
|
{
|
|
*dst = cur;
|
|
ret = 0;
|
|
}
|
|
|
|
return( ret );
|
|
}
|
|
|
|
|
|
int mbedtls_ssl_cache_get( void *data,
|
|
unsigned char const *session_id,
|
|
size_t session_id_len,
|
|
mbedtls_ssl_session *session )
|
|
{
|
|
int ret = 1;
|
|
mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
|
|
mbedtls_ssl_cache_entry *entry;
|
|
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
if( mbedtls_mutex_lock( &cache->mutex ) != 0 )
|
|
return( 1 );
|
|
#endif
|
|
|
|
ret = ssl_cache_find_entry( cache, session_id, session_id_len, &entry );
|
|
if( ret != 0 )
|
|
goto exit;
|
|
|
|
ret = mbedtls_ssl_session_load( session,
|
|
entry->session,
|
|
entry->session_len );
|
|
if( ret != 0 )
|
|
goto exit;
|
|
|
|
ret = 0;
|
|
|
|
exit:
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
if( mbedtls_mutex_unlock( &cache->mutex ) != 0 )
|
|
ret = 1;
|
|
#endif
|
|
|
|
return( ret );
|
|
}
|
|
|
|
static int ssl_cache_pick_writing_slot( mbedtls_ssl_cache_context *cache,
|
|
unsigned char const *session_id,
|
|
size_t session_id_len,
|
|
mbedtls_ssl_cache_entry **dst )
|
|
{
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
mbedtls_time_t t = mbedtls_time( NULL ), oldest = 0;
|
|
mbedtls_ssl_cache_entry *old = NULL;
|
|
#endif
|
|
int count = 0;
|
|
mbedtls_ssl_cache_entry *cur, *prv;
|
|
|
|
cur = cache->chain;
|
|
prv = NULL;
|
|
|
|
/* Check 1: Is there already an entry with the given session ID?
|
|
*
|
|
* If yes, overwrite it.
|
|
*
|
|
* If not, `count` will hold the size of the session cache
|
|
* at the end of this loop, and `prv` will point to the last
|
|
* entry, both of which will be used later. */
|
|
|
|
while( cur != NULL )
|
|
{
|
|
count++;
|
|
|
|
if( session_id_len == cur->session_id_len &&
|
|
memcmp( session_id, cur->session_id, cur->session_id_len ) == 0 )
|
|
{
|
|
goto found;
|
|
}
|
|
|
|
prv = cur;
|
|
cur = cur->next;
|
|
}
|
|
|
|
/* Check 2: Is there an outdated entry in the cache?
|
|
*
|
|
* If so, overwrite it.
|
|
*
|
|
* If not, remember the oldest entry in `old` for later.
|
|
*/
|
|
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
while( cur != NULL )
|
|
{
|
|
if( cache->timeout != 0 &&
|
|
(int) ( t - cur->timestamp ) > cache->timeout )
|
|
{
|
|
goto found;
|
|
}
|
|
|
|
if( oldest == 0 || cur->timestamp < oldest )
|
|
{
|
|
oldest = cur->timestamp;
|
|
old = cur;
|
|
}
|
|
|
|
prv = cur;
|
|
cur = cur->next;
|
|
}
|
|
#endif /* MBEDTLS_HAVE_TIME */
|
|
|
|
/* Check 3: Is there free space in the cache? */
|
|
|
|
if( count < cache->max_entries )
|
|
{
|
|
/* Create new entry */
|
|
cur = mbedtls_calloc( 1, sizeof(mbedtls_ssl_cache_entry) );
|
|
if( cur == NULL )
|
|
return( 1 );
|
|
|
|
/* Append to the end of the linked list. */
|
|
if( prv == NULL )
|
|
cache->chain = cur;
|
|
else
|
|
prv->next = cur;
|
|
|
|
goto found;
|
|
}
|
|
|
|
/* Last resort: The cache is full and doesn't contain any outdated
|
|
* elements. In this case, we evict the oldest one, judged by timestamp
|
|
* (if present) or cache-order. */
|
|
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
if( old == NULL )
|
|
{
|
|
/* This should only happen on an ill-configured cache
|
|
* with max_entries == 0. */
|
|
return( 1 );
|
|
}
|
|
#else /* MBEDTLS_HAVE_TIME */
|
|
/* Reuse first entry in chain, but move to last place. */
|
|
if( cache->chain == NULL )
|
|
return( 1 );
|
|
|
|
old = cache->chain;
|
|
cache->chain = old->next;
|
|
cur->next = NULL;
|
|
prv->next = old;
|
|
#endif /* MBEDTLS_HAVE_TIME */
|
|
|
|
/* Now `old` points to the oldest entry to be overwritten. */
|
|
cur = old;
|
|
|
|
found:
|
|
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
cur->timestamp = t;
|
|
#endif
|
|
|
|
/* If we're reusing an entry, free it first. */
|
|
if( cur->session != NULL )
|
|
{
|
|
mbedtls_free( cur->session );
|
|
cur->session = NULL;
|
|
cur->session_len = 0;
|
|
memset( cur->session_id, 0, sizeof( cur->session_id ) );
|
|
cur->session_id_len = 0;
|
|
}
|
|
|
|
*dst = cur;
|
|
return( 0 );
|
|
}
|
|
|
|
int mbedtls_ssl_cache_set( void *data,
|
|
unsigned char const *session_id,
|
|
size_t session_id_len,
|
|
const mbedtls_ssl_session *session )
|
|
{
|
|
int ret = 1;
|
|
mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
|
|
mbedtls_ssl_cache_entry *cur;
|
|
|
|
size_t session_serialized_len;
|
|
unsigned char *session_serialized = NULL;
|
|
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
if( ( ret = mbedtls_mutex_lock( &cache->mutex ) ) != 0 )
|
|
return( ret );
|
|
#endif
|
|
|
|
ret = ssl_cache_pick_writing_slot( cache,
|
|
session_id, session_id_len,
|
|
&cur );
|
|
if( ret != 0 )
|
|
goto exit;
|
|
|
|
/* Check how much space we need to serialize the session
|
|
* and allocate a sufficiently large buffer. */
|
|
ret = mbedtls_ssl_session_save( session, NULL, 0, &session_serialized_len );
|
|
if( ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL )
|
|
{
|
|
ret = 1;
|
|
goto exit;
|
|
}
|
|
|
|
session_serialized = mbedtls_calloc( 1, session_serialized_len );
|
|
if( session_serialized == NULL )
|
|
{
|
|
ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
|
|
goto exit;
|
|
}
|
|
|
|
/* Now serialize the session into the allocated buffer. */
|
|
ret = mbedtls_ssl_session_save( session,
|
|
session_serialized,
|
|
session_serialized_len,
|
|
&session_serialized_len );
|
|
if( ret != 0 )
|
|
goto exit;
|
|
|
|
if( session_id_len > sizeof( cur->session_id ) )
|
|
{
|
|
ret = 1;
|
|
goto exit;
|
|
}
|
|
cur->session_id_len = session_id_len;
|
|
memcpy( cur->session_id, session_id, session_id_len );
|
|
|
|
cur->session = session_serialized;
|
|
cur->session_len = session_serialized_len;
|
|
session_serialized = NULL;
|
|
|
|
ret = 0;
|
|
|
|
exit:
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
if( mbedtls_mutex_unlock( &cache->mutex ) != 0 )
|
|
ret = 1;
|
|
#endif
|
|
|
|
if( session_serialized != NULL )
|
|
mbedtls_platform_zeroize( session_serialized, session_serialized_len );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
#if defined(MBEDTLS_HAVE_TIME)
|
|
void mbedtls_ssl_cache_set_timeout( mbedtls_ssl_cache_context *cache, int timeout )
|
|
{
|
|
if( timeout < 0 ) timeout = 0;
|
|
|
|
cache->timeout = timeout;
|
|
}
|
|
#endif /* MBEDTLS_HAVE_TIME */
|
|
|
|
void mbedtls_ssl_cache_set_max_entries( mbedtls_ssl_cache_context *cache, int max )
|
|
{
|
|
if( max < 0 ) max = 0;
|
|
|
|
cache->max_entries = max;
|
|
}
|
|
|
|
void mbedtls_ssl_cache_free( mbedtls_ssl_cache_context *cache )
|
|
{
|
|
mbedtls_ssl_cache_entry *cur, *prv;
|
|
|
|
cur = cache->chain;
|
|
|
|
while( cur != NULL )
|
|
{
|
|
prv = cur;
|
|
cur = cur->next;
|
|
|
|
mbedtls_free( prv->session );
|
|
mbedtls_free( prv );
|
|
}
|
|
|
|
#if defined(MBEDTLS_THREADING_C)
|
|
mbedtls_mutex_free( &cache->mutex );
|
|
#endif
|
|
cache->chain = NULL;
|
|
}
|
|
|
|
#endif /* MBEDTLS_SSL_CACHE_C */
|