Coverage Report

Created: 2026-10-18 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/tls/ticket_stateless.c
Line
Count
Source
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: 377e6b880f38e5236fda421b1065302ba4b18557 $
19
 *
20
 * @file tls/ticket_stateless.c
21
 * @brief Stateless TLS session tickets, and the `encode session` and `decode session` sections
22
 *
23
 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24
 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
25
 */
26
RCSID("$Id: 377e6b880f38e5236fda421b1065302ba4b18557 $")
27
USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
28
29
#ifdef WITH_TLS
30
0
#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
#include <openssl/kdf.h>
50
51
/** Resume after processing `encode session { ... }` or `decode session { ... }`
52
 *
53
 * Check the result and return success / fail depending.
54
 */
55
static unlang_action_t tls_ticket_stateless_resume(request_t *request, void *uctx)
56
0
{
57
0
  fr_tls_session_t  *tls_session = talloc_get_type_abort(uctx, fr_tls_session_t);
58
0
  fr_pair_t   *vp;
59
60
0
  fr_assert((tls_session->ticket == FR_TLS_TICKET_STATELESS_ENCODE_REQUESTED) ||
61
0
      (tls_session->ticket == FR_TLS_TICKET_STATELESS_DECODE_REQUESTED));
62
63
0
  vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_tls_packet_type);
64
0
  if (!vp || (vp->vp_uint32 != enum_tls_packet_type_success->vb_uint32)) {
65
0
    tls_session->ticket = FR_TLS_TICKET_STATELESS_FAILED;
66
0
    return UNLANG_ACTION_CALCULATE_RESULT;
67
0
  }
68
69
0
  tls_session->ticket = FR_TLS_TICKET_STATELESS_SUCCESS;
70
71
0
  return UNLANG_ACTION_CALCULATE_RESULT;
72
0
}
73
74
/** Push `encode session { ... }` or `decode session { ... }`
75
 *
76
 * @param[in] request   The current request.
77
 * @param[in] tls_session The current TLS session.
78
 * @param[in] packet_type Which of the two sections to run.
79
 * @return
80
 *  - UNLANG_ACTION_PUSHED_CHILD on success.
81
 *  - UNLANG_ACTION_FAIL on failure.
82
 */
83
static unlang_action_t tls_ticket_stateless_push(request_t *request, fr_tls_session_t *tls_session,
84
               uint32_t packet_type)
85
0
{
86
0
  fr_tls_conf_t *conf = tls_session->conf;
87
0
  request_t *child;
88
0
  unlang_action_t ua;
89
90
0
  fr_assert(conf->virtual_server);
91
92
0
  MEM(child = tls_subrequest_alloc(request, packet_type, &tls_session->session_id));
93
94
0
  fr_tls_session_extra_pairs_copy_to_child(child, tls_session);
95
96
0
  ua = fr_tls_call_push(child, tls_ticket_stateless_resume, conf, tls_session, false);
97
0
  if (ua == UNLANG_ACTION_FAIL) {
98
0
    REDEBUG("(TLS) Failed calling TLS virtual server");
99
0
    talloc_free(child);
100
0
    return UNLANG_ACTION_FAIL;
101
0
  }
102
103
0
  return ua;
104
0
}
105
106
/** Push `encode session` or `decode session`, depending on what's needed.
107
 *
108
 * @param[in] request   The current request.
109
 * @param[in] tls_session The current TLS session.
110
 * @return
111
 *  - UNLANG_ACTION_CALCULATE_RESULT  - nothing was pending.
112
 *  - UNLANG_ACTION_PUSHED_CHILD    - a section is running.
113
 *  - UNLANG_ACTION_FAIL      - the frame could not be pushed.
114
 */
115
unlang_action_t fr_tls_ticket_stateless_pending_push(request_t *request, fr_tls_session_t *tls_session)
116
0
{
117
0
  switch (tls_session->ticket) {
118
0
  case FR_TLS_TICKET_STATELESS_ENCODE_REQUESTED:
119
0
    return tls_ticket_stateless_push(request, tls_session,
120
0
               enum_tls_packet_type_encode_session->vb_uint32);
121
122
0
  case FR_TLS_TICKET_STATELESS_DECODE_REQUESTED:
123
0
    return tls_ticket_stateless_push(request, tls_session,
124
0
               enum_tls_packet_type_decode_session->vb_uint32);
125
126
0
  default:
127
0
    return UNLANG_ACTION_CALCULATE_RESULT;
128
0
  }
129
0
}
130
131
/** Request `encode session { ... }`, and tell the handshake the section is pending
132
 */
133
static inline CC_HINT(always_inline) void tls_ticket_stateless_encode_request(fr_tls_session_t *tls_session)
134
0
{
135
0
  tls_session->ticket = FR_TLS_TICKET_STATELESS_ENCODE_REQUESTED;
136
0
  TLS_PENDING_SET(tls_session, FR_TLS_PENDING_STATELESS_TICKET);
137
0
}
138
139
/** Request `decode session { ... }`, and tell the handshake the section is pending
140
 */
141
static inline CC_HINT(always_inline) void tls_ticket_stateless_decode_request(fr_tls_session_t *tls_session)
142
0
{
143
0
  tls_session->ticket = FR_TLS_TICKET_STATELESS_DECODE_REQUESTED;
144
0
  TLS_PENDING_SET(tls_session, FR_TLS_PENDING_STATELESS_TICKET);
145
0
}
146
147
/** Pause the handshake for `encode session { ... }` or `decode session { ... }`, and read the result
148
 *
149
 * We can't run the interpreter inside of a callback, so the caller records
150
 * what we want to do, and this function tells OpenSSL to pause its
151
 * processing.  We then return to session.c, which determines that there's
152
 * work to do, pushes the section, runs it, and calls us again.  That resumes
153
 * after the ASYNC_pause_job() call.
154
 *
155
 * @param[in] request   bound to the session.
156
 * @param[in] tls_session the ticket belongs to.
157
 * @param[in] name    of the section, for the log.
158
 * @param[in] error   to record when the section does not return success.
159
 * @return
160
 *  - true if the section ran and approved the session-state list.
161
 *  - false if it did not, or if it could not be run at all.
162
 */
163
static inline CC_HINT(always_inline)
164
bool tls_ticket_stateless_section_run(request_t *request, fr_tls_session_t *tls_session,
165
              char const *name, uint32_t error)
166
0
{
167
  /*
168
   *  Sections are only allowed during the handshake, as
169
   *  with certificate re-validation.  See the FIXME in
170
   *  tls_ticket_stateless_app_data_get().
171
   */
172
0
  if (unlikely(!tls_session->can_pause)) {
173
0
    fr_assert_msg("Unexpected call to %s. "
174
0
            "tls_session_async_handshake_cont must be in call stack", __FUNCTION__);
175
0
    tls_session->ticket = FR_TLS_TICKET_STATELESS_INIT;
176
0
    return false;
177
0
  }
178
179
0
  ASYNC_pause_job();
180
181
  /*
182
   *  If the request was cancelled, reset the ticket state
183
   *  so that we don't do anything.
184
   */
185
0
  if (unlang_request_is_cancelled(request)) {
186
0
    tls_session->ticket = FR_TLS_TICKET_STATELESS_INIT;
187
0
    return false;
188
0
  }
189
190
0
  if (tls_session->ticket != FR_TLS_TICKET_STATELESS_SUCCESS) {
191
0
    REDEBUG("(TLS) `%s` did not return success", name);
192
0
    fr_tls_session_error_add(request, error);
193
0
    tls_session->ticket = FR_TLS_TICKET_STATELESS_INIT;
194
0
    return false;
195
0
  }
196
197
0
  tls_session->ticket = FR_TLS_TICKET_STATELESS_INIT;
198
199
0
  return true;
200
0
}
201
202
/** Run `encode session { ... }` from inside an OpenSSL callback
203
 *
204
 * @param[in] request   bound to the session.
205
 * @param[in] tls_session the ticket belongs to.
206
 * @return
207
 *  - true if the section ran and approved the session-state list.
208
 *  - false if it did not, or if it could not be run at all.
209
 */
210
static bool tls_ticket_stateless_encode_run(request_t *request, fr_tls_session_t *tls_session)
211
0
{
212
0
  fr_assert(tls_session->ticket == FR_TLS_TICKET_STATELESS_INIT);
213
214
0
  tls_ticket_stateless_encode_request(tls_session);
215
216
0
  return tls_ticket_stateless_section_run(request, tls_session, "encode session",
217
0
            FR_ERROR_VALUE_ENCODE_SESSION_FAILED);
218
0
}
219
220
/** Run `decode session { ... }` from inside an OpenSSL callback
221
 *
222
 * @param[in] request   bound to the session.
223
 * @param[in] tls_session the ticket belongs to.
224
 * @return
225
 *  - true if the section ran and approved the session-state list.
226
 *  - false if it did not, or if it could not be run at all.
227
 */
228
static bool tls_ticket_stateless_decode_run(request_t *request, fr_tls_session_t *tls_session)
229
0
{
230
0
  fr_assert(tls_session->ticket == FR_TLS_TICKET_STATELESS_INIT);
231
232
0
  tls_ticket_stateless_decode_request(tls_session);
233
234
0
  return tls_ticket_stateless_section_run(request, tls_session, "decode session",
235
0
            FR_ERROR_VALUE_DECODE_SESSION_FAILED);
236
0
}
237
238
/** Disable stateless session tickets for a given TLS ctx
239
 *
240
 * @param[in] ctx to disable session tickets for.
241
 * @param[in] stateful Are stateful tickets in use.
242
 */
243
void fr_tls_ticket_stateless_disable(SSL_CTX *ctx, bool stateful)
244
0
{
245
0
  long ctx_options = SSL_CTX_get_options(ctx);
246
247
  /*
248
   *  Disable session tickets for older TLS versions
249
   */
250
0
  ctx_options |= SSL_OP_NO_TICKET;
251
0
  SSL_CTX_set_options(ctx, ctx_options);
252
253
  /*
254
   *  This controls the number of stateful or stateless
255
   *  tickets generated with TLS 1.3.
256
   *  Set to 1 ticket if stateful tickets are in use.
257
   */
258
0
  SSL_CTX_set_num_tickets(ctx, stateful ? 1 : 0);
259
0
}
260
261
/** Called when new tickets are being generated
262
 *
263
 * This adds additional application data to the session ticket to
264
 * allow us to perform validation checks when the session is
265
 * resumed.
266
 */
267
static int tls_ticket_stateless_app_data_set(SSL *ssl, void *arg)
268
0
{
269
0
  fr_tls_session_t  *tls_session = fr_tls_session(ssl);
270
0
  fr_tls_ticket_conf_t  *tls_cache_conf = arg;  /* Not talloced */
271
0
  SSL_SESSION   *sess;
272
0
  request_t   *request;
273
0
  fr_tls_conf_t   *conf;
274
0
  int     ret;
275
276
  /*
277
   *  Check to see if we have a request bound
278
   *  to the session.  If we don't have a
279
   *  request there's no application data to
280
   *  add.
281
   */
282
0
  if (!fr_tls_session_request_bound(ssl)) return 1;
283
284
  /*
285
   *  If the client doesn't support session tickets then there is no
286
   *  point in adding the application data - it won't get sent.
287
   */
288
0
  if (!tls_session->session_ticket_supported) return 1;
289
290
  /*
291
   *  Encode the complete session state list
292
   *  as app data.  Then, when the session is
293
   *  resumed, the session-state list is
294
   *  repopulated.
295
   */
296
0
  request = fr_tls_session_request(ssl);
297
298
  /*
299
   *  Request was cancelled, don't do anything.
300
   */
301
0
  if (unlang_request_is_cancelled(request)) return 0;
302
303
  /*
304
   *  Fatal error - We definitely shouldn't be
305
   *      attempting to generate session tickets
306
   *      if it's not permitted.
307
   */
308
0
  if (!tls_session->allow_session_resumption ||
309
0
      (!(tls_cache_conf->mode & FR_TLS_TICKET_STATELESS))) {
310
0
    REDEBUG("(TLS) Generating session-tickets is not allowed");
311
0
    fr_tls_session_error_add(request, FR_ERROR_VALUE_SESSION_TICKET_NOT_ALLOWED);
312
0
    return 0;
313
0
  }
314
315
0
  sess = SSL_get_session(ssl);
316
0
  if (!sess) {
317
0
    REDEBUG("(TLS) Failed retrieving session in session generation callback");
318
0
    return 0;
319
0
  }
320
321
  /*
322
   *  Run `encode session` to allow the admin to update the
323
   *  `session-state` list, before we encode it into a
324
   *  stateless session ticket.
325
   */
326
0
  conf = fr_tls_session_conf(ssl);
327
328
0
  if (conf->encode_session && !tls_ticket_stateless_encode_run(request, tls_session)) {
329
0
    REDEBUG("(TLS) Not generating a session-ticket");
330
0
    return 0;
331
0
  }
332
333
0
  ret = tls_ticket_app_data_set(request, sess, &tls_session->session_id);
334
0
  if (ret == 1) tls_session->session_ticket_created = true;
335
336
0
  return ret;
337
0
}
338
339
/** Called when new tickets are being decoded
340
 *
341
 * This adds the session-state attributes back to the current request.
342
 */
343
static SSL_TICKET_RETURN tls_ticket_stateless_app_data_get(SSL *ssl, SSL_SESSION *sess,
344
                     UNUSED unsigned char const *keyname,
345
                     UNUSED size_t keyname_len,
346
                     SSL_TICKET_STATUS status,
347
                     void *arg)
348
0
{
349
0
  fr_tls_session_t  *tls_session = fr_tls_session(ssl);
350
0
  fr_tls_conf_t   *conf = tls_session->conf;
351
0
  fr_tls_ticket_conf_t  *tls_cache_conf = arg;  /* Not talloced */
352
0
  request_t   *request = NULL;
353
354
0
  if (fr_tls_session_request_bound(ssl)) {
355
0
    request = fr_tls_session_request(ssl);
356
0
    if (unlang_request_is_cancelled(request)) return SSL_TICKET_RETURN_ABORT;
357
0
  }
358
359
0
  if (!tls_session->allow_session_resumption ||
360
0
      (!(tls_cache_conf->mode & FR_TLS_TICKET_STATELESS))) {
361
0
    ROPTIONAL(RDEBUG2, DEBUG2, "(TLS) Session resumption not enabled for this TLS session, "
362
0
        "denying session resumption via session-ticket");
363
0
        return SSL_TICKET_RETURN_IGNORE;
364
0
  }
365
366
0
  switch (status) {
367
0
  case SSL_TICKET_EMPTY:
368
0
  case SSL_TICKET_NO_DECRYPT:
369
0
  case SSL_TICKET_FATAL_ERR_MALLOC:
370
0
  case SSL_TICKET_FATAL_ERR_OTHER:
371
0
  case SSL_TICKET_NONE:
372
#ifdef STATIC_ANALYZER
373
  default:
374
#endif
375
0
    return SSL_TICKET_RETURN_IGNORE_RENEW; /* Send a new ticket */
376
377
0
  case SSL_TICKET_SUCCESS:
378
0
    if (!request) return SSL_TICKET_RETURN_USE;
379
0
    break;
380
381
0
  case SSL_TICKET_SUCCESS_RENEW:
382
0
    if (!request) return SSL_TICKET_RETURN_USE_RENEW;
383
0
    break;
384
0
  }
385
386
  /*
387
   *  This restores the contents of &session-state[*]
388
   *  which hopefully still contains all the certificate
389
   *  pairs.
390
   *
391
   *  Although the SSL_SESSION does contain a copy of
392
   *  the peer's certificate, it does not contain the
393
   *  peer's certificate chain, and so isn't reliable
394
   *  for performing re-validation.
395
   *
396
   *  The session ticket includes a lifetime.  But we didn't
397
   *  generate it, so we don't trust it.  If the ticket has
398
   *  passed its lifetime, then we reject it and force full
399
   *  reauthentication.
400
   */
401
0
  if (!tls_ticket_session_resumable(request, &tls_session->session_id, conf, sess)) {
402
0
    REDEBUG("(TLS) Session-ticket has too little life left, denying session resumption");
403
0
    fr_tls_session_error_add(request, FR_ERROR_VALUE_SESSION_TICKET_EXPIRED);
404
0
    return SSL_TICKET_RETURN_IGNORE_RENEW;
405
0
  }
406
407
0
  if (tls_ticket_app_data_get(request, sess, &tls_session->session_id) < 0) {
408
0
    REDEBUG("(TLS) Denying session resumption via session-ticket");
409
0
    return SSL_TICKET_RETURN_IGNORE_RENEW;
410
0
  }
411
412
  /*
413
   *  The session-state list is back.  Give policy the chance to
414
   *  look at what the ticket carried before anything relies on
415
   *  it, certificate re-validation below included.
416
   */
417
0
  if (conf->decode_session && !tls_ticket_stateless_decode_run(request, tls_session)) {
418
0
    REDEBUG("(TLS) Denying session resumption via session-ticket");
419
0
    return SSL_TICKET_RETURN_IGNORE_RENEW;
420
0
  }
421
422
0
  if (conf->virtual_server && tls_session->verify_peer_cert) {
423
0
    RDEBUG2("(TLS) Requesting certificate re-validation for session-ticket");
424
    /*
425
     *  This sets the validation state of the tls_session
426
     *  so that when we call ASYNC_pause_job(), and execution
427
     *  jumps back to tls_session_async_handshake_cont
428
     *  (just under SSL_read())
429
     *  the code there knows what job it needs to push onto
430
     *  the unlang stack.
431
     */
432
0
    fr_tls_verify_resumed_request(tls_session);
433
434
    /*
435
     *  Cache functions are only allowed during the handshake
436
     *  FIXME: With TLS 1.3 session tickets can be sent
437
     *  later... Technically every point where we call
438
     *  SSL_read() may need to be a yield point.
439
     */
440
0
    if (unlikely(!tls_session->can_pause)) {
441
0
      fr_assert_msg("Unexpected call to %s. "
442
0
              "tls_session_async_handshake_cont must be in call stack", __FUNCTION__);
443
0
      return SSL_TICKET_RETURN_IGNORE_RENEW;
444
0
    }
445
446
    /*
447
     *  Jumps back to SSL_read() in session.c
448
     *
449
     *  Be aware that if the request is cancelled
450
     *  whatever was meant to be done during the
451
     *  time we yielded may not have been completed.
452
     */
453
0
    ASYNC_pause_job();
454
455
    /*
456
     *  If the request was cancelled get everything back into
457
     *  a known state.
458
     */
459
0
    if (unlang_request_is_cancelled(request)) {
460
0
      fr_tls_verify_cert_reset(tls_session);
461
0
      return SSL_TICKET_RETURN_ABORT;
462
0
    }
463
464
    /*
465
     *  If we couldn't validate the client certificate
466
     *  give the client the opportunity to send a new
467
     *  one, but _don't_ allow session resumption.
468
     */
469
0
    if (!fr_tls_verify_cert_result(tls_session)) {
470
0
      RDEBUG2("(TLS) Certificate re-validation failed, denying session resumption via session-ticket");
471
0
      return SSL_TICKET_RETURN_IGNORE_RENEW;
472
0
    }
473
0
  }
474
475
0
  return (status == SSL_TICKET_SUCCESS_RENEW) ? SSL_TICKET_RETURN_USE_RENEW : SSL_TICKET_RETURN_USE;
476
0
}
477
478
/** Derive the session ticket key, and install the session ticket callbacks on an SSL_CTX
479
 *
480
 * The key the configuration gives is stretched to the length OpenSSL asks
481
 * for with HKDF.  The callbacks embed the session-state list in each session
482
 * ticket the server issues, and extract the list again when a ticket is
483
 * presented.
484
 *
485
 * @param[in] ctx   to install the key and callbacks on.
486
 * @param[in] cache_conf  Session caching configuration, for the key and the callbacks.
487
 * @return
488
 *  - 0 on success.
489
 *  - -1 on failure.
490
 */
491
int fr_tls_ticket_stateless_ctx_init(SSL_CTX *ctx, fr_tls_ticket_conf_t const *cache_conf)
492
0
{
493
0
  size_t    key_len;
494
0
  uint8_t   *key_buff;
495
0
  EVP_PKEY_CTX  *pkey_ctx = NULL;
496
497
  /*
498
   *  If keys is NULL, then OpenSSL returns the expected
499
   *  key length, which may be different across different
500
   *  flavours/versions of OpenSSL.
501
   *
502
   *  We could calculate this in conf.c, but, if in future
503
   *  OpenSSL decides to use different key lengths based
504
   *  on other parameters in the ctx, that'd break.
505
   */
506
0
  key_len = SSL_CTX_set_tlsext_ticket_keys(ctx, NULL, 0);
507
508
0
  if (unlikely((pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL)) {
509
0
    fr_tls_strerror_printf(NULL);
510
0
    PERROR("Failed initialising KDF");
511
0
  kdf_error:
512
0
    if (pkey_ctx) EVP_PKEY_CTX_free(pkey_ctx);
513
0
    return -1;
514
0
  }
515
0
  if (unlikely(EVP_PKEY_derive_init(pkey_ctx) != 1)) {
516
0
    fr_tls_strerror_printf(NULL);
517
0
    PERROR("Failed initialising KDF derivation ctx");
518
0
    goto kdf_error;
519
0
  }
520
0
  if (unlikely(EVP_PKEY_CTX_set_hkdf_md(pkey_ctx, UNCONST(struct evp_md_st *, EVP_sha256())) != 1)) {
521
0
    fr_tls_strerror_printf(NULL);
522
0
    PERROR("Failed setting KDF MD");
523
0
    goto kdf_error;
524
0
  }
525
0
  if (unlikely(EVP_PKEY_CTX_set1_hkdf_key(pkey_ctx,
526
0
            UNCONST(unsigned char *, cache_conf->session_ticket_key),
527
0
            talloc_array_length(cache_conf->session_ticket_key)) != 1)) {
528
0
    fr_tls_strerror_printf(NULL);
529
0
    PERROR("Failed setting KDF key");
530
0
    goto kdf_error;
531
0
  }
532
0
  if (unlikely(EVP_PKEY_CTX_add1_hkdf_info(pkey_ctx,
533
0
             UNCONST(unsigned char *, "freeradius-session-ticket"),
534
0
             sizeof("freeradius-session-ticket") - 1) != 1)) {
535
0
    fr_tls_strerror_printf(NULL);
536
0
    PERROR("Failed setting KDF label");
537
0
    goto kdf_error;
538
0
  }
539
540
  /*
541
   *  SSL_CTX_set_tlsext_ticket_keys memcpys its
542
   *  inputs so this is just a temporary buffer.
543
   */
544
0
  MEM(key_buff = talloc_array(NULL, uint8_t, key_len));
545
0
  if (EVP_PKEY_derive(pkey_ctx, key_buff, &key_len) != 1) {
546
0
    fr_tls_strerror_printf(NULL);
547
0
    PERROR("Failed deriving session ticket key");
548
549
0
  key_buff_error:
550
0
    talloc_free(key_buff);
551
0
    goto kdf_error;
552
0
  }
553
0
  EVP_PKEY_CTX_free(pkey_ctx);
554
0
  pkey_ctx = NULL;
555
556
0
  fr_assert(talloc_array_length(key_buff) == key_len);
557
558
  /*
559
   *  Ensure the same keys are used across all threads
560
   */
561
0
  if (SSL_CTX_set_tlsext_ticket_keys(ctx,
562
0
             key_buff, key_len) != 1) {
563
0
    fr_tls_strerror_printf(NULL);
564
0
    PERROR("Failed setting session ticket keys");
565
0
    goto key_buff_error;
566
0
  }
567
568
0
  DEBUG3("Derived session-ticket-key:");
569
0
  HEXDUMP3(key_buff, key_len, NULL);
570
0
  TALLOC_FREE(key_buff);
571
572
  /*
573
   *  These callbacks embed and extract the
574
   *  session-state list from the session-ticket.
575
   */
576
0
  if (unlikely(SSL_CTX_set_session_ticket_cb(ctx,
577
0
               tls_ticket_stateless_app_data_set,
578
0
               tls_ticket_stateless_app_data_get,
579
0
               UNCONST(fr_tls_ticket_conf_t *, cache_conf)) != 1)) {
580
0
    fr_tls_strerror_printf(NULL);
581
0
    PERROR("Failed setting session ticket callbacks");
582
0
    return -1;
583
0
  }
584
585
  /*
586
   *  Stateless resumption is enabled by default when
587
   *  the TLS ctx is created, but OpenSSL sends too
588
   *  many session tickets by default (2), and we only
589
   *      need one.
590
   */
591
0
  SSL_CTX_set_num_tickets(ctx, 1);
592
593
0
  return 0;
594
0
}
595
#endif /* WITH_TLS */