1d4afb5ceSopenharmony_ci/*
2d4afb5ceSopenharmony_ci * libwebsockets - small server side websockets and web server implementation
3d4afb5ceSopenharmony_ci *
4d4afb5ceSopenharmony_ci * Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
5d4afb5ceSopenharmony_ci *
6d4afb5ceSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
7d4afb5ceSopenharmony_ci * of this software and associated documentation files (the "Software"), to
8d4afb5ceSopenharmony_ci * deal in the Software without restriction, including without limitation the
9d4afb5ceSopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10d4afb5ceSopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
11d4afb5ceSopenharmony_ci * furnished to do so, subject to the following conditions:
12d4afb5ceSopenharmony_ci *
13d4afb5ceSopenharmony_ci * The above copyright notice and this permission notice shall be included in
14d4afb5ceSopenharmony_ci * all copies or substantial portions of the Software.
15d4afb5ceSopenharmony_ci *
16d4afb5ceSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d4afb5ceSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d4afb5ceSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19d4afb5ceSopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d4afb5ceSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21d4afb5ceSopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22d4afb5ceSopenharmony_ci * IN THE SOFTWARE.
23d4afb5ceSopenharmony_ci */
24d4afb5ceSopenharmony_ci
25d4afb5ceSopenharmony_ci#include "private-lib-core.h"
26d4afb5ceSopenharmony_ci
27d4afb5ceSopenharmony_citypedef struct lws_tls_session_cache_openssl {
28d4afb5ceSopenharmony_ci	lws_dll2_t			list;
29d4afb5ceSopenharmony_ci
30d4afb5ceSopenharmony_ci	SSL_SESSION			*session;
31d4afb5ceSopenharmony_ci	lws_sorted_usec_list_t		sul_ttl;
32d4afb5ceSopenharmony_ci
33d4afb5ceSopenharmony_ci	/* name is overallocated here */
34d4afb5ceSopenharmony_ci} lws_tls_sco_t;
35d4afb5ceSopenharmony_ci
36d4afb5ceSopenharmony_ci#define tlssess_loglevel		LLL_INFO
37d4afb5ceSopenharmony_ci#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
38d4afb5ceSopenharmony_ci#define lwsl_tlssess(...)		_lws_log(tlssess_loglevel, __VA_ARGS__)
39d4afb5ceSopenharmony_ci#else
40d4afb5ceSopenharmony_ci#define lwsl_tlssess(...)
41d4afb5ceSopenharmony_ci#endif
42d4afb5ceSopenharmony_ci
43d4afb5ceSopenharmony_cistatic void
44d4afb5ceSopenharmony_ci__lws_tls_session_destroy(lws_tls_sco_t *ts)
45d4afb5ceSopenharmony_ci{
46d4afb5ceSopenharmony_ci	lwsl_tlssess("%s: %s (%u)\n", __func__, (const char *)&ts[1],
47d4afb5ceSopenharmony_ci				     ts->list.owner->count - 1);
48d4afb5ceSopenharmony_ci
49d4afb5ceSopenharmony_ci	lws_sul_cancel(&ts->sul_ttl);
50d4afb5ceSopenharmony_ci	SSL_SESSION_free(ts->session);
51d4afb5ceSopenharmony_ci	lws_dll2_remove(&ts->list);		/* vh lock */
52d4afb5ceSopenharmony_ci
53d4afb5ceSopenharmony_ci	lws_free(ts);
54d4afb5ceSopenharmony_ci}
55d4afb5ceSopenharmony_ci
56d4afb5ceSopenharmony_cistatic lws_tls_sco_t *
57d4afb5ceSopenharmony_ci__lws_tls_session_lookup_by_name(struct lws_vhost *vh, const char *name)
58d4afb5ceSopenharmony_ci{
59d4afb5ceSopenharmony_ci	lws_start_foreach_dll(struct lws_dll2 *, p,
60d4afb5ceSopenharmony_ci			      lws_dll2_get_head(&vh->tls_sessions)) {
61d4afb5ceSopenharmony_ci		lws_tls_sco_t *ts = lws_container_of(p, lws_tls_sco_t, list);
62d4afb5ceSopenharmony_ci		const char *ts_name = (const char *)&ts[1];
63d4afb5ceSopenharmony_ci
64d4afb5ceSopenharmony_ci		if (!strcmp(name, ts_name))
65d4afb5ceSopenharmony_ci			return ts;
66d4afb5ceSopenharmony_ci
67d4afb5ceSopenharmony_ci	} lws_end_foreach_dll(p);
68d4afb5ceSopenharmony_ci
69d4afb5ceSopenharmony_ci	return NULL;
70d4afb5ceSopenharmony_ci}
71d4afb5ceSopenharmony_ci
72d4afb5ceSopenharmony_ci/*
73d4afb5ceSopenharmony_ci * If possible, reuse an existing, cached session
74d4afb5ceSopenharmony_ci */
75d4afb5ceSopenharmony_ci
76d4afb5ceSopenharmony_civoid
77d4afb5ceSopenharmony_cilws_tls_reuse_session(struct lws *wsi)
78d4afb5ceSopenharmony_ci{
79d4afb5ceSopenharmony_ci	char tag[LWS_SESSION_TAG_LEN];
80d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts;
81d4afb5ceSopenharmony_ci
82d4afb5ceSopenharmony_ci	if (!wsi->a.vhost ||
83d4afb5ceSopenharmony_ci	    wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
84d4afb5ceSopenharmony_ci		return;
85d4afb5ceSopenharmony_ci
86d4afb5ceSopenharmony_ci	lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */
87d4afb5ceSopenharmony_ci	lws_vhost_lock(wsi->a.vhost); /* -------------- vh { */
88d4afb5ceSopenharmony_ci
89d4afb5ceSopenharmony_ci	if (lws_tls_session_tag_from_wsi(wsi, tag, sizeof(tag)))
90d4afb5ceSopenharmony_ci		goto bail;
91d4afb5ceSopenharmony_ci	ts = __lws_tls_session_lookup_by_name(wsi->a.vhost, tag);
92d4afb5ceSopenharmony_ci
93d4afb5ceSopenharmony_ci	if (!ts) {
94d4afb5ceSopenharmony_ci		lwsl_tlssess("%s: no existing session for %s\n", __func__, tag);
95d4afb5ceSopenharmony_ci		goto bail;
96d4afb5ceSopenharmony_ci	}
97d4afb5ceSopenharmony_ci
98d4afb5ceSopenharmony_ci	lwsl_tlssess("%s: %s\n", __func__, (const char *)&ts[1]);
99d4afb5ceSopenharmony_ci
100d4afb5ceSopenharmony_ci	if (!SSL_set_session(wsi->tls.ssl, ts->session)) {
101d4afb5ceSopenharmony_ci		lwsl_err("%s: session not set for %s\n", __func__, tag);
102d4afb5ceSopenharmony_ci		goto bail;
103d4afb5ceSopenharmony_ci	}
104d4afb5ceSopenharmony_ci
105d4afb5ceSopenharmony_ci#if !defined(USE_WOLFSSL)
106d4afb5ceSopenharmony_ci	/* extend session lifetime */
107d4afb5ceSopenharmony_ci	SSL_SESSION_set_time(ts->session,
108d4afb5ceSopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL)
109d4afb5ceSopenharmony_ci			(unsigned long)
110d4afb5ceSopenharmony_ci#else
111d4afb5ceSopenharmony_ci			(long)
112d4afb5ceSopenharmony_ci#endif
113d4afb5ceSopenharmony_ci			time(NULL));
114d4afb5ceSopenharmony_ci#endif
115d4afb5ceSopenharmony_ci
116d4afb5ceSopenharmony_ci	/* keep our session list sorted in lru -> mru order */
117d4afb5ceSopenharmony_ci
118d4afb5ceSopenharmony_ci	lws_dll2_remove(&ts->list);
119d4afb5ceSopenharmony_ci	lws_dll2_add_tail(&ts->list, &wsi->a.vhost->tls_sessions);
120d4afb5ceSopenharmony_ci
121d4afb5ceSopenharmony_cibail:
122d4afb5ceSopenharmony_ci	lws_vhost_unlock(wsi->a.vhost); /* } vh --------------  */
123d4afb5ceSopenharmony_ci	lws_context_unlock(wsi->a.context); /* } cx --------------  */
124d4afb5ceSopenharmony_ci}
125d4afb5ceSopenharmony_ci
126d4afb5ceSopenharmony_ciint
127d4afb5ceSopenharmony_cilws_tls_session_is_reused(struct lws *wsi)
128d4afb5ceSopenharmony_ci{
129d4afb5ceSopenharmony_ci#if defined(LWS_WITH_CLIENT)
130d4afb5ceSopenharmony_ci	struct lws *nwsi = lws_get_network_wsi(wsi);
131d4afb5ceSopenharmony_ci
132d4afb5ceSopenharmony_ci	if (!nwsi || !nwsi->tls.ssl)
133d4afb5ceSopenharmony_ci		return 0;
134d4afb5ceSopenharmony_ci
135d4afb5ceSopenharmony_ci       return (int)SSL_session_reused(nwsi->tls.ssl);
136d4afb5ceSopenharmony_ci#else
137d4afb5ceSopenharmony_ci       return 0;
138d4afb5ceSopenharmony_ci#endif
139d4afb5ceSopenharmony_ci}
140d4afb5ceSopenharmony_ci
141d4afb5ceSopenharmony_cistatic int
142d4afb5ceSopenharmony_cilws_tls_session_destroy_dll(struct lws_dll2 *d, void *user)
143d4afb5ceSopenharmony_ci{
144d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts = lws_container_of(d, lws_tls_sco_t, list);
145d4afb5ceSopenharmony_ci
146d4afb5ceSopenharmony_ci	__lws_tls_session_destroy(ts);
147d4afb5ceSopenharmony_ci
148d4afb5ceSopenharmony_ci	return 0;
149d4afb5ceSopenharmony_ci}
150d4afb5ceSopenharmony_ci
151d4afb5ceSopenharmony_civoid
152d4afb5ceSopenharmony_cilws_tls_session_vh_destroy(struct lws_vhost *vh)
153d4afb5ceSopenharmony_ci{
154d4afb5ceSopenharmony_ci	lws_dll2_foreach_safe(&vh->tls_sessions, NULL,
155d4afb5ceSopenharmony_ci			      lws_tls_session_destroy_dll);
156d4afb5ceSopenharmony_ci}
157d4afb5ceSopenharmony_ci
158d4afb5ceSopenharmony_cistatic void
159d4afb5ceSopenharmony_cilws_tls_session_expiry_cb(lws_sorted_usec_list_t *sul)
160d4afb5ceSopenharmony_ci{
161d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts = lws_container_of(sul, lws_tls_sco_t, sul_ttl);
162d4afb5ceSopenharmony_ci	struct lws_vhost *vh = lws_container_of(ts->list.owner,
163d4afb5ceSopenharmony_ci						struct lws_vhost, tls_sessions);
164d4afb5ceSopenharmony_ci
165d4afb5ceSopenharmony_ci	lws_context_lock(vh->context, __func__); /* -------------- cx { */
166d4afb5ceSopenharmony_ci	lws_vhost_lock(vh); /* -------------- vh { */
167d4afb5ceSopenharmony_ci	__lws_tls_session_destroy(ts);
168d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
169d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
170d4afb5ceSopenharmony_ci}
171d4afb5ceSopenharmony_ci
172d4afb5ceSopenharmony_cistatic lws_tls_sco_t *
173d4afb5ceSopenharmony_cilws_tls_session_add_entry(struct lws_vhost *vh, const char *tag)
174d4afb5ceSopenharmony_ci{
175d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts;
176d4afb5ceSopenharmony_ci	size_t nl = strlen(tag);
177d4afb5ceSopenharmony_ci
178d4afb5ceSopenharmony_ci	if (vh->tls_sessions.count == (vh->tls_session_cache_max ?
179d4afb5ceSopenharmony_ci				      vh->tls_session_cache_max : 10)) {
180d4afb5ceSopenharmony_ci
181d4afb5ceSopenharmony_ci		/*
182d4afb5ceSopenharmony_ci		 * We have reached the vhost's session cache limit,
183d4afb5ceSopenharmony_ci		 * prune the LRU / head
184d4afb5ceSopenharmony_ci		 */
185d4afb5ceSopenharmony_ci		ts = lws_container_of(vh->tls_sessions.head,
186d4afb5ceSopenharmony_ci				      lws_tls_sco_t, list);
187d4afb5ceSopenharmony_ci
188d4afb5ceSopenharmony_ci		if (ts) { /* centos 7 ... */
189d4afb5ceSopenharmony_ci			lwsl_tlssess("%s: pruning oldest session\n", __func__);
190d4afb5ceSopenharmony_ci
191d4afb5ceSopenharmony_ci			lws_vhost_lock(vh); /* -------------- vh { */
192d4afb5ceSopenharmony_ci			__lws_tls_session_destroy(ts);
193d4afb5ceSopenharmony_ci			lws_vhost_unlock(vh); /* } vh --------------  */
194d4afb5ceSopenharmony_ci		}
195d4afb5ceSopenharmony_ci	}
196d4afb5ceSopenharmony_ci
197d4afb5ceSopenharmony_ci	ts = lws_malloc(sizeof(*ts) + nl + 1, __func__);
198d4afb5ceSopenharmony_ci
199d4afb5ceSopenharmony_ci	if (!ts)
200d4afb5ceSopenharmony_ci		return NULL;
201d4afb5ceSopenharmony_ci
202d4afb5ceSopenharmony_ci	memset(ts, 0, sizeof(*ts));
203d4afb5ceSopenharmony_ci	memcpy(&ts[1], tag, nl + 1);
204d4afb5ceSopenharmony_ci
205d4afb5ceSopenharmony_ci	lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
206d4afb5ceSopenharmony_ci
207d4afb5ceSopenharmony_ci	return ts;
208d4afb5ceSopenharmony_ci}
209d4afb5ceSopenharmony_ci
210d4afb5ceSopenharmony_cistatic int
211d4afb5ceSopenharmony_cilws_tls_session_new_cb(SSL *ssl, SSL_SESSION *sess)
212d4afb5ceSopenharmony_ci{
213d4afb5ceSopenharmony_ci	struct lws *wsi = (struct lws *)SSL_get_ex_data(ssl,
214d4afb5ceSopenharmony_ci					openssl_websocket_private_data_index);
215d4afb5ceSopenharmony_ci	char tag[LWS_SESSION_TAG_LEN];
216d4afb5ceSopenharmony_ci	struct lws_vhost *vh;
217d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts;
218d4afb5ceSopenharmony_ci	long ttl;
219d4afb5ceSopenharmony_ci#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
220d4afb5ceSopenharmony_ci	const char *disposition = "reuse";
221d4afb5ceSopenharmony_ci#endif
222d4afb5ceSopenharmony_ci
223d4afb5ceSopenharmony_ci	if (!wsi) {
224d4afb5ceSopenharmony_ci		lwsl_warn("%s: can't get wsi from ssl privdata\n", __func__);
225d4afb5ceSopenharmony_ci
226d4afb5ceSopenharmony_ci		return 0;
227d4afb5ceSopenharmony_ci	}
228d4afb5ceSopenharmony_ci
229d4afb5ceSopenharmony_ci	vh = wsi->a.vhost;
230d4afb5ceSopenharmony_ci	if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
231d4afb5ceSopenharmony_ci		return 0;
232d4afb5ceSopenharmony_ci
233d4afb5ceSopenharmony_ci	if (lws_tls_session_tag_from_wsi(wsi, tag, sizeof(tag)))
234d4afb5ceSopenharmony_ci		return 0;
235d4afb5ceSopenharmony_ci
236d4afb5ceSopenharmony_ci	/* api return is long, although we only support setting
237d4afb5ceSopenharmony_ci	 * default (300s) or max uint32_t */
238d4afb5ceSopenharmony_ci	ttl = SSL_SESSION_get_timeout(sess);
239d4afb5ceSopenharmony_ci
240d4afb5ceSopenharmony_ci	lws_context_lock(vh->context, __func__); /* -------------- cx { */
241d4afb5ceSopenharmony_ci	lws_vhost_lock(vh); /* -------------- vh { */
242d4afb5ceSopenharmony_ci
243d4afb5ceSopenharmony_ci	ts = __lws_tls_session_lookup_by_name(vh, tag);
244d4afb5ceSopenharmony_ci
245d4afb5ceSopenharmony_ci	if (!ts) {
246d4afb5ceSopenharmony_ci		ts = lws_tls_session_add_entry(vh, tag);
247d4afb5ceSopenharmony_ci
248d4afb5ceSopenharmony_ci		if (!ts)
249d4afb5ceSopenharmony_ci			goto bail;
250d4afb5ceSopenharmony_ci
251d4afb5ceSopenharmony_ci		lws_sul_schedule(wsi->a.context, wsi->tsi, &ts->sul_ttl,
252d4afb5ceSopenharmony_ci				 lws_tls_session_expiry_cb,
253d4afb5ceSopenharmony_ci				 ttl * LWS_US_PER_SEC);
254d4afb5ceSopenharmony_ci
255d4afb5ceSopenharmony_ci#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
256d4afb5ceSopenharmony_ci		disposition = "new";
257d4afb5ceSopenharmony_ci#endif
258d4afb5ceSopenharmony_ci
259d4afb5ceSopenharmony_ci		/*
260d4afb5ceSopenharmony_ci		 * We don't have to do a SSL_SESSION_up_ref() here, because
261d4afb5ceSopenharmony_ci		 * we will return from this callback indicating that we kept the
262d4afb5ceSopenharmony_ci		 * ref
263d4afb5ceSopenharmony_ci		 */
264d4afb5ceSopenharmony_ci	} else {
265d4afb5ceSopenharmony_ci		/*
266d4afb5ceSopenharmony_ci		 * Give up our refcount on the session we are about to replace
267d4afb5ceSopenharmony_ci		 * with a newer one
268d4afb5ceSopenharmony_ci		 */
269d4afb5ceSopenharmony_ci		SSL_SESSION_free(ts->session);
270d4afb5ceSopenharmony_ci
271d4afb5ceSopenharmony_ci		/* keep our session list sorted in lru -> mru order */
272d4afb5ceSopenharmony_ci
273d4afb5ceSopenharmony_ci		lws_dll2_remove(&ts->list);
274d4afb5ceSopenharmony_ci		lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
275d4afb5ceSopenharmony_ci	}
276d4afb5ceSopenharmony_ci
277d4afb5ceSopenharmony_ci	ts->session = sess;
278d4afb5ceSopenharmony_ci
279d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
280d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
281d4afb5ceSopenharmony_ci
282d4afb5ceSopenharmony_ci	lwsl_tlssess("%s: %p: %s: %s %s, ttl %lds (%s:%u)\n", __func__,
283d4afb5ceSopenharmony_ci		     sess, wsi->lc.gutag, disposition, tag, ttl, vh->name,
284d4afb5ceSopenharmony_ci		     vh->tls_sessions.count);
285d4afb5ceSopenharmony_ci
286d4afb5ceSopenharmony_ci	/*
287d4afb5ceSopenharmony_ci	 * indicate we will hold on to the SSL_SESSION reference, and take
288d4afb5ceSopenharmony_ci	 * responsibility to call SSL_SESSION_free() on it ourselves
289d4afb5ceSopenharmony_ci	 */
290d4afb5ceSopenharmony_ci
291d4afb5ceSopenharmony_ci	return 1;
292d4afb5ceSopenharmony_ci
293d4afb5ceSopenharmony_cibail:
294d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
295d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
296d4afb5ceSopenharmony_ci
297d4afb5ceSopenharmony_ci	return 0;
298d4afb5ceSopenharmony_ci}
299d4afb5ceSopenharmony_ci
300d4afb5ceSopenharmony_ci#if defined(LWS_TLS_SYNTHESIZE_CB)
301d4afb5ceSopenharmony_ci
302d4afb5ceSopenharmony_ci/*
303d4afb5ceSopenharmony_ci * On openssl, there is an async cb coming when the server issues the session
304d4afb5ceSopenharmony_ci * information on the link, so we can pick it up and update the cache at the
305d4afb5ceSopenharmony_ci * right time.
306d4afb5ceSopenharmony_ci *
307d4afb5ceSopenharmony_ci * On mbedtls and some version at least of borning ssl, this cb is either not
308d4afb5ceSopenharmony_ci * part of the tls library apis or fails to arrive.
309d4afb5ceSopenharmony_ci *
310d4afb5ceSopenharmony_ci * This synthetic cb is called instead for those build cases, scheduled for
311d4afb5ceSopenharmony_ci * +500ms after the tls negotiation completed.
312d4afb5ceSopenharmony_ci */
313d4afb5ceSopenharmony_ci
314d4afb5ceSopenharmony_civoid
315d4afb5ceSopenharmony_cilws_sess_cache_synth_cb(lws_sorted_usec_list_t *sul)
316d4afb5ceSopenharmony_ci{
317d4afb5ceSopenharmony_ci	struct lws_lws_tls *tls = lws_container_of(sul, struct lws_lws_tls,
318d4afb5ceSopenharmony_ci						   sul_cb_synth);
319d4afb5ceSopenharmony_ci	struct lws *wsi = lws_container_of(tls, struct lws, tls);
320d4afb5ceSopenharmony_ci	SSL_SESSION *sess;
321d4afb5ceSopenharmony_ci
322d4afb5ceSopenharmony_ci	if (lws_tls_session_is_reused(wsi))
323d4afb5ceSopenharmony_ci		return;
324d4afb5ceSopenharmony_ci
325d4afb5ceSopenharmony_ci	sess = SSL_get1_session(tls->ssl);
326d4afb5ceSopenharmony_ci	if (!sess)
327d4afb5ceSopenharmony_ci		return;
328d4afb5ceSopenharmony_ci
329d4afb5ceSopenharmony_ci	if (!SSL_SESSION_is_resumable(sess) || /* not worth caching, or... */
330d4afb5ceSopenharmony_ci	    !lws_tls_session_new_cb(tls->ssl, sess)) { /* ...cb didn't keep it */
331d4afb5ceSopenharmony_ci		/*
332d4afb5ceSopenharmony_ci		 * For now the policy if no session message after the wait,
333d4afb5ceSopenharmony_ci		 * is just let it be.  Typically the session info is sent
334d4afb5ceSopenharmony_ci		 * early.
335d4afb5ceSopenharmony_ci		 */
336d4afb5ceSopenharmony_ci		SSL_SESSION_free(sess);
337d4afb5ceSopenharmony_ci	}
338d4afb5ceSopenharmony_ci}
339d4afb5ceSopenharmony_ci#endif
340d4afb5ceSopenharmony_ci
341d4afb5ceSopenharmony_civoid
342d4afb5ceSopenharmony_cilws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl)
343d4afb5ceSopenharmony_ci{
344d4afb5ceSopenharmony_ci	long cmode;
345d4afb5ceSopenharmony_ci
346d4afb5ceSopenharmony_ci	if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
347d4afb5ceSopenharmony_ci		return;
348d4afb5ceSopenharmony_ci
349d4afb5ceSopenharmony_ci	cmode = SSL_CTX_get_session_cache_mode(vh->tls.ssl_client_ctx);
350d4afb5ceSopenharmony_ci
351d4afb5ceSopenharmony_ci	SSL_CTX_set_session_cache_mode(vh->tls.ssl_client_ctx,
352d4afb5ceSopenharmony_ci				       (int)(cmode | SSL_SESS_CACHE_CLIENT));
353d4afb5ceSopenharmony_ci
354d4afb5ceSopenharmony_ci	SSL_CTX_sess_set_new_cb(vh->tls.ssl_client_ctx, lws_tls_session_new_cb);
355d4afb5ceSopenharmony_ci
356d4afb5ceSopenharmony_ci	if (!ttl)
357d4afb5ceSopenharmony_ci		return;
358d4afb5ceSopenharmony_ci
359d4afb5ceSopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL)
360d4afb5ceSopenharmony_ci	SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, ttl);
361d4afb5ceSopenharmony_ci#else
362d4afb5ceSopenharmony_ci	SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, (long)ttl);
363d4afb5ceSopenharmony_ci#endif
364d4afb5ceSopenharmony_ci}
365d4afb5ceSopenharmony_ci
366d4afb5ceSopenharmony_ciint
367d4afb5ceSopenharmony_cilws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port,
368d4afb5ceSopenharmony_ci			  lws_tls_sess_cb_t cb_save, void *opq)
369d4afb5ceSopenharmony_ci{
370d4afb5ceSopenharmony_ci	struct lws_tls_session_dump d;
371d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts;
372d4afb5ceSopenharmony_ci	int ret = 1, bl;
373d4afb5ceSopenharmony_ci	void *v;
374d4afb5ceSopenharmony_ci
375d4afb5ceSopenharmony_ci	if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
376d4afb5ceSopenharmony_ci		return 1;
377d4afb5ceSopenharmony_ci
378d4afb5ceSopenharmony_ci	lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag));
379d4afb5ceSopenharmony_ci
380d4afb5ceSopenharmony_ci	lws_context_lock(vh->context, __func__); /* -------------- cx { */
381d4afb5ceSopenharmony_ci	lws_vhost_lock(vh); /* -------------- vh { */
382d4afb5ceSopenharmony_ci
383d4afb5ceSopenharmony_ci	ts = __lws_tls_session_lookup_by_name(vh, d.tag);
384d4afb5ceSopenharmony_ci	if (!ts)
385d4afb5ceSopenharmony_ci		goto bail;
386d4afb5ceSopenharmony_ci
387d4afb5ceSopenharmony_ci	/* We have a ref on the session, exit via bail to clean it... */
388d4afb5ceSopenharmony_ci
389d4afb5ceSopenharmony_ci	bl = i2d_SSL_SESSION(ts->session, NULL);
390d4afb5ceSopenharmony_ci	if (!bl)
391d4afb5ceSopenharmony_ci		goto bail;
392d4afb5ceSopenharmony_ci
393d4afb5ceSopenharmony_ci	d.blob_len = (size_t)bl;
394d4afb5ceSopenharmony_ci	v = d.blob = lws_malloc(d.blob_len, __func__);
395d4afb5ceSopenharmony_ci
396d4afb5ceSopenharmony_ci	if (d.blob) {
397d4afb5ceSopenharmony_ci
398d4afb5ceSopenharmony_ci		/* this advances d.blob by the blob size ;-) */
399d4afb5ceSopenharmony_ci		i2d_SSL_SESSION(ts->session, (uint8_t **)&d.blob);
400d4afb5ceSopenharmony_ci
401d4afb5ceSopenharmony_ci		d.opaque = opq;
402d4afb5ceSopenharmony_ci		d.blob = v;
403d4afb5ceSopenharmony_ci		if (cb_save(vh->context, &d))
404d4afb5ceSopenharmony_ci			lwsl_notice("%s: save failed\n", __func__);
405d4afb5ceSopenharmony_ci		else
406d4afb5ceSopenharmony_ci			ret = 0;
407d4afb5ceSopenharmony_ci
408d4afb5ceSopenharmony_ci		lws_free(v);
409d4afb5ceSopenharmony_ci	}
410d4afb5ceSopenharmony_ci
411d4afb5ceSopenharmony_cibail:
412d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
413d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
414d4afb5ceSopenharmony_ci
415d4afb5ceSopenharmony_ci	return ret;
416d4afb5ceSopenharmony_ci}
417d4afb5ceSopenharmony_ci
418d4afb5ceSopenharmony_ciint
419d4afb5ceSopenharmony_cilws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port,
420d4afb5ceSopenharmony_ci			  lws_tls_sess_cb_t cb_load, void *opq)
421d4afb5ceSopenharmony_ci{
422d4afb5ceSopenharmony_ci	struct lws_tls_session_dump d;
423d4afb5ceSopenharmony_ci	lws_tls_sco_t *ts;
424d4afb5ceSopenharmony_ci	SSL_SESSION *sess = NULL; /* allow it to "bail" early */
425d4afb5ceSopenharmony_ci	void *v;
426d4afb5ceSopenharmony_ci
427d4afb5ceSopenharmony_ci	if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
428d4afb5ceSopenharmony_ci		return 1;
429d4afb5ceSopenharmony_ci
430d4afb5ceSopenharmony_ci	d.opaque = opq;
431d4afb5ceSopenharmony_ci	lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag));
432d4afb5ceSopenharmony_ci
433d4afb5ceSopenharmony_ci	lws_context_lock(vh->context, __func__); /* -------------- cx { */
434d4afb5ceSopenharmony_ci	lws_vhost_lock(vh); /* -------------- vh { */
435d4afb5ceSopenharmony_ci
436d4afb5ceSopenharmony_ci	ts = __lws_tls_session_lookup_by_name(vh, d.tag);
437d4afb5ceSopenharmony_ci
438d4afb5ceSopenharmony_ci	if (ts) {
439d4afb5ceSopenharmony_ci		/*
440d4afb5ceSopenharmony_ci		 * Since we are getting this out of cold storage, we should
441d4afb5ceSopenharmony_ci		 * not replace any existing session since it is likely newer
442d4afb5ceSopenharmony_ci		 */
443d4afb5ceSopenharmony_ci		lwsl_notice("%s: session already exists for %s\n", __func__,
444d4afb5ceSopenharmony_ci				d.tag);
445d4afb5ceSopenharmony_ci		goto bail1;
446d4afb5ceSopenharmony_ci	}
447d4afb5ceSopenharmony_ci
448d4afb5ceSopenharmony_ci	if (cb_load(vh->context, &d)) {
449d4afb5ceSopenharmony_ci		lwsl_warn("%s: load failed\n", __func__);
450d4afb5ceSopenharmony_ci
451d4afb5ceSopenharmony_ci		goto bail1;
452d4afb5ceSopenharmony_ci	}
453d4afb5ceSopenharmony_ci
454d4afb5ceSopenharmony_ci	/* the callback has allocated the blob and set d.blob / d.blob_len */
455d4afb5ceSopenharmony_ci
456d4afb5ceSopenharmony_ci	v = d.blob;
457d4afb5ceSopenharmony_ci	/* this advances d.blob by the blob size ;-) */
458d4afb5ceSopenharmony_ci	sess = d2i_SSL_SESSION(NULL, (const uint8_t **)&d.blob,
459d4afb5ceSopenharmony_ci							(long)d.blob_len);
460d4afb5ceSopenharmony_ci	free(v); /* user code will have used malloc() */
461d4afb5ceSopenharmony_ci	if (!sess) {
462d4afb5ceSopenharmony_ci		lwsl_warn("%s: d2i_SSL_SESSION failed\n", __func__);
463d4afb5ceSopenharmony_ci		goto bail;
464d4afb5ceSopenharmony_ci	}
465d4afb5ceSopenharmony_ci
466d4afb5ceSopenharmony_ci	lws_vhost_lock(vh); /* -------------- vh { */
467d4afb5ceSopenharmony_ci	ts = lws_tls_session_add_entry(vh, d.tag);
468d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
469d4afb5ceSopenharmony_ci
470d4afb5ceSopenharmony_ci	if (!ts) {
471d4afb5ceSopenharmony_ci		lwsl_warn("%s: unable to add cache entry\n", __func__);
472d4afb5ceSopenharmony_ci		goto bail;
473d4afb5ceSopenharmony_ci	}
474d4afb5ceSopenharmony_ci
475d4afb5ceSopenharmony_ci	ts->session = sess;
476d4afb5ceSopenharmony_ci	lwsl_tlssess("%s: session loaded OK\n", __func__);
477d4afb5ceSopenharmony_ci
478d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
479d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
480d4afb5ceSopenharmony_ci
481d4afb5ceSopenharmony_ci	return 0;
482d4afb5ceSopenharmony_ci
483d4afb5ceSopenharmony_cibail:
484d4afb5ceSopenharmony_ci	SSL_SESSION_free(sess);
485d4afb5ceSopenharmony_cibail1:
486d4afb5ceSopenharmony_ci
487d4afb5ceSopenharmony_ci	lws_vhost_unlock(vh); /* } vh --------------  */
488d4afb5ceSopenharmony_ci	lws_context_unlock(vh->context); /* } cx --------------  */
489d4afb5ceSopenharmony_ci
490d4afb5ceSopenharmony_ci	return 1;
491d4afb5ceSopenharmony_ci}
492