2
0

ssl_cache.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. #include "common.h"
  12. #if defined(MBEDTLS_SSL_CACHE_C)
  13. #include "mbedtls/platform.h"
  14. #include "mbedtls/ssl_cache.h"
  15. #include "ssl_misc.h"
  16. #include "mbedtls/error.h"
  17. #include <string.h>
  18. void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache)
  19. {
  20. memset(cache, 0, sizeof(mbedtls_ssl_cache_context));
  21. cache->timeout = MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT;
  22. cache->max_entries = MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES;
  23. #if defined(MBEDTLS_THREADING_C)
  24. mbedtls_mutex_init(&cache->mutex);
  25. #endif
  26. }
  27. MBEDTLS_CHECK_RETURN_CRITICAL
  28. static int ssl_cache_find_entry(mbedtls_ssl_cache_context *cache,
  29. unsigned char const *session_id,
  30. size_t session_id_len,
  31. mbedtls_ssl_cache_entry **dst)
  32. {
  33. int ret = MBEDTLS_ERR_SSL_CACHE_ENTRY_NOT_FOUND;
  34. #if defined(MBEDTLS_HAVE_TIME)
  35. mbedtls_time_t t = mbedtls_time(NULL);
  36. #endif
  37. mbedtls_ssl_cache_entry *cur;
  38. for (cur = cache->chain; cur != NULL; cur = cur->next) {
  39. #if defined(MBEDTLS_HAVE_TIME)
  40. if (cache->timeout != 0 &&
  41. (int) (t - cur->timestamp) > cache->timeout) {
  42. continue;
  43. }
  44. #endif
  45. if (session_id_len != cur->session_id_len ||
  46. memcmp(session_id, cur->session_id,
  47. cur->session_id_len) != 0) {
  48. continue;
  49. }
  50. break;
  51. }
  52. if (cur != NULL) {
  53. *dst = cur;
  54. ret = 0;
  55. }
  56. return ret;
  57. }
  58. int mbedtls_ssl_cache_get(void *data,
  59. unsigned char const *session_id,
  60. size_t session_id_len,
  61. mbedtls_ssl_session *session)
  62. {
  63. int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
  64. mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
  65. mbedtls_ssl_cache_entry *entry;
  66. #if defined(MBEDTLS_THREADING_C)
  67. if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
  68. return ret;
  69. }
  70. #endif
  71. ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry);
  72. if (ret != 0) {
  73. goto exit;
  74. }
  75. ret = mbedtls_ssl_session_load(session,
  76. entry->session,
  77. entry->session_len);
  78. if (ret != 0) {
  79. goto exit;
  80. }
  81. ret = 0;
  82. exit:
  83. #if defined(MBEDTLS_THREADING_C)
  84. if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
  85. ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
  86. }
  87. #endif
  88. return ret;
  89. }
  90. /* zeroize a cache entry */
  91. static void ssl_cache_entry_zeroize(mbedtls_ssl_cache_entry *entry)
  92. {
  93. if (entry == NULL) {
  94. return;
  95. }
  96. /* zeroize and free session structure */
  97. if (entry->session != NULL) {
  98. mbedtls_zeroize_and_free(entry->session, entry->session_len);
  99. }
  100. /* zeroize the whole entry structure */
  101. mbedtls_platform_zeroize(entry, sizeof(mbedtls_ssl_cache_entry));
  102. }
  103. MBEDTLS_CHECK_RETURN_CRITICAL
  104. static int ssl_cache_pick_writing_slot(mbedtls_ssl_cache_context *cache,
  105. unsigned char const *session_id,
  106. size_t session_id_len,
  107. mbedtls_ssl_cache_entry **dst)
  108. {
  109. #if defined(MBEDTLS_HAVE_TIME)
  110. mbedtls_time_t t = mbedtls_time(NULL), oldest = 0;
  111. #endif /* MBEDTLS_HAVE_TIME */
  112. mbedtls_ssl_cache_entry *old = NULL;
  113. int count = 0;
  114. mbedtls_ssl_cache_entry *cur, *last;
  115. /* Check 1: Is there already an entry with the given session ID?
  116. *
  117. * If yes, overwrite it.
  118. *
  119. * If not, `count` will hold the size of the session cache
  120. * at the end of this loop, and `last` will point to the last
  121. * entry, both of which will be used later. */
  122. last = NULL;
  123. for (cur = cache->chain; cur != NULL; cur = cur->next) {
  124. count++;
  125. if (session_id_len == cur->session_id_len &&
  126. memcmp(session_id, cur->session_id, cur->session_id_len) == 0) {
  127. goto found;
  128. }
  129. last = cur;
  130. }
  131. /* Check 2: Is there an outdated entry in the cache?
  132. *
  133. * If so, overwrite it.
  134. *
  135. * If not, remember the oldest entry in `old` for later.
  136. */
  137. #if defined(MBEDTLS_HAVE_TIME)
  138. for (cur = cache->chain; cur != NULL; cur = cur->next) {
  139. if (cache->timeout != 0 &&
  140. (int) (t - cur->timestamp) > cache->timeout) {
  141. goto found;
  142. }
  143. if (oldest == 0 || cur->timestamp < oldest) {
  144. oldest = cur->timestamp;
  145. old = cur;
  146. }
  147. }
  148. #endif /* MBEDTLS_HAVE_TIME */
  149. /* Check 3: Is there free space in the cache? */
  150. if (count < cache->max_entries) {
  151. /* Create new entry */
  152. cur = mbedtls_calloc(1, sizeof(mbedtls_ssl_cache_entry));
  153. if (cur == NULL) {
  154. return MBEDTLS_ERR_SSL_ALLOC_FAILED;
  155. }
  156. /* Append to the end of the linked list. */
  157. if (last == NULL) {
  158. cache->chain = cur;
  159. } else {
  160. last->next = cur;
  161. }
  162. goto found;
  163. }
  164. /* Last resort: The cache is full and doesn't contain any outdated
  165. * elements. In this case, we evict the oldest one, judged by timestamp
  166. * (if present) or cache-order. */
  167. #if defined(MBEDTLS_HAVE_TIME)
  168. if (old == NULL) {
  169. /* This should only happen on an ill-configured cache
  170. * with max_entries == 0. */
  171. return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
  172. }
  173. #else /* MBEDTLS_HAVE_TIME */
  174. /* Reuse first entry in chain, but move to last place. */
  175. if (cache->chain == NULL) {
  176. /* This should never happen */
  177. return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
  178. }
  179. old = cache->chain;
  180. cache->chain = old->next;
  181. old->next = NULL;
  182. last->next = old;
  183. #endif /* MBEDTLS_HAVE_TIME */
  184. /* Now `old` points to the oldest entry to be overwritten. */
  185. cur = old;
  186. found:
  187. /* If we're reusing an entry, free it first. */
  188. if (cur->session != NULL) {
  189. /* `ssl_cache_entry_zeroize` would break the chain,
  190. * so we reuse `old` to record `next` temporarily. */
  191. old = cur->next;
  192. ssl_cache_entry_zeroize(cur);
  193. cur->next = old;
  194. }
  195. #if defined(MBEDTLS_HAVE_TIME)
  196. cur->timestamp = t;
  197. #endif
  198. *dst = cur;
  199. return 0;
  200. }
  201. int mbedtls_ssl_cache_set(void *data,
  202. unsigned char const *session_id,
  203. size_t session_id_len,
  204. const mbedtls_ssl_session *session)
  205. {
  206. int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
  207. mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
  208. mbedtls_ssl_cache_entry *cur;
  209. size_t session_serialized_len = 0;
  210. unsigned char *session_serialized = NULL;
  211. #if defined(MBEDTLS_THREADING_C)
  212. if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
  213. return ret;
  214. }
  215. #endif
  216. ret = ssl_cache_pick_writing_slot(cache,
  217. session_id, session_id_len,
  218. &cur);
  219. if (ret != 0) {
  220. goto exit;
  221. }
  222. /* Check how much space we need to serialize the session
  223. * and allocate a sufficiently large buffer. */
  224. ret = mbedtls_ssl_session_save(session, NULL, 0, &session_serialized_len);
  225. if (ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) {
  226. goto exit;
  227. }
  228. session_serialized = mbedtls_calloc(1, session_serialized_len);
  229. if (session_serialized == NULL) {
  230. ret = MBEDTLS_ERR_SSL_ALLOC_FAILED;
  231. goto exit;
  232. }
  233. /* Now serialize the session into the allocated buffer. */
  234. ret = mbedtls_ssl_session_save(session,
  235. session_serialized,
  236. session_serialized_len,
  237. &session_serialized_len);
  238. if (ret != 0) {
  239. goto exit;
  240. }
  241. if (session_id_len > sizeof(cur->session_id)) {
  242. ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA;
  243. goto exit;
  244. }
  245. cur->session_id_len = session_id_len;
  246. memcpy(cur->session_id, session_id, session_id_len);
  247. cur->session = session_serialized;
  248. cur->session_len = session_serialized_len;
  249. session_serialized = NULL;
  250. ret = 0;
  251. exit:
  252. #if defined(MBEDTLS_THREADING_C)
  253. if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
  254. ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
  255. }
  256. #endif
  257. if (session_serialized != NULL) {
  258. mbedtls_zeroize_and_free(session_serialized, session_serialized_len);
  259. session_serialized = NULL;
  260. }
  261. return ret;
  262. }
  263. int mbedtls_ssl_cache_remove(void *data,
  264. unsigned char const *session_id,
  265. size_t session_id_len)
  266. {
  267. int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
  268. mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data;
  269. mbedtls_ssl_cache_entry *entry;
  270. mbedtls_ssl_cache_entry *prev;
  271. #if defined(MBEDTLS_THREADING_C)
  272. if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) {
  273. return ret;
  274. }
  275. #endif
  276. ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry);
  277. /* No valid entry found, exit with success */
  278. if (ret != 0) {
  279. ret = 0;
  280. goto exit;
  281. }
  282. /* Now we remove the entry from the chain */
  283. if (entry == cache->chain) {
  284. cache->chain = entry->next;
  285. goto free;
  286. }
  287. for (prev = cache->chain; prev->next != NULL; prev = prev->next) {
  288. if (prev->next == entry) {
  289. prev->next = entry->next;
  290. break;
  291. }
  292. }
  293. free:
  294. ssl_cache_entry_zeroize(entry);
  295. mbedtls_free(entry);
  296. ret = 0;
  297. exit:
  298. #if defined(MBEDTLS_THREADING_C)
  299. if (mbedtls_mutex_unlock(&cache->mutex) != 0) {
  300. ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR;
  301. }
  302. #endif
  303. return ret;
  304. }
  305. #if defined(MBEDTLS_HAVE_TIME)
  306. void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout)
  307. {
  308. if (timeout < 0) {
  309. timeout = 0;
  310. }
  311. cache->timeout = timeout;
  312. }
  313. #endif /* MBEDTLS_HAVE_TIME */
  314. void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max)
  315. {
  316. if (max < 0) {
  317. max = 0;
  318. }
  319. cache->max_entries = max;
  320. }
  321. void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache)
  322. {
  323. mbedtls_ssl_cache_entry *cur, *prv;
  324. cur = cache->chain;
  325. while (cur != NULL) {
  326. prv = cur;
  327. cur = cur->next;
  328. ssl_cache_entry_zeroize(prv);
  329. mbedtls_free(prv);
  330. }
  331. #if defined(MBEDTLS_THREADING_C)
  332. mbedtls_mutex_free(&cache->mutex);
  333. #endif
  334. cache->chain = NULL;
  335. }
  336. #endif /* MBEDTLS_SSL_CACHE_C */