1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "private-lib-core.h" 26 27typedef struct lws_tls_session_cache_mbedtls { 28 lws_dll2_t list; 29 30 mbedtls_ssl_session session; 31 lws_sorted_usec_list_t sul_ttl; 32 33 /* name is overallocated here */ 34} lws_tls_scm_t; 35 36#define lwsl_tlssess lwsl_info 37 38 39 40static void 41__lws_tls_session_destroy(lws_tls_scm_t *ts) 42{ 43 lwsl_tlssess("%s: %s (%u)\n", __func__, (const char *)&ts[1], 44 (unsigned int)(ts->list.owner->count - 1)); 45 46 lws_sul_cancel(&ts->sul_ttl); 47 mbedtls_ssl_session_free(&ts->session); 48 lws_dll2_remove(&ts->list); /* vh lock */ 49 50 lws_free(ts); 51} 52 53static lws_tls_scm_t * 54__lws_tls_session_lookup_by_name(struct lws_vhost *vh, const char *name) 55{ 56 lws_start_foreach_dll(struct lws_dll2 *, p, 57 lws_dll2_get_head(&vh->tls_sessions)) { 58 lws_tls_scm_t *ts = lws_container_of(p, lws_tls_scm_t, list); 59 const char *ts_name = (const char *)&ts[1]; 60 61 if (!strcmp(name, ts_name)) 62 return ts; 63 64 } lws_end_foreach_dll(p); 65 66 return NULL; 67} 68 69/* 70 * If possible, reuse an existing, cached session 71 */ 72 73void 74lws_tls_reuse_session(struct lws *wsi) 75{ 76 char buf[LWS_SESSION_TAG_LEN]; 77 mbedtls_ssl_context *msc; 78 lws_tls_scm_t *ts; 79 80 if (!wsi->a.vhost || 81 wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE) 82 return; 83 84 lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */ 85 lws_vhost_lock(wsi->a.vhost); /* -------------- vh { */ 86 87 if (lws_tls_session_tag_from_wsi(wsi, buf, sizeof(buf))) 88 goto bail; 89 90 ts = __lws_tls_session_lookup_by_name(wsi->a.vhost, buf); 91 92 if (!ts) { 93 lwsl_tlssess("%s: no existing session for %s\n", __func__, buf); 94 goto bail; 95 } 96 97 lwsl_tlssess("%s: %s\n", __func__, (const char *)&ts[1]); 98 wsi->tls_session_reused = 1; 99 100 msc = SSL_mbedtls_ssl_context_from_SSL(wsi->tls.ssl); 101 mbedtls_ssl_set_session(msc, &ts->session); 102 103 /* keep our session list sorted in lru -> mru order */ 104 105 lws_dll2_remove(&ts->list); 106 lws_dll2_add_tail(&ts->list, &wsi->a.vhost->tls_sessions); 107 108bail: 109 lws_vhost_unlock(wsi->a.vhost); /* } vh -------------- */ 110 lws_context_unlock(wsi->a.context); /* } cx -------------- */ 111} 112 113int 114lws_tls_session_is_reused(struct lws *wsi) 115{ 116#if defined(LWS_WITH_CLIENT) 117 struct lws *nwsi = lws_get_network_wsi(wsi); 118 119 if (!nwsi) 120 return 0; 121 122 return nwsi->tls_session_reused; 123#else 124 return 0; 125#endif 126} 127 128static int 129lws_tls_session_destroy_dll(struct lws_dll2 *d, void *user) 130{ 131 lws_tls_scm_t *ts = lws_container_of(d, lws_tls_scm_t, list); 132 133 __lws_tls_session_destroy(ts); 134 135 return 0; 136} 137 138void 139lws_tls_session_vh_destroy(struct lws_vhost *vh) 140{ 141 lws_dll2_foreach_safe(&vh->tls_sessions, NULL, 142 lws_tls_session_destroy_dll); 143} 144 145static void 146lws_tls_session_expiry_cb(lws_sorted_usec_list_t *sul) 147{ 148 lws_tls_scm_t *ts = lws_container_of(sul, lws_tls_scm_t, sul_ttl); 149 struct lws_vhost *vh = lws_container_of(ts->list.owner, 150 struct lws_vhost, tls_sessions); 151 152 lws_context_lock(vh->context, __func__); /* -------------- cx { */ 153 lws_vhost_lock(vh); /* -------------- vh { */ 154 __lws_tls_session_destroy(ts); 155 lws_vhost_unlock(vh); /* } vh -------------- */ 156 lws_context_unlock(vh->context); /* } cx -------------- */ 157} 158 159/* 160 * Called after SSL_accept on the wsi 161 */ 162 163int 164lws_tls_session_new_mbedtls(struct lws *wsi) 165{ 166 char buf[LWS_SESSION_TAG_LEN]; 167 mbedtls_ssl_context *msc; 168 struct lws_vhost *vh; 169 lws_tls_scm_t *ts; 170 size_t nl; 171#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG) 172 const char *disposition = "reuse"; 173#endif 174 175 vh = wsi->a.vhost; 176 if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE) 177 return 0; 178 179 if (lws_tls_session_tag_from_wsi(wsi, buf, sizeof(buf))) 180 return 0; 181 182 nl = strlen(buf); 183 184 msc = SSL_mbedtls_ssl_context_from_SSL(wsi->tls.ssl); 185 186 lws_context_lock(vh->context, __func__); /* -------------- cx { */ 187 lws_vhost_lock(vh); /* -------------- vh { */ 188 189 ts = __lws_tls_session_lookup_by_name(vh, buf); 190 191 if (!ts) { 192 /* 193 * We have to make our own, new session 194 */ 195 196 if (vh->tls_sessions.count == vh->tls_session_cache_max) { 197 198 /* 199 * We have reached the vhost's session cache limit, 200 * prune the LRU / head 201 */ 202 ts = lws_container_of(vh->tls_sessions.head, 203 lws_tls_scm_t, list); 204 205 lwsl_tlssess("%s: pruning oldest session (hit max %u)\n", 206 __func__, 207 (unsigned int)vh->tls_session_cache_max); 208 209 lws_vhost_lock(vh); /* -------------- vh { */ 210 __lws_tls_session_destroy(ts); 211 lws_vhost_unlock(vh); /* } vh -------------- */ 212 } 213 214 ts = lws_malloc(sizeof(*ts) + nl + 1, __func__); 215 216 if (!ts) 217 goto bail; 218 219 memset(ts, 0, sizeof(*ts)); 220 memcpy(&ts[1], buf, nl + 1); 221 222 if (mbedtls_ssl_get_session(msc, &ts->session)) { 223 lws_free(ts); 224 /* no joy for whatever reason */ 225 goto bail; 226 } 227 228 lws_dll2_add_tail(&ts->list, &vh->tls_sessions); 229 230 lws_sul_schedule(wsi->a.context, wsi->tsi, &ts->sul_ttl, 231 lws_tls_session_expiry_cb, 232 (int64_t)vh->tls.tls_session_cache_ttl * 233 LWS_US_PER_SEC); 234 235#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG) 236 disposition = "new"; 237#endif 238 } else { 239 240 mbedtls_ssl_session_free(&ts->session); 241 242 if (mbedtls_ssl_get_session(msc, &ts->session)) 243 /* no joy for whatever reason */ 244 goto bail; 245 246 /* keep our session list sorted in lru -> mru order */ 247 248 lws_dll2_remove(&ts->list); 249 lws_dll2_add_tail(&ts->list, &vh->tls_sessions); 250 } 251 252 lws_vhost_unlock(vh); /* } vh -------------- */ 253 lws_context_unlock(vh->context); /* } cx -------------- */ 254 255 lwsl_tlssess("%s: %s: %s %s, (%s:%u)\n", __func__, 256 wsi->lc.gutag, disposition, buf, vh->name, 257 (unsigned int)vh->tls_sessions.count); 258 259 /* 260 * indicate we will hold on to the SSL_SESSION reference, and take 261 * responsibility to call SSL_SESSION_free() on it ourselves 262 */ 263 264 return 1; 265 266bail: 267 lws_vhost_unlock(vh); /* } vh -------------- */ 268 lws_context_unlock(vh->context); /* } cx -------------- */ 269 270 return 0; 271} 272 273#if defined(LWS_TLS_SYNTHESIZE_CB) 274 275/* 276 * On openssl, there is an async cb coming when the server issues the session 277 * information on the link, so we can pick it up and update the cache at the 278 * right time. 279 * 280 * On mbedtls and some version at least of borning ssl, this cb is either not 281 * part of the tls library apis or fails to arrive. 282 */ 283 284void 285lws_sess_cache_synth_cb(lws_sorted_usec_list_t *sul) 286{ 287 struct lws_lws_tls *tls = lws_container_of(sul, struct lws_lws_tls, 288 sul_cb_synth); 289 struct lws *wsi = lws_container_of(tls, struct lws, tls); 290 291 lws_tls_session_new_mbedtls(wsi); 292} 293#endif 294 295void 296lws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl) 297{ 298 /* Default to 1hr max recommendation from RFC5246 F.1.4 */ 299 vh->tls.tls_session_cache_ttl = !ttl ? 3600 : ttl; 300} 301 302int 303lws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port, 304 lws_tls_sess_cb_t cb_save, void *opq) 305{ 306 /* there seems no serialization / deserialization helper in mbedtls */ 307 lwsl_warn("%s: only supported on openssl atm\n", __func__); 308 309 return 1; 310} 311 312int 313lws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port, 314 lws_tls_sess_cb_t cb_load, void *opq) 315{ 316 /* there seems no serialization / deserialization helper in mbedtls */ 317 lwsl_warn("%s: only supported on openssl atm\n", __func__); 318 319 return 1; 320} 321