The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
ticket_stateful.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 1f2105e4dbbb699744a38c2c79b12ec1e2110eaa $
19 *
20 * @file tls/ticket_stateful.c
21 * @brief Stateful TLS session resumption, the session cache and its `load session`, `store session` and `clear session` sections
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
25 */
26RCSID("$Id: 1f2105e4dbbb699744a38c2c79b12ec1e2110eaa $")
27USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
28
29#ifdef WITH_TLS
30#define LOG_PREFIX "tls"
31#define _TLS_PRIVATE 1
32
33#include <freeradius-devel/internal/internal.h>
34#include <freeradius-devel/server/pair.h>
35#include <freeradius-devel/unlang/function.h>
36#include <freeradius-devel/unlang/subrequest.h>
37#include <freeradius-devel/util/debug.h>
38
39#include <freeradius-devel/protocol/tls/freeradius.h>
40
41#include "attrs.h"
42#include "base.h"
43#include "log.h"
44#include "strerror.h"
45#include "ticket.h"
46#include "verify.h"
47
48#include <openssl/ssl.h>
49
50#define TLS_TICKET_STATEFUL_DISABLED (!tls_cache || !conf->virtual_server)
51
52static inline CC_HINT(always_inline, nonnull(2))
53void _tls_ticket_stateful_load_state_reset(request_t *request, fr_tls_ticket_stateful_t *cache, char const *func)
54{
55 if (cache->load.sess) {
57 ROPTIONAL(RDEBUG3, DEBUG3, "(TLS) Session ID %pV - Freeing loaded session in %s", cache->session_id, func);
58 }
59
60 SSL_SESSION_free(cache->load.sess);
61 cache->load.sess = NULL;
62 }
63 cache->load.state = FR_TLS_TICKET_STATEFUL_INIT;
64}
65#define tls_ticket_stateful_load_state_reset(_request, _cache) _tls_ticket_stateful_load_state_reset(_request, _cache, __FUNCTION__)
66
67static inline CC_HINT(always_inline, nonnull(2))
68void _tls_ticket_stateful_store_state_reset(request_t *request, fr_tls_ticket_stateful_t *cache, char const *func)
69{
70 if (cache->store.sess) {
72 ROPTIONAL(RDEBUG3, DEBUG3, "(TLS) Session ID %pV - Freeing session to store in %s", &cache->store.id, func);
73 }
74 SSL_SESSION_free(cache->store.sess);
75 cache->store.sess = NULL;
76 fr_value_box_clear(&cache->store.id);
77 }
78 cache->store.state = FR_TLS_TICKET_STATEFUL_INIT;
79}
80#define tls_ticket_stateful_store_state_reset(_request, _cache) _tls_ticket_stateful_store_state_reset(_request, _cache, __FUNCTION__)
81
82static inline CC_HINT(always_inline)
83void _tls_ticket_stateful_clear_state_reset(request_t *request, fr_tls_ticket_stateful_t *cache, char const *func)
84{
85 if (!fr_type_is_null(cache->clear.id.type)) {
87 ROPTIONAL(RDEBUG3, DEBUG3, "(TLS) Session ID %pV - Freeing session ID to clear in %s",
88 &cache->clear.id, func);
89 }
90 fr_value_box_clear(&cache->clear.id);
91 }
92 cache->clear.state = FR_TLS_TICKET_STATEFUL_INIT;
93}
94#define tls_ticket_stateful_clear_state_reset(_request, _cache) _tls_ticket_stateful_clear_state_reset(_request, _cache, __FUNCTION__)
95
96/** Request `load session { ... }`, and tell the handshake a cache section is pending
97 */
98static inline CC_HINT(always_inline) void tls_ticket_stateful_load_state_request(fr_tls_session_t *tls_session)
99{
100 tls_session->cache->load.state = FR_TLS_TICKET_STATEFUL_REQUESTED;
101 TLS_PENDING_SET(tls_session, FR_TLS_PENDING_STATEFUL_TICKET);
102}
103
104/** Request `store session { ... }`, and tell the handshake a cache section is pending
105 */
106static inline CC_HINT(always_inline) void tls_ticket_stateful_store_state_request(fr_tls_session_t *tls_session)
107{
108 tls_session->cache->store.state = FR_TLS_TICKET_STATEFUL_REQUESTED;
109 TLS_PENDING_SET(tls_session, FR_TLS_PENDING_STATEFUL_TICKET);
110}
111
112/** Request `clear session { ... }`, and tell the handshake a cache section is pending
113 */
114static inline CC_HINT(always_inline) void tls_ticket_stateful_clear_state_request(fr_tls_session_t *tls_session)
115{
116 tls_session->cache->clear.state = FR_TLS_TICKET_STATEFUL_REQUESTED;
117 TLS_PENDING_SET(tls_session, FR_TLS_PENDING_STATEFUL_TICKET);
118}
119
120/** Delete session data be deleted from the cache
121 *
122 * @param[in] sess to be deleted.
123 */
124static void tls_ticket_stateful_delete_request(fr_tls_session_t *tls_session, SSL_SESSION *sess)
125{
126 fr_tls_ticket_stateful_t *tls_cache;
128
129 if (!tls_session->cache) return;
130
131 request = fr_tls_session_request(tls_session->ssl);
132 tls_cache = tls_session->cache;
133
134 /*
135 * Request was cancelled just return without doing any work.
136 */
138
139 fr_assert(tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_INIT);
140
141 /*
142 * Record the session to delete
143 */
144 if (tls_ticket_id_to_box(tls_cache, &tls_cache->clear.id, sess) < 0) {
145 RWDEBUG("(TLS) Error retrieving Session ID");
146 return;
147 }
148
149 RDEBUG3("(TLS) Session ID %pV - Requested session clear", &tls_cache->clear.id);
150
151 tls_ticket_stateful_clear_state_request(tls_session);
152
153 /*
154 * Reset any pending `store session`, so that we skip
155 * unnecessary work.
156 */
157 tls_ticket_stateful_store_state_reset(request, tls_cache);
158
159 /*
160 * We _usually_ store a copy of the SSL_SESSION in tls_session->session. If the
161 * session is being freed, then we invalidate the cached SSL_SESSION. Note that
162 * tls_session->session can be NULL sometimes, see tls_ticket_stateful_delete_cb().
163 *
164 * In any case, if the SSL_SESSION pointer exists, we clear it here to avoid leaving a dangling
165 * pointer.
166 */
167 if (tls_session->session) {
168 fr_assert(tls_session->session == sess);
169 tls_session->session = NULL;
170 }
171
172 /*
173 * Previously the code called ASYNC_pause_job();
174 * assuming this callback would always be called
175 * from SSL_read() or another SSL function.
176 *
177 * Unfortunately it appears that the call path
178 * can also be triggered with SSL_CTX_remove_session
179 * if the reference count on the SSL_SESSION
180 * drops to zero.
181 *
182 * We now check the 'can_pause' flag to determine
183 * if we're inside a yieldable SSL_read call.
184 */
185 if (tls_session->can_pause) ASYNC_pause_job();
186}
187
188/** Process the result of `load session { ... }`
189 */
190static unlang_action_t tls_ticket_stateful_load_resume(request_t *request, void *uctx)
191{
192 fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
193 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
194 fr_pair_t *vp;
195 uint8_t const *q, **p;
196 SSL_SESSION *sess;
197
198 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
199 if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) {
200 RWDEBUG("(TLS) Failed acquiring session data");
201
202 /*
203 * A cache miss answers `notfound`, and a miss is the
204 * normal answer on a first connection. Recording it as
205 * a failure would put an `Error` in the session-state
206 * list of every healthy handshake.
207 */
208 fr_tls_session_error_add(request->parent,
209 (vp && (vp->vp_uint32 == enum_tls_packet_type_notfound->vb_uint32)) ?
210 FR_ERROR_VALUE_LOAD_SESSION_NOT_FOUND :
211 FR_ERROR_VALUE_LOAD_SESSION_FAILED);
212 error:
213 tls_cache->load.state = FR_TLS_TICKET_STATEFUL_FAILED;
215 }
216
217 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_session_data);
218 if (!vp) {
219 RWDEBUG("(TLS) No cached session found");
220 fr_tls_session_error_add(request->parent, FR_ERROR_VALUE_LOAD_SESSION_NOT_FOUND);
221 goto error;
222 }
223
224 q = vp->vp_octets; /* openssl will mutate q, so we can't use vp_octets directly */
225 p = (unsigned char const **)&q;
226
227 sess = d2i_SSL_SESSION(NULL, p, vp->vp_length);
228 if (!sess) {
229 fr_tls_log_perror(request, "Failed loading persisted session");
230 fr_tls_session_error_add(request->parent, FR_ERROR_VALUE_LOAD_SESSION_MALFORMED);
231 goto error;
232 }
233
234 tls_session_id_cache(tls_session, sess); /* the client path has no ID until now */
235
236 if (RDEBUG_ENABLED3) {
237 RDEBUG3("(TLS) Session ID %pV - Read %zu bytes of data. "
238 "Session de-serialized successfully", &tls_session->session_id, vp->vp_length);
239 SSL_SESSION_print(fr_tls_request_log_bio(request, L_DBG, L_DBG_LVL_3), sess);
240 }
241
242 /*
243 * Enforce session timeouts. We don't resume sessions which have exceeded their lifetime.
244 *
245 * The lifetime is already enforced for `store session`. The session may exist on disk (or in a
246 * DB) for long enough that it expires.
247 *
248 * tls_ticket_session_lifetime() clamps to the lowest of the session's own lifetime, the
249 * configured `lifetime`, and the RFC maximum. Clamping to the configured value is what lets
250 * a policy update reach sessions which were stored before it.
251 */
252 {
253 fr_tls_conf_t *conf = tls_session->conf;
254
255 if (!tls_ticket_session_resumable(request, &tls_session->session_id, conf, sess)) {
256 RWDEBUG("(TLS) Session ID %pV - Cached session has too little life left, not resuming",
257 &tls_session->session_id);
258 fr_tls_session_error_add(request, FR_ERROR_VALUE_LOAD_SESSION_EXPIRED);
259
260 /*
261 * The session was allocated by d2i_SSL_SESSION(), and it is not yet saved in
262 * tls_cache->load.sess. We're the only one who knows about it, so we have to
263 * free it.
264 */
265 SSL_SESSION_free(sess);
266 goto error;
267 }
268 }
269
270 /*
271 * OpenSSL's API is very inconsistent.
272 *
273 * We need to set external data here, so it can be
274 * retrieved in tls_ticket_stateful_delete_cb().
275 *
276 * ex_data is not serialised in i2d_SSL_SESSION
277 * so we don't have to bother unsetting it.
278 */
279 SSL_SESSION_set_ex_data(sess, fr_tls_session_ex_index, fr_tls_session(tls_session->ssl));
280
281 tls_cache->load.state = FR_TLS_TICKET_STATEFUL_SUCCESS;
282 tls_cache->load.sess = sess; /* This is consumed in tls_ticket_stateful_load_cb */
283
284 /*
285 * Remember that we loaded an entry from the session
286 * cache. If the session eventually fails, we then know
287 * that we have to remove the failed cache entry.
288 */
289 tls_cache->loaded = true;
290
292}
293
294/** Push a `load session { ... }` call into the current request, using a subrequest
295 *
296 * @param[in] request The current request.
297 * @param[in] tls_session The current TLS session.
298 * @return
299 * - UNLANG_ACTION_CALCULATE_RESULT on noop.
300 * - UNLANG_ACTION_PUSHED_CHILD on success.
301 * - UNLANG_ACTION_FAIL on failure.
302 */
303static unlang_action_t tls_ticket_stateful_load_push(request_t *request, fr_tls_session_t *tls_session)
304{
305 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
306 fr_tls_conf_t *conf = tls_session->conf;
307 request_t *child;
309
310 if (TLS_TICKET_STATEFUL_DISABLED) return UNLANG_ACTION_CALCULATE_RESULT;
311
312 if (tls_cache->load.state != FR_TLS_TICKET_STATEFUL_REQUESTED) return UNLANG_ACTION_CALCULATE_RESULT;
313
314 /*
315 * Reset any pending `load session` if there is also a
316 * pending `clear session`, and mark up the load as failed.
317 *
318 * The load is stuck in an async callback via
319 * ASYNC_pause_job(), so we can't reset the load state
320 * here. When it runs, the callback sees that the load
321 * failed, tells OpenSSL that there is no session, and the
322 * peer does a full handshake instead.
323 */
324 if (tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
325 RDEBUG3("(TLS) Session ID %pV - Clear is pending, skipping `load session { ... }`",
326 &tls_cache->load.id);
327 tls_cache->load.state = FR_TLS_TICKET_STATEFUL_FAILED;
329 }
330
331 fr_assert(!fr_type_is_null(tls_cache->load.id.type));
332
333 MEM(child = tls_subrequest_alloc(request, enum_tls_packet_type_load_session->vb_uint32,
334 &tls_cache->load.id));
335
336 /*
337 * Allocate a child, and set it up to call
338 * the TLS virtual server.
339 */
340 ua = fr_tls_call_push(child, tls_ticket_stateful_load_resume, conf, tls_session, true);
341 if (ua == UNLANG_ACTION_FAIL) {
342 talloc_free(child);
343 tls_ticket_stateful_load_state_reset(request, tls_cache);
344 return UNLANG_ACTION_FAIL;
345 }
346
347 return ua;
348}
349
350/** Process the result of `store session { ... }`
351 */
352static unlang_action_t tls_ticket_stateful_store_resume(request_t *request, void *uctx)
353{
354 fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
355 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
356 fr_pair_t *vp;
357
358 tls_ticket_stateful_store_state_reset(request, tls_cache);
359
360 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
361 if (vp && (vp->vp_uint32 == enum_tls_packet_type_success->vb_uint32)) {
362 tls_cache->store.state = FR_TLS_TICKET_STATEFUL_SUCCESS; /* Avoid spurious clear calls */
363 } else {
364 RWDEBUG("(TLS) Failed storing session data");
365 fr_tls_session_error_add(request->parent, FR_ERROR_VALUE_STORE_SESSION_FAILED);
366 tls_cache->store.state = FR_TLS_TICKET_STATEFUL_INIT;
367 }
368
370}
371
372/** Push a `store session { ... }` call into the current request, using a subrequest
373 *
374 * @param[in] request The current request.
375 * @param[in] conf TLS configuration.
376 * @param[in] tls_session The current TLS session.
377 * @return
378 * - UNLANG_ACTION_CALCULATE_RESULT on noop.
379 * - UNLANG_ACTION_PUSHED_CHILD on success.
380 * - UNLANG_ACTION_FAIL on failure.
381 */
382static inline CC_HINT(always_inline)
383unlang_action_t tls_ticket_stateful_store_push(request_t *request, fr_tls_conf_t *conf, fr_tls_session_t *tls_session)
384{
385 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
386 size_t len, ret;
387 int rcode;
388
389 uint8_t *p, *data = NULL;
390
391 request_t *child;
392 fr_pair_t *vp;
393 SSL_SESSION *sess = tls_session->cache->store.sess;
395 fr_time_delta_t ttl;
396
397 if (TLS_TICKET_STATEFUL_DISABLED) return UNLANG_ACTION_CALCULATE_RESULT;
398
399 fr_assert(tls_cache->store.sess);
400 fr_assert(tls_cache->store.state == FR_TLS_TICKET_STATEFUL_REQUESTED);
401
402 /*
403 * If there's a pending clear, then don't push any load /
404 * save / etc. This isn't strictly necessary, but is
405 * good "defense in depth" for any future code changes.
406 * It also documents / enforces our expectations.
407 */
408 if (tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
409 RWDEBUG("(TLS) Session ID %pV - Clear is pending, not storing", &tls_cache->store.id);
410 fr_tls_session_error_add(request, FR_ERROR_VALUE_STORE_CANCELLED_BY_CLEAR);
411 tls_ticket_stateful_store_state_reset(request, tls_cache);
413 }
414
415 ttl = tls_ticket_session_lifetime(request, &tls_cache->store.id, conf, sess);
416 if (!fr_time_delta_ispos(ttl)) {
417 RWDEBUG("(TLS) Session ID %pV - Session has already expired, not storing", &tls_cache->store.id);
418 fr_tls_session_error_add(request, FR_ERROR_VALUE_STORE_SESSION_EXPIRED);
420 }
421
422 /*
423 * Add the current session-state list
424 * contents to the ssl-data
425 */
426 rcode = tls_ticket_app_data_set(request, sess, &tls_cache->store.id);
427 if (rcode < 0) {
428 tls_ticket_stateful_store_state_reset(request, tls_cache);
429 return UNLANG_ACTION_FAIL;
430 }
431
432 if (rcode == 0) return UNLANG_ACTION_CALCULATE_RESULT;
433
434 MEM(child = tls_subrequest_alloc(request, enum_tls_packet_type_store_session->vb_uint32,
435 &tls_cache->store.id));
436 request = child;
437
438 /*
439 * How long the session has to live. Already clamped, the RFC
440 * maximum included, by tls_ticket_session_lifetime() above.
441 */
443 vp->vp_time_delta = ttl;
444
445 /*
446 * Serialize the session
447 */
448 ret = i2d_SSL_SESSION(sess, NULL); /* find out what length data we need */
449 if (ret < 1) {
450 /* something went wrong */
451 fr_tls_strerror_printf(NULL); /* Drain the OpenSSL error stack */
452 RPWDEBUG("(TLS) Session ID %pV - Serialisation failed, couldn't determine "
453 "required buffer length", &tls_cache->store.id);
454 error:
455 tls_ticket_stateful_store_state_reset(request, tls_cache);
456 talloc_free(child);
457 return UNLANG_ACTION_FAIL;
458 }
459 len = ret;
460
462 MEM(data = talloc_array(vp, uint8_t, len));
463
464 /* openssl mutates &p */
465 p = data;
466 ret = i2d_SSL_SESSION(sess, &p); /* Serialize as ASN.1 */
467 if (ret != len) {
468 fr_tls_strerror_printf(NULL); /* Drain the OpenSSL error stack */
469 RPWDEBUG("(TLS) Session ID %pV - Serialisation failed", &tls_cache->store.id);
471 goto error;
472 }
474
475 /*
476 * Allocate a child, and set it up to call
477 * the TLS virtual server.
478 */
479 ua = fr_tls_call_push(child, tls_ticket_stateful_store_resume, conf, tls_session, true);
480 if (ua == UNLANG_ACTION_FAIL) goto error;
481
482 return ua;
483}
484
485/** Process the result of `clear session { ... }`
486 */
487static unlang_action_t tls_ticket_stateful_clear_resume(request_t *request, void *uctx)
488{
489 fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
490 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
491 fr_pair_t *vp;
492
493 tls_ticket_stateful_clear_state_reset(request, tls_cache);
494
495 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
496 if (vp &&
497 ((vp->vp_uint32 == enum_tls_packet_type_success->vb_uint32) ||
498 (vp->vp_uint32 == enum_tls_packet_type_notfound->vb_uint32))) {
500 }
501
502 RWDEBUG("(TLS) Failed deleting session data - security may be compromised");
503 fr_tls_session_error_add(request->parent, FR_ERROR_VALUE_CLEAR_SESSION_FAILED);
505}
506
507/** Push a `clear session { ... }` call into the current request, using a subrequest
508 *
509 * @param[in] request The current request.
510 * @param[in] conf TLS configuration.
511 * @param[in] tls_session The current TLS session.
512 * @return
513 * - UNLANG_ACTION_CALCULATE_RESULT on noop.
514 * - UNLANG_ACTION_PUSHED_CHILD on success.
515 * - UNLANG_ACTION_FAIL on failure.
516 */
517static inline CC_HINT(always_inline)
518unlang_action_t tls_ticket_stateful_clear_push(request_t *request, fr_tls_conf_t *conf, fr_tls_session_t *tls_session)
519{
520 request_t *child;
521 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
523
524 if (TLS_TICKET_STATEFUL_DISABLED) return UNLANG_ACTION_CALCULATE_RESULT;
525
526 fr_assert(tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_REQUESTED);
527 fr_assert(!fr_type_is_null(tls_cache->clear.id.type));
528
529 MEM(child = tls_subrequest_alloc(request, enum_tls_packet_type_clear_session->vb_uint32,
530 &tls_cache->clear.id));
531
532 /*
533 * Allocate a child, and set it up to call
534 * the TLS virtual server.
535 */
536 ua = fr_tls_call_push(child, tls_ticket_stateful_clear_resume, conf, tls_session, true);
537 if (ua == UNLANG_ACTION_FAIL) {
538 talloc_free(child);
539 tls_ticket_stateful_clear_state_reset(request, tls_cache);
540 return UNLANG_ACTION_FAIL;
541 }
542
543 return ua;
544}
545
546/** Process the result of a client's `load session { ... }` call
547 *
548 * A client hands the session to OpenSSL before the handshake starts. The
549 * server path differs: OpenSSL hands a server the session ID part way through
550 * a handshake, and the server returns the matching session.
551 */
552static unlang_action_t tls_ticket_stateful_load_client_resume(request_t *request, void *uctx)
553{
554 fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
555 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
556
557 (void) tls_ticket_stateful_load_resume(request, uctx);
558
559 if (tls_cache->load.state != FR_TLS_TICKET_STATEFUL_SUCCESS) {
560 RDEBUG2("(TLS) No session to resume");
562 }
563
564 if (SSL_set_session(tls_session->ssl, tls_cache->load.sess) != 1) {
565 fr_tls_log_perror(request, "Failed setting the session to resume");
566 tls_ticket_stateful_load_state_reset(request, tls_cache);
568 }
569
570 RDEBUG2("(TLS) Offering the cached session for resumption");
571
572 /*
573 * SSL_set_session() takes its own reference, and nothing else
574 * consumes this one, unlike the server path where the session is
575 * handed back to OpenSSL from tls_ticket_stateful_load_cb.
576 */
577 tls_ticket_stateful_load_state_reset(request, tls_cache);
578
580}
581
582/** Ask the virtual server for a session to resume, as a client
583 *
584 * Call this before the handshake starts. A client chooses which session to
585 * offer, so the client looks a session up under a key the client already
586 * knows, the expansion of `session { name = ... }`. A server makes no such
587 * choice. OpenSSL hands the server the session ID the peer asked for, so the
588 * server path lives in tls_ticket_stateful_load_cb().
589 *
590 * The policy decides what the key means. `TLS-Session-Id` holds the expanded
591 * name, and the `load session { ... }` section may look the session up by
592 * `TLS-Session-Id`, or by any other attribute in the request.
593 *
594 * @param[in] request The current request.
595 * @param[in] tls_session The current TLS session.
596 * @return
597 * - UNLANG_ACTION_CALCULATE_RESULT if there is nothing to do.
598 * - UNLANG_ACTION_PUSHED_CHILD on success.
599 * - UNLANG_ACTION_FAIL on failure.
600 */
601unlang_action_t fr_tls_ticket_stateful_load_client_push(request_t *request, fr_tls_session_t *tls_session)
602{
603 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
604 fr_tls_conf_t *conf = tls_session->conf;
605 char *name;
606 request_t *child;
608
609 if (TLS_TICKET_STATEFUL_DISABLED) return UNLANG_ACTION_CALCULATE_RESULT;
610 if (!tls_session->allow_session_resumption) return UNLANG_ACTION_CALCULATE_RESULT;
611
612 fr_assert(conf->cache.id_name);
613
614 if (tmpl_aexpand(tls_session, &name, request, conf->cache.id_name, NULL, NULL) < 0) {
615 RPEDEBUG("(TLS) Failed expanding the session name");
616 return UNLANG_ACTION_FAIL;
617 }
618
619 MEM(child = tls_subrequest_alloc(request, enum_tls_packet_type_load_session->vb_uint32,
621
623
624 ua = fr_tls_call_push(child, tls_ticket_stateful_load_client_resume, conf, tls_session, true);
625 if (ua == UNLANG_ACTION_FAIL) {
626 talloc_free(child);
627 return UNLANG_ACTION_FAIL;
628 }
629
630 return ua;
631}
632
633/** Push a `store session { ... }` or `clear session { ... }` or `load session { ... }`
634 *
635 * Depending on what operation is needed.
636 *
637 * @param[in] request The current request.
638 * @param[in] tls_session The current TLS session.
639 * @return
640 * - UNLANG_ACTION_CALCULATE_RESULT - No pending actions
641 * - UNLANG_ACTION_PUSHED_CHILD - Pending operations to evaluate.
642 */
643unlang_action_t fr_tls_ticket_stateful_pending_push(request_t *request, fr_tls_session_t *tls_session)
644{
645 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
646 fr_tls_conf_t *conf = tls_session->conf;
648
649 if (!tls_cache) return UNLANG_ACTION_CALCULATE_RESULT; /* No caching allowed, nothing to discard */
650
651 /*
652 * The caller is asking us to push load / store / etc. Since the cache is disabled, we just
653 * reset the state (and free resources), then return. The callers can then check
654 * fr_tls_ticket_stateful_pending(), which will now return "nope".
655 */
656 if (TLS_TICKET_STATEFUL_DISABLED) {
657 if (tls_cache->load.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
658 tls_ticket_stateful_load_state_reset(request, tls_cache);
659 }
660 if (tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
661 tls_ticket_stateful_clear_state_reset(request, tls_cache);
662 }
663 if (tls_cache->store.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
664 tls_ticket_stateful_store_state_reset(request, tls_cache);
665 }
667 }
668
669 /*
670 * Load stateful session data.
671 *
672 * tls_ticket_stateful_load_push() may return
673 * UNLANG_ACTION_CALCULATE_RESULT there's a pending
674 * `clear session`. When that happens, we skip the load,
675 * and run the clear instead.
676 */
677 if (tls_cache->load.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
678 ua = tls_ticket_stateful_load_push(request, tls_session);
679 if (ua != UNLANG_ACTION_CALCULATE_RESULT) return ua;
680 }
681
682 /*
683 * We only support a single session
684 * ticket currently...
685 */
686 if (tls_cache->clear.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
687 /*
688 * Enforce that there's no queued store, as it
689 * should have been cancelled.
690 */
691 fr_assert(tls_cache->store.state != FR_TLS_TICKET_STATEFUL_REQUESTED);
692 tls_ticket_stateful_store_state_reset(request, tls_cache);
693
694 return tls_ticket_stateful_clear_push(request, conf, tls_session);
695 }
696
697 if (tls_cache->store.state == FR_TLS_TICKET_STATEFUL_REQUESTED) {
698 return tls_ticket_stateful_store_push(request, conf, tls_session);
699 }
700
702}
703
704/** Run all queued cache operations
705 *
706 * Multiple operations may be pushed at the same time. We set
707 * ourselves as the resume function before each push, so that when the
708 * queued operation is done, we can check for another one, and run it.
709 *
710 * @param[in] request to run the cache sections in.
711 * @param[in] uctx the #fr_tls_session_t whose queued operations to run.
712 * @return
713 * - UNLANG_ACTION_PUSHED_CHILD - a cache section is running.
714 * - UNLANG_ACTION_CALCULATE_RESULT - no operation is left.
715 */
716static unlang_action_t tls_ticket_stateful_drain(request_t *request, void *uctx)
717{
718 fr_tls_session_t *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
720
721 if (!fr_tls_ticket_stateful_pending(tls_session->cache)) return UNLANG_ACTION_CALCULATE_RESULT;
722
723 /*
724 * Set our repeat before any child is pushed.
725 */
726 if (unlikely(unlang_function_repeat_set(request, tls_ticket_stateful_drain) < 0)) return UNLANG_ACTION_FAIL;
727
728 ua = fr_tls_ticket_stateful_pending_push(request, tls_session);
729 if (ua == UNLANG_ACTION_PUSHED_CHILD) return ua;
730
731 /*
732 * We didn't push anything. Clear the repeat.
733 */
735
736 /*
737 * On error, log the failure and continue.
738 */
739 if (ua == UNLANG_ACTION_FAIL) {
740 REDEBUG("(TLS) Failed running session cache operations");
742 }
743
744 return ua;
745}
746
747/** Push a frame which runs every queued cache operation
748 *
749 * @param[in] request to run the cache sections in.
750 * @param[in] tls_session whose queued operations to run.
751 * @return
752 * - UNLANG_ACTION_PUSHED_CHILD - the queued operations are running.
753 * - UNLANG_ACTION_CALCULATE_RESULT - no operation was queued.
754 * - UNLANG_ACTION_FAIL - the frame could not be pushed.
755 */
756static unlang_action_t tls_ticket_stateful_drain_push(request_t *request, fr_tls_session_t *tls_session)
757{
758 if (!fr_tls_ticket_stateful_pending(tls_session->cache)) return UNLANG_ACTION_CALCULATE_RESULT;
759
760 return unlang_function_push(request, tls_ticket_stateful_drain, NULL, NULL, FR_SIGNAL_NONE, UNLANG_SUB_FRAME, tls_session);
761}
762
763/** Store a session after a successful authentication
764 *
765 * Remember to run `store session { ... }`.
766 *
767 * Just finishing the TLS handshake is not always enough. EAP runs
768 * inner methods inside of the TLS tunnel which can fail. So we only
769 * run `store session` after the inner method succeeds.
770 *
771 * @note The caller MUST set the caller's own unlang result before calling
772 * fr_tls_ticket_stateful_store_session(). A pushed child returns to the caller's
773 * caller with that result already in place.
774 *
775 * @param[in] request to run the cache sections in.
776 * @param[in] tls_session to keep.
777 * @return
778 * - UNLANG_ACTION_PUSHED_CHILD - cache sections are running.
779 * - UNLANG_ACTION_CALCULATE_RESULT - there was nothing to do.
780 * - UNLANG_ACTION_FAIL - the frame could not be pushed.
781 */
782unlang_action_t fr_tls_ticket_stateful_store_session(request_t *request, fr_tls_session_t *tls_session)
783{
784 return tls_ticket_stateful_drain_push(request, tls_session);
785}
786
787/** Clear a session after a failed authentication
788 *
789 * Cancels any pending `store session`, and if necessary, remember to run `clear session { ... }` as the
790 * session might have been loaded from the cache.
791 *
792 * @note The caller MUST set the caller's own unlang result before calling
793 * fr_tls_ticket_stateful_clear_session(). A pushed child returns to the
794 * caller's caller with that result already in place.
795 *
796 * @param[in] request to run the cache sections in.
797 * @param[in] tls_session to discard.
798 * @return
799 * - UNLANG_ACTION_PUSHED_CHILD - cache sections are running.
800 * - UNLANG_ACTION_CALCULATE_RESULT - there was nothing to do.
801 * - UNLANG_ACTION_FAIL - the frame could not be pushed.
802 */
803unlang_action_t fr_tls_ticket_stateful_clear_session(request_t *request, fr_tls_session_t *tls_session)
804{
805 fr_tls_ticket_stateful_t *tls_cache = tls_session->cache;
806 bool bound;
807
808 /*
809 * No caching allowed, so there is nothing to deny, and
810 * nothing to clear.
811 */
812 if (!tls_cache) return UNLANG_ACTION_CALCULATE_RESULT;
813
814 bound = fr_tls_session_request_bound(tls_session->ssl);
815
816 /*
817 * This is necessary to allow this function to
818 * be called inside and outside of OpenSSL handshake
819 * code.
820 */
821 if (!bound) {
822 fr_tls_session_request_bind(tls_session->ssl, request);
823
824 } else {
825 /*
826 * If there's already a request bound, it better be
827 * the one passed to this function.
828 */
829 fr_assert(fr_tls_session_request(tls_session->ssl) == request);
830 }
831
832 /*
833 * SSL_CTX_remove_session() frees the previously loaded session in tls_session. If the reference
834 * count reaches zero the SSL_CTX_sess_remove_cb is called, which in our code is
835 * tls_ticket_stateful_delete_cb. HOWEVER, we've already set SSL_SESS_CACHE_NO_INTERNAL, which means that
836 * SSL_CTX_remove_session() largely does nothing, and skips our callback. These checks are
837 * largely for paranoia, just in case the internal OpenSSL cache is somehow re-enabled.
838 *
839 * tls_ticket_stateful_delete_cb calls tls_ticket_stateful_delete_request to record the ID of tls_session->session in
840 * our pending cache state structure.
841 *
842 * tls_ticket_stateful_delete_request does NOT immediately call `clear session {}` as that must
843 * be done in a code area which can return a yield to the interpreter.
844 */
845 if (tls_session->session) {
846 SSL_CTX_remove_session(tls_session->ctx, tls_session->session);
847
848 /*
849 * Manually call tls_ticket_stateful_delete_request(), just in case. That function clears
850 * tls_session->session, so it's idempotent. It clears tls_session->session, so it's
851 * safe to call twice. If it's called via the above path, then it clears the session
852 * pointer, which means that we don't call it again.
853 */
854 if (tls_session->session && tls_cache->loaded) tls_ticket_stateful_delete_request(tls_session, tls_session->session);
855 }
856 tls_session->allow_session_resumption = false;
857
858 /*
859 * Clear any pending store requests.
860 */
861 tls_ticket_stateful_store_state_reset(request, tls_cache);
862
863 /*
864 * The request wasn't bound when we were called, so unbind it now.
865 */
866 if (!bound) fr_tls_session_request_unbind(tls_session->ssl);
867
868 return tls_ticket_stateful_drain_push(request, tls_session);
869}
870
871/** Write a newly created session data to the tls_session->cache structure
872 *
873 * @note If you hit an assert in this function, it was likely called twice, which shouldn't happen
874 * so blame OpenSSL.
875 *
876 * @param[in] ssl session state.
877 * @param[in] sess to serialise and write to the cache.
878 * @return
879 * - 1. What we return is not used by OpenSSL to indicate success
880 * or failure, but to indicate whether it should free its copy of
881 * the session data.
882 * In this case we tell it not to free the session data, as we
883 */
884static int tls_ticket_stateful_store_cb(SSL *ssl, SSL_SESSION *sess)
885{
887 fr_tls_session_t *tls_session;
888 fr_tls_ticket_stateful_t *tls_cache;
889
890 /*
891 * This functions should only be called once during the lifetime
892 * of the tls_session, as the fields aren't re-populated on
893 * resumption.
894 */
895 tls_session = fr_tls_session(ssl);
896 request = fr_tls_session_request(tls_session->ssl);
897
898 /*
899 * OpenSSL runs this callback when a session is established or
900 * when a NewSessionTicket arrives. Both happen inside
901 * fr_tls_session_recv(), fr_tls_session_send() or a handshake
902 * round, and each of those binds the request across the call.
903 */
904 fr_assert(request != NULL);
905
906 /*
907 * When handling TLS 1.3, if they are enabled OpenSSL calls both the callback to create stateless
908 * session tickets and this one. If a stateless ticket has been created, we don't need to store
909 * the data in a stateful store as well.
910 * TLS 1.2 handling does the right thing.
911 */
912 if (tls_session->session_ticket_created) {
913 RDEBUG2("(TLS) Stateless ticket already created, not performing stateful store");
914 return 0;
915 }
916
917 /*
918 * RFC 8446 Section 4.6.1 lets a TLS 1.3 server send NewSessionTicket at any point after its
919 * Finished message, including part way through application data. We do not support that.
920 *
921 * Once application data is moving, we discard any session tickets that we receive. TLS doesn't
922 * require us to do this, but we don't know what else to do. The application is processing it's
923 * data, and we do not (as yet) have the code to pause the application, and run a subrequest to
924 * store the new ticket.
925 *
926 * The current implementation runs the cache sections from the handshake, which is the only place
927 * the interpreter can yield. tls_ticket_stateful_store_cb() reaches them through ASYNC_pause_job(), and
928 * that is only legal while tls_session->can_pause is set. Fixing this is an issue for the
929 * future.
930 *
931 * One possible solution is to spawn a _detached_ subrequest which processes the ticket, and only
932 * the ticket. This has to be a subrequest in order to avoid confusing the main application
933 * request (and state machine) with TLS data. We don't care if the `store session` succeeded or
934 * failed, because there's nothing we can really do with that result. The connection is likely
935 * to stay up, so we might as well just ignore errors on `store session`.
936 *
937 * For those reasons and more, we just discard the session ticket. All this means is that the
938 * peer (client here) is unable to use that ticket for session resumption.
939 */
940 if (tls_session->seen_application_data) {
941 RDEBUG2("(TLS) Ignoring session ticket received after application data started");
942 return 0;
943 }
944
945 /*
946 * We have a session ticket. See fr_tls_session_is_init_finished()
947 */
948 tls_session->session_ticket_received = true;
949
950 /*
951 * A server sees this callback once, before anything else has
952 * recorded a session.
953 *
954 * A client has already recorded one: SSL_get_session() runs
955 * when OpenSSL finishes the handshake, which is before the
956 * NewSessionTicket arrives here. The ticket's session is the
957 * one worth keeping, because it is what the client offers
958 * back, so it supersedes.
959 */
960 fr_assert(!tls_session->session || !SSL_is_server(ssl));
961 tls_session->session = sess;
962 tls_session_id_cache(tls_session, sess);
963
964 tls_cache = tls_session->cache;
965
966 fr_assert(tls_cache);
967 fr_assert(tls_session->conf->virtual_server);
968
969 /*
970 * Request was cancelled, just get OpenSSL to
971 * free the session data, and don't do any work.
972 */
974
975 /*
976 * Take a copy of the ID, because "return 0" tells
977 * OpenSSL that it can delete the session. Which means
978 * that the cached pointer to the session ID could be
979 * freed.
980 *
981 * We may need to use the ID in the `store session` even
982 * after the session is gone.
983 */
984 if (tls_ticket_id_to_box(tls_cache, &tls_cache->store.id, sess) < 0) {
985 RDEBUG3("(TLS) No Session ID to store");
986 return 0;
987 }
988
989 RDEBUG3("(TLS) Session ID %pV - Requested store", &tls_cache->store.id);
990
991 /*
992 * Store the session blob and session id for writing
993 * later, once all the authentication phases have completed.
994 */
995 tls_cache->store.sess = sess;
996 tls_ticket_stateful_store_state_request(tls_session);
997
998 return 1;
999}
1000
1001/** Read session data from the cache
1002 *
1003 * @param[in] ssl session state.
1004 * @param[in] key to retrieve session data for.
1005 * @param[in] key_len The length of the key.
1006 * @param[out] copy Indicates whether OpenSSL should increment the reference
1007 * count on SSL_SESSION to prevent it being automatically freed. We always
1008 * set this to 0.
1009 * @return
1010 * - Deserialised session data on success.
1011 * - NULL on error.
1012 */
1013static SSL_SESSION *tls_ticket_stateful_load_cb(SSL *ssl,
1014 unsigned char const *key,
1015 int key_len, int *copy)
1016{
1017 fr_tls_session_t *tls_session;
1018 fr_tls_ticket_stateful_t *tls_cache;
1020
1021 tls_session = fr_tls_session(ssl);
1022 request = fr_tls_session_request(tls_session->ssl);
1023 tls_cache = tls_session->cache;
1024
1025 fr_assert(tls_cache);
1026 fr_assert(tls_session->conf->virtual_server);
1027
1028 /*
1029 * The request was cancelled. Do not return a session, and
1030 * let OpenSSL fall back to a full handshake.
1031 */
1032 if (unlang_request_is_cancelled(request)) return NULL;
1033
1034 /*
1035 * Never return a session when session resumption is
1036 * disallowed.
1037 */
1038 if (!tls_cache || !tls_session->allow_session_resumption) return NULL;
1039
1040 /*
1041 * OpenSSL runs the handshake, including this callback, on
1042 * one of its fibers (a 32K micro stack by default).
1043 *
1044 * So that we don't have silent memory corruption we ensure
1045 * all the heavy lifting and unlang execution occurs on the
1046 * main thread, and never on the fiber.
1047 *
1048 * 1. On the first call, the callback records the session
1049 * ID, marks the section as pending, and pauses the
1050 * handshake.
1051 * 2. Control returns to
1052 * tls_session_async_handshake_cont(), which pushes the
1053 * section with fr_tls_ticket_stateful_pending_push().
1054 * 3. Once the section has finished,
1055 * tls_session_async_handshake_cont() resumes the
1056 * handshake, and execution continues after the first
1057 * ASYNC_pause_job() call below.
1058 * 4. The callback jumps back to the switch, where the load
1059 * state records whether the section found a session.
1060 * When the section found a session, the callback pauses
1061 * the handshake a second time, and
1062 * `verify certificate { ... }` re-validates the peer
1063 * certificate.
1064 */
1065again:
1066 switch (tls_cache->load.state) {
1067 case FR_TLS_TICKET_STATEFUL_INIT:
1068 fr_assert(fr_type_is_null(tls_cache->load.id.type));
1069
1070 tls_ticket_stateful_load_state_request(tls_session);
1071 MEM(fr_value_box_memdup(tls_cache, &tls_cache->load.id, NULL,
1072 (uint8_t const *)key, key_len, true) == 0);
1073
1074 /*
1075 * The key is the ID of the session that the peer
1076 * asks to resume, and the key stays the session ID
1077 * for the rest of the handshake. Caching the key
1078 * here keeps the log lines that print the ID
1079 * working after load.id is cleared.
1080 */
1081 if (fr_type_is_null(tls_session->session_id.type)) {
1082 MEM(fr_value_box_memdup(tls_session, &tls_session->session_id, NULL,
1083 (uint8_t const *)key, key_len, true) == 0);
1084 if (tls_cache) tls_cache->session_id = &tls_session->session_id;
1085 }
1086
1087 RDEBUG3("(TLS) Requested session load - ID %pV", &tls_cache->load.id);
1088
1089 /*
1090 * Cache functions are only allowed during the
1091 * handshake.
1092 *
1093 * FIXME: With TLS 1.3 session tickets can be sent
1094 * later. Technically every point where we call
1095 * SSL_read() may need to be a yield point.
1096 */
1097 if (unlikely(!tls_session->can_pause)) {
1098 cant_pause:
1099 fr_assert_msg("Unexpected call to %s. "
1100 "tls_session_async_handshake_cont must be in call stack", __FUNCTION__);
1101 return NULL;
1102 }
1103 /*
1104 * Jumps back to SSL_read() in session.c.
1105 *
1106 * If the request is cancelled, whatever was meant
1107 * to be done while the handshake was paused may
1108 * not have been completed.
1109 */
1110 ASYNC_pause_job();
1111
1112 /*
1113 * `load session { ... }` finished, but the request
1114 * was cancelled. Free any loaded session, reset
1115 * the load state, and tell OpenSSL that the load
1116 * failed.
1117 */
1119 tls_ticket_stateful_load_state_reset(request, tls_cache); /* Clears any loaded session data */
1120 return NULL;
1121
1122 }
1123 goto again;
1124
1125 case FR_TLS_TICKET_STATEFUL_REQUESTED:
1126 fr_assert(0); /* Called twice without attempting the load?! */
1127 tls_cache->load.state = FR_TLS_TICKET_STATEFUL_FAILED;
1128 break;
1129
1130 case FR_TLS_TICKET_STATEFUL_SUCCESS:
1131 {
1132 SSL_SESSION *sess;
1133
1134 /*
1135 * The handshake continues with the loaded session.
1136 * Cache the ID of the loaded session in
1137 * tls_session->session_id now. load.id is cleared
1138 * below.
1139 */
1140 tls_session_id_cache(tls_session, tls_cache->load.sess);
1141
1142 RDEBUG3("(TLS) Setting session data");
1143
1144 fr_value_box_clear(&tls_cache->load.id);
1145
1146 /*
1147 * The SSL_SESSION holds a copy of the peer's
1148 * certificate, but not the peer's certificate
1149 * chain. Re-validation needs the chain.
1150 * tls_ticket_app_data_get() therefore restores the
1151 * session-state list, which holds the certificate
1152 * pairs, from the application data stored with the
1153 * session.
1154 */
1155 if (tls_ticket_app_data_get(request, tls_cache->load.sess, &tls_session->session_id) < 0) {
1156 REDEBUG("(TLS) Denying session resumption via session-id");
1157 verify_error:
1158 /*
1159 * Request the `delete session { ... }`
1160 * section, which runs the next time the
1161 * handshake pauses.
1162 */
1163 tls_ticket_stateful_delete_request(tls_session, tls_cache->load.sess);
1164 tls_ticket_stateful_load_state_reset(request, tls_session->cache); /* Free the session */
1165 return NULL;
1166 }
1167
1168 /*
1169 * Mark `verify certificate { ... }` as pending,
1170 * for tls_session_async_handshake_cont() to push
1171 * once the handshake pauses below.
1172 */
1173 fr_tls_verify_resumed_request(tls_session);
1174
1175 if (unlikely(!tls_session->can_pause)) goto cant_pause;
1176 /*
1177 * Jumps back to SSL_read() in session.c.
1178 *
1179 * If the request is cancelled, whatever was meant
1180 * to be done while the handshake was paused may
1181 * not have been completed.
1182 */
1183 ASYNC_pause_job();
1184
1185 /*
1186 * `verify certificate { ... }` finished, but the
1187 * request was cancelled. Free any loaded session,
1188 * reset the load and verify states, and tell
1189 * OpenSSL that the load failed.
1190 */
1192 tls_ticket_stateful_load_state_reset(request, tls_cache); /* Clears any loaded session data */
1193 fr_tls_verify_cert_reset(tls_session);
1194 return NULL;
1195
1196 }
1197
1198 /*
1199 * The callback denies resumption when the peer
1200 * certificate fails re-validation.
1201 */
1202 if (!fr_tls_verify_cert_result(tls_session)) {
1203 RDEBUG2("(TLS) Certificate re-validation failed, denying session resumption via session-id");
1204 goto verify_error;
1205 }
1206 sess = tls_cache->load.sess;
1207
1208 /*
1209 * After we return it's OpenSSL's responsibility
1210 * to free the session data, so set our copy of
1211 * the pointer to NULL, to prevent a double free
1212 * on cleanup.
1213 */
1214 {
1215 RDEBUG3("(TLS) Session ID %pV - Session ownership transferred to libssl", &tls_session->session_id);
1216 *copy = 0;
1217 tls_cache->load.sess = NULL;
1218 }
1219 return sess;
1220 }
1221
1222
1223 case FR_TLS_TICKET_STATEFUL_FAILED:
1224 RDEBUG3("(TLS) Session data load failed");
1225 break;
1226 }
1227
1228 fr_value_box_clear(&tls_cache->load.id);
1229 fr_assert(!tls_cache->load.sess);
1230
1231 return NULL;
1232}
1233
1234/** Delete session data from the cache
1235 *
1236 * @param[in] ctx Current ssl context.
1237 * @param[in] sess to be deleted.
1238 */
1239static void tls_ticket_stateful_delete_cb(UNUSED SSL_CTX *ctx, SSL_SESSION *sess)
1240{
1241 fr_tls_session_t *tls_session;
1242
1243 /*
1244 * Not sure why this happens, but sometimes SSL_SESSION *s
1245 * make it here without the correct ex data.
1246 *
1247 * Maybe it's one OpenSSL created internally?
1248 */
1249 tls_session = SSL_SESSION_get_ex_data(sess, fr_tls_session_ex_index);
1250 if (!tls_session) return;
1251
1252 (void) talloc_get_type_abort(tls_session, fr_tls_session_t);
1253
1254 /*
1255 * Note that tls_session->session CAN be NULL here. That's because that field is set during the
1256 * TLS negotiation. If we get a rejection part way though the TLS negotiation, then the field
1257 * isn't set. But OpenSSL passes the SSL_SESSION to us here, so we use that.
1258 */
1259 tls_ticket_stateful_delete_request(tls_session, sess);
1260}
1261
1262/** Cleanup any memory allocated by OpenSSL
1263 */
1264static int _tls_ticket_stateful_free(fr_tls_ticket_stateful_t *tls_cache)
1265{
1266 tls_ticket_stateful_load_state_reset(NULL, tls_cache);
1267 tls_ticket_stateful_store_state_reset(NULL, tls_cache);
1268
1269 return 0;
1270}
1271
1272/** Allocate a session cache state structure, and assign it to a tls_session
1273 *
1274 * @note This must be called if session caching is enabled for a tls session.
1275 *
1276 * @param[in] tls_session to assign cache structure to.
1277 */
1278void fr_tls_ticket_stateful_session_alloc(fr_tls_session_t *tls_session)
1279{
1280 fr_assert(!tls_session->cache);
1281
1282 MEM(tls_session->cache = talloc_zero(tls_session, fr_tls_ticket_stateful_t));
1283 talloc_set_destructor(tls_session->cache, _tls_ticket_stateful_free);
1284}
1285
1286/** Disable stateful session resumption for a given TLS ctx
1287 *
1288 * @param[in] ctx to disable stateful session resumption for.
1289 */
1290void fr_tls_ticket_stateful_disable(SSL_CTX *ctx)
1291{
1292 /*
1293 * Only disables stateful session-resumption.
1294 *
1295 * As per Matt Caswell:
1296 *
1297 * SSL_SESS_CACHE_OFF, when called on the server,
1298 * disables caching of server side sessions.
1299 * It does not switch off resumption. Resumption can
1300 * still occur if a stateless session ticket is used
1301 * (even in TLSv1.2).
1302 */
1303 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
1304}
1305
1306/** Install the stateful session cache callbacks on an SSL_CTX
1307 *
1308 * The callbacks run `store session`, `load session` and `clear session`.
1309 * Internal lookups are disabled, so every lookup goes through the
1310 * callbacks. OpenSSL calls the store callback only for the role the
1311 * cache mode names, so a client context sets SSL_SESS_CACHE_CLIENT
1312 * rather than SSL_SESS_CACHE_SERVER.
1313 *
1314 * @param[in] ctx to install the callbacks on.
1315 * @param[in] cache_conf Session caching configuration, for the lifetime.
1316 * @param[in] client true when the context is for a client.
1317 */
1318void fr_tls_ticket_stateful_ctx_init(SSL_CTX *ctx, fr_tls_ticket_conf_t const *cache_conf, bool client)
1319{
1320 SSL_CTX_sess_set_new_cb(ctx, tls_ticket_stateful_store_cb);
1321 SSL_CTX_sess_set_get_cb(ctx, tls_ticket_stateful_load_cb);
1322 SSL_CTX_sess_set_remove_cb(ctx, tls_ticket_stateful_delete_cb);
1323
1324 SSL_CTX_set_session_cache_mode(ctx, (client ? SSL_SESS_CACHE_CLIENT : SSL_SESS_CACHE_SERVER) |
1325 SSL_SESS_CACHE_NO_INTERNAL);
1326
1327 /*
1328 * Controls the validity period of the stateful cache.
1329 */
1330 SSL_CTX_set_timeout(ctx, fr_time_delta_to_sec(cache_conf->lifetime));
1331}
1332#endif /* WITH_TLS */
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:40
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:38
static uint8_t request[4]
#define USES_APPLE_DEPRECATED_API
Definition build.h:590
#define RCSID(id)
Definition build.h:603
#define IGNORE(_expr, _type)
Definition build.h:627
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:248
#define MEM(x)
Definition debug.h:38
int unlang_function_clear(request_t *request)
Clear pending repeat function calls, and remove the signal handler.
Definition function.c:275
#define unlang_function_repeat_set(_request, _repeat)
Set a new repeat function for an existing function frame.
Definition function.h:110
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:181
talloc_free(hp)
bool unlang_request_is_cancelled(request_t const *request)
Return whether a request has been cancelled.
Definition interpret.c:1971
#define UNLANG_SUB_FRAME
Definition interpret.h:37
#define DEBUG3(_fmt,...)
Definition log.h:271
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition log.h:545
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:352
#define RDEBUG3(fmt,...)
Definition log.h:360
#define ROPTIONAL_ENABLED(_e_request, _e_global)
Check if a debug level is set by the request (if !NULL) or by the global log.
Definition log.h:559
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RPWDEBUG(fmt,...)
Definition log.h:383
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:264
fr_value_box_t const * enum_tls_packet_type_store_session
HIDDEN fr_dict_attr_t const * attr_tls_packet_type
fr_value_box_t const * enum_tls_packet_type_success
HIDDEN fr_dict_attr_t const * attr_tls_session_ttl
HIDDEN fr_dict_attr_t const * attr_tls_session_data
fr_value_box_t const * enum_tls_packet_type_load_session
fr_value_box_t const * enum_tls_packet_type_clear_session
fr_value_box_t const * enum_tls_packet_type_notfound
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition log.h:69
@ L_DBG
Only displayed when debugging is enabled.
Definition log.h:56
unsigned char uint8_t
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
int fr_pair_value_memdup_buffer_shallow(fr_pair_t *vp, uint8_t const *src, bool tainted)
Assign a talloced buffer to a "octets" type value pair.
Definition pair.c:2972
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
static char const * name
#define tmpl_aexpand(_ctx, _out, _request, _vpt, _escape, _escape_ctx)
Expand a tmpl to a C type, allocing a new buffer to hold the string.
Definition tmpl.h:1087
@ FR_SIGNAL_NONE
No signals.
Definition signal.h:39
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
#define fr_time_delta_ispos(_a)
Definition time.h:290
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
#define fr_type_is_null(_x)
Definition types.h:347
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:4448
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:5194
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
int nonnull(2, 5))
#define fr_box_octets(_val, _len)
Definition value.h:342