ssl_cache.c 11 KB

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