• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 typedef 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 
40 static void
__lws_tls_session_destroy(lws_tls_scm_t * ts)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 
53 static lws_tls_scm_t *
__lws_tls_session_lookup_by_name(struct lws_vhost * vh,const char * name)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 
73 void
lws_tls_reuse_session(struct lws * wsi)74 lws_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 
108 bail:
109 	lws_vhost_unlock(wsi->a.vhost); /* } vh --------------  */
110 	lws_context_unlock(wsi->a.context); /* } cx --------------  */
111 }
112 
113 int
lws_tls_session_is_reused(struct lws * wsi)114 lws_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 
128 static int
lws_tls_session_destroy_dll(struct lws_dll2 * d,void * user)129 lws_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 
138 void
lws_tls_session_vh_destroy(struct lws_vhost * vh)139 lws_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 
145 static void
lws_tls_session_expiry_cb(lws_sorted_usec_list_t * sul)146 lws_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 
163 int
lws_tls_session_new_mbedtls(struct lws * wsi)164 lws_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 
266 bail:
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 
284 void
lws_sess_cache_synth_cb(lws_sorted_usec_list_t * sul)285 lws_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 
295 void
lws_tls_session_cache(struct lws_vhost * vh,uint32_t ttl)296 lws_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 
302 int
lws_tls_session_dump_save(struct lws_vhost * vh,const char * host,uint16_t port,lws_tls_sess_cb_t cb_save,void * opq)303 lws_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 
312 int
lws_tls_session_dump_load(struct lws_vhost * vh,const char * host,uint16_t port,lws_tls_sess_cb_t cb_load,void * opq)313 lws_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