Coverage Report

Created: 2026-09-28 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/freeradius-devel/tls/session.h
Line
Count
Source
1
#pragma once
2
/*
3
 *  This program is free software; you can redistribute it and/or modify
4
 *  it under the terms of the GNU General Public License as published by
5
 *  the Free Software Foundation; either version 2 of the License, or
6
 *  (at your option) any later version.
7
 *
8
 *  This program is distributed in the hope that it will be useful,
9
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 *  GNU General Public License for more details.
12
 *
13
 *  You should have received a copy of the GNU General Public License
14
 *  along with this program; if not, write to the Free Software
15
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16
 */
17
#ifdef WITH_TLS
18
/**
19
 * $Id: e05e3ef7e8c1fd334f92dfb930f46fdda0fb5f97 $
20
 *
21
 * @file lib/tls/session.h
22
 * @brief Structures for session-resumption management.
23
 *
24
 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25
 */
26
RCSIDH(session_h, "$Id: e05e3ef7e8c1fd334f92dfb930f46fdda0fb5f97 $")
27
28
#include "openssl_user_macros.h"
29
30
#include <openssl/ssl.h>
31
#include <openssl/err.h>
32
33
typedef struct fr_tls_session_s fr_tls_session_t;
34
35
#include <freeradius-devel/server/request.h>
36
#include <freeradius-devel/util/dbuff.h>
37
38
#include "bio.h"
39
#include "cache.h"
40
#include "conf.h"
41
#include "index.h"
42
#include "verify.h"
43
44
#ifdef __cplusplus
45
extern "C" {
46
#endif
47
48
/*
49
 *  A single TLS record may be up to 16384 octets in length, but a
50
 *  TLS message may span multiple TLS records, and a TLS
51
 *  certificate message may in principle be as long as 16MB.
52
 *
53
 *  However, note that in order to protect against reassembly
54
 *  lockup and denial of service attacks, it may be desirable for
55
 *  an implementation to set a maximum size for one such group of
56
 *  TLS messages.
57
 *
58
 *  The TLS Message Length field is four octets, and provides the
59
 *  total length of the TLS message or set of messages that is
60
 *  being fragmented; this simplifies buffer allocation.
61
 */
62
#define FR_TLS_MAX_RECORD_SIZE 16384
63
64
/*
65
 *  Cap the maximum number of handshakes that we receive in a row.
66
 *  If we don't make progress, then either the certificates are
67
 *  enormous, or the TLS chunks are deliberately small, or the
68
 *  other end is trying to catch us in an infinite ACK / ACK loop.
69
 *
70
 *  The EAP code also caps the number of rounds it does, but there
71
 *  are non-TLS EAP methods which can use multiple rounds.  We
72
 *  need both limits in order to catch all corner cases.  Note
73
 *  also that TLS-based EAP methods will ACK each _fragment_ of a
74
 *  TLS record.  So one TLS "round" could map to multple EAP
75
 *  "rounds".
76
 */
77
#define FR_TLS_MAX_ROUNDS 50
78
79
/*
80
 * FIXME: Dynamic allocation of buffer to overcome FR_TLS_MAX_RECORD_SIZE overflows.
81
 *  or configure TLS not to exceed FR_TLS_MAX_RECORD_SIZE.
82
 *
83
 * clean_in and clean_out are dbuffs over a fixed
84
 * FR_TLS_MAX_RECORD_SIZE allocation.  The buffers should not be
85
 * extensible, as doing so could allow the peer to send unlimited data.
86
 *
87
 * dirty_in and dirty_out allow for extensions.  We therefore can't
88
 * call the fill/drain helpers below on those buffers.  Calling
89
 * fr_tls_record_init() on one would re-init the dbuff and lose the
90
 * talloc context which lets the buffer extend.
91
 */
92
93
/** Reset a record buffer so that it can be filled again
94
 *
95
 * A record buffer is filled, then drained.  While the buffer is filling, the
96
 * dbuff runs from the start of the buffer to the end of the memory, the
97
 * current position is where the next octet is written, and fr_dbuff_used()
98
 * says how many octets are in the buffer.
99
 *
100
 * Resetting returns the buffer to the filling state, and discards whatever
101
 * the buffer held.
102
 *
103
 * @param[in] record  to reset.
104
 */
105
static inline void fr_tls_record_init(fr_dbuff_t *record)
106
0
{
107
0
  fr_dbuff_init(record, fr_dbuff_start(record), (size_t) FR_TLS_MAX_RECORD_SIZE);
108
0
}
Unexecuted instantiation: ctx.c:fr_tls_record_init
Unexecuted instantiation: log.c:fr_tls_record_init
Unexecuted instantiation: verify.c:fr_tls_record_init
Unexecuted instantiation: thread.c:fr_tls_record_init
109
110
/** Stop filling a record buffer, and start draining it
111
 *
112
 * While the buffer is draining, the dbuff runs from the start of the buffer
113
 * to the end of the data which was written, the current position is where the
114
 * next octet is read, and fr_dbuff_remaining() says how many octets are left
115
 * to read.
116
 *
117
 * The buffer must not be written to while it is draining.  Call
118
 * fr_tls_record_init() to fill it again.
119
 *
120
 * @param[in] record  to start draining.
121
 */
122
static inline void fr_tls_record_drain(fr_dbuff_t *record)
123
0
{
124
0
  size_t used = fr_dbuff_used(record);
125
0
126
0
  fr_dbuff_init(record, fr_dbuff_start(record), used);
127
0
}
Unexecuted instantiation: ctx.c:fr_tls_record_drain
Unexecuted instantiation: log.c:fr_tls_record_drain
Unexecuted instantiation: verify.c:fr_tls_record_drain
Unexecuted instantiation: thread.c:fr_tls_record_drain
128
129
typedef enum {
130
  TLS_INFO_ORIGIN_RECORD_RECEIVED,
131
  TLS_INFO_ORIGIN_RECORD_SENT
132
} fr_tls_info_origin_t;
133
134
typedef struct {
135
  int   origin;
136
  int   content_type;
137
  uint8_t   handshake_type;
138
  uint8_t   alert_level;
139
  uint8_t   alert_description;
140
  bool    initialized;
141
142
  char    info_description[256];
143
  size_t    record_len;
144
  int   version;
145
} fr_tls_info_t;
146
147
/** Result of the last operation on the session
148
 *
149
 * This is needed to record the result of an asynchronous
150
 */
151
typedef enum {
152
  FR_TLS_RESULT_IN_PROGRESS = 0x00,   //!< Handshake round in progress.
153
  FR_TLS_RESULT_ERROR   = 0x01,   //!< Handshake failed.
154
  FR_TLS_RESULT_SUCCESS   = 0x02    //!< Handshake round succeed.
155
} fr_tls_result_t;
156
157
/** Tracks the state of a TLS session
158
 *
159
 * Currently used for RADSEC and EAP-TLS + dependents (EAP-TTLS, EAP-PEAP etc...).
160
 *
161
 * In the case of EAP-TLS + dependents a #eap_tls_session_t struct is used to track
162
 * the transfer of TLS records.
163
 */
164
struct fr_tls_session_s {
165
  SSL_CTX     *ctx;       //!< TLS configuration context.
166
  SSL       *ssl;       //!< This SSL session.
167
  SSL_SESSION   *session;     //!< Session resumption data.
168
  fr_tls_result_t   result;       //!< Result of the last handshake round.
169
  fr_tls_info_t   info;       //!< Information about the state of the TLS session.
170
171
  fr_tls_bio_dbuff_t  *into_ssl;      //!< Encrypted data from the peer, which OpenSSL reads.
172
  fr_tls_bio_dbuff_t  *from_ssl;      //!< Encrypted data OpenSSL wrote, waiting to be sent.
173
  fr_dbuff_t    clean_in;     //!< Cleartext data that needs to be encrypted.
174
  fr_dbuff_t    clean_out;      //!< Decrypted cleartext, for the caller to read.
175
  fr_dbuff_t    *dirty_in;      //!< Encrypted data to decrypt.  The producer
176
                ///< cursor of into_ssl, which the caller fills.
177
  fr_dbuff_t    *dirty_out;     //!< Encrypted data, ready to send.  The consumer
178
                ///< cursor of from_ssl, which the caller drains.
179
  int     last_ret;     //!< Last result returned by SSL_read().
180
181
  uint32_t    rounds;       //!< Handshake round trips.
182
183
  size_t      mtu;        //!< Maximum record fragment size.
184
185
  void      *opaque;      //!< Used to store module specific data.
186
187
  fr_tls_cache_t    *cache;       //!< Current session resumption state.
188
  bool      allow_session_resumption; //!< Whether session resumption is allowed.
189
  bool      verify_peer_cert;   //!< Whether verification of the peer's certificate
190
                ///< has been requested.
191
192
  fr_tls_verify_t   validate;     //!< Current session certificate validation state.
193
194
  bool      invalid;      //!< Whether heartbleed attack was detected.
195
196
  bool      peer_cert_ok;     //!< Whether the peer's certificate was validated
197
  bool      can_pause;      //!< If true, it's ok to pause the request
198
                ///< using the OpenSSL async API.
199
200
  uint8_t     alerts_sent;
201
  bool      pending_alert;
202
  uint8_t     pending_alert_level;
203
  uint8_t     pending_alert_description;
204
205
  fr_pair_list_t    extra_pairs;      //!< Pairs to add to cache and certificate validation
206
                ///< calls.  These will be duplicated for every call.
207
};
208
209
/** Return the tls config associated with a tls_session
210
 *
211
 * @param[in] ssl to retrieve the configuration from.
212
 * @return #fr_tls_conf_t associated with the session.
213
 */
214
static inline fr_tls_conf_t *fr_tls_session_conf(SSL *ssl)
215
0
{
216
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF), fr_tls_conf_t);
217
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_conf
Unexecuted instantiation: log.c:fr_tls_session_conf
Unexecuted instantiation: verify.c:fr_tls_session_conf
Unexecuted instantiation: thread.c:fr_tls_session_conf
218
219
/** Return the tls_session associated with a SSL *
220
 *
221
 * @param[in] ssl to retrieve the configuration from.
222
 * @return #fr_tls_conf_t associated with the session.
223
 */
224
static inline fr_tls_session_t *fr_tls_session(SSL *ssl)
225
0
{
226
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TLS_SESSION), fr_tls_session_t);
227
0
}
Unexecuted instantiation: ctx.c:fr_tls_session
Unexecuted instantiation: log.c:fr_tls_session
Unexecuted instantiation: verify.c:fr_tls_session
Unexecuted instantiation: thread.c:fr_tls_session
228
229
/** Check to see if a request is bound to a session
230
 *
231
 * @param[in] ssl session to check for requests.
232
 * @return
233
 *  - true if a request is bound to this session.
234
 *  - false if a request is not bound to this session.
235
 */
236
static inline CC_HINT(nonnull) bool fr_tls_session_request_bound(SSL *ssl)
237
0
{
238
0
  return (SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST) != NULL);
239
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_request_bound
Unexecuted instantiation: log.c:fr_tls_session_request_bound
Unexecuted instantiation: verify.c:fr_tls_session_request_bound
Unexecuted instantiation: thread.c:fr_tls_session_request_bound
240
241
/** Return the request associated with a ssl session
242
 *
243
 * @param[in] ssl session to retrieve the configuration from.
244
 * @return #request associated with the session.
245
 */
246
static inline request_t *fr_tls_session_request(SSL const *ssl)
247
0
{
248
0
  request_t *request = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
249
250
0
  if (!request) return NULL;
251
252
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST), request_t);
253
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_request
Unexecuted instantiation: log.c:fr_tls_session_request
Unexecuted instantiation: verify.c:fr_tls_session_request
Unexecuted instantiation: thread.c:fr_tls_session_request
254
255
static inline CC_HINT(nonnull) void _fr_tls_session_request_bind(char const *file, int line,
256
                 SSL *ssl, request_t *request)
257
0
{
258
0
  int ret;
259
0
260
0
  RDEBUG3("%s[%d] - Binding SSL * (%p) to request (%p)", file, line, ssl, request);
261
0
262
0
#ifndef NDEBUG
263
0
  {
264
0
    request_t *old;
265
0
    old = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
266
0
    if (old) {
267
0
      (void)talloc_get_type_abort(old, request_t);
268
0
      fr_assert(0);
269
0
    }
270
0
  }
271
0
#endif
272
0
  ret = SSL_set_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST, request);
273
0
  if (unlikely(ret == 0)) {
274
0
    fr_assert(0);
275
0
    return;
276
0
  }
277
0
}
Unexecuted instantiation: ctx.c:_fr_tls_session_request_bind
Unexecuted instantiation: log.c:_fr_tls_session_request_bind
Unexecuted instantiation: verify.c:_fr_tls_session_request_bind
Unexecuted instantiation: thread.c:_fr_tls_session_request_bind
278
/** Place a request pointer in the SSL * for retrieval by callbacks
279
 *
280
 * @note A request must not already be bound to the SSL *
281
 *
282
 * @param[in] ssl   to be bound.
283
 * @param[in] request   to bind to the tls_session.
284
 */
285
 #define fr_tls_session_request_bind(_ssl, _request) _fr_tls_session_request_bind(__FILE__, __LINE__, _ssl, _request)
286
287
static inline CC_HINT(nonnull) void _fr_tls_session_request_unbind(char const *file, int line, SSL *ssl)
288
0
{
289
0
  request_t *request = fr_tls_session_request(ssl);
290
0
  int   ret;
291
0
292
0
#ifndef NDEBUG
293
0
  (void)talloc_get_type_abort(request, request_t);
294
0
#endif
295
0
296
0
  RDEBUG3("%s[%d] - Unbinding SSL * (%p) from request (%p)", file, line, ssl, request);
297
0
  ret = SSL_set_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST, NULL);
298
0
  if (unlikely(ret == 0)) {
299
0
    fr_assert(0);
300
0
    return;
301
0
  }
302
0
}
Unexecuted instantiation: ctx.c:_fr_tls_session_request_unbind
Unexecuted instantiation: log.c:_fr_tls_session_request_unbind
Unexecuted instantiation: verify.c:_fr_tls_session_request_unbind
Unexecuted instantiation: thread.c:_fr_tls_session_request_unbind
303
/** Remove a request pointer from the tls_session
304
 *
305
 * @note A request must be bound to the tls_session
306
 *
307
 * @param[in] ssl session containing the request pointer.
308
 */
309
#define fr_tls_session_request_unbind(_ssl) _fr_tls_session_request_unbind(__FILE__, __LINE__, _ssl)
310
311
/** Add extra pairs to the temporary subrequests
312
 *
313
 * @param[in] child   to add extra pairs to.
314
 * @param[in] tls_session to add extra pairs from.
315
 */
316
static inline CC_HINT(nonnull)
317
void fr_tls_session_extra_pairs_copy_to_child(request_t *child, fr_tls_session_t *tls_session)
318
0
{
319
0
  if (!fr_pair_list_empty(&tls_session->extra_pairs)) {
320
0
    MEM(fr_pair_list_copy(child->request_ctx, &child->request_pairs, &tls_session->extra_pairs) >= 0);
321
0
  }
322
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: log.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: thread.c:fr_tls_session_extra_pairs_copy_to_child
323
324
/** Add an additional pair (copying it) to the list of extra pairs
325
 *
326
 * @param[in] tls_session to add extra pairs to.
327
 * @param[in] vp    to add to tls_session.
328
 */
329
static inline CC_HINT(nonnull)
330
void fr_tls_session_extra_pair_add(fr_tls_session_t *tls_session, fr_pair_t *vp)
331
0
{
332
0
  fr_pair_t *copy;
333
0
334
0
  MEM(copy = fr_pair_copy(tls_session, vp));
335
0
  fr_pair_append(&tls_session->extra_pairs, copy);
336
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: log.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: verify.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: thread.c:fr_tls_session_extra_pair_add
337
338
/** Add an additional pair to the list of extra pairs
339
 *
340
 * @param[in] tls_session to add extra pairs to.
341
 * @param[in] vp    to add to tls_session.
342
 */
343
static inline CC_HINT(nonnull)
344
void fr_tls_session_extra_pair_add_shallow(fr_tls_session_t *tls_session, fr_pair_t *vp)
345
0
{
346
0
  fr_assert(talloc_parent(vp) == tls_session);
347
0
  fr_pair_append(&tls_session->extra_pairs, vp);
348
0
}
Unexecuted instantiation: ctx.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: log.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: verify.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: thread.c:fr_tls_session_extra_pair_add_shallow
349
350
351
int     fr_tls_session_password_cb(char *buf, int num, int rwflag, void *userdata);
352
353
unsigned int  fr_tls_session_psk_client_cb(SSL *ssl, UNUSED char const *hint,
354
               char *identity, unsigned int max_identity_len,
355
               unsigned char *psk, unsigned int max_psk_len);
356
357
unsigned int  fr_tls_session_psk_server_cb(SSL *ssl, const char *identity,
358
               unsigned char *psk, unsigned int max_psk_len);
359
360
void    fr_tls_session_info_cb(SSL const *s, int where, int ret);
361
362
void    fr_tls_session_msg_cb(int write_p, int msg_version, int content_type,
363
              void const *buf, size_t len, SSL *ssl, void *arg);
364
365
void    fr_tls_session_keylog_cb(const SSL *ssl, const char *line);
366
367
int   fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx,
368
                    request_t *request, X509 *cert, bool der_decode) CC_HINT(nonnull);
369
370
int   fr_tls_session_client_hello_cb(SSL *ssl, int *al, void *arg);
371
372
int   fr_tls_session_recv(request_t *request, fr_tls_session_t *tls_session);
373
374
int     fr_tls_session_send(request_t *request, fr_tls_session_t *tls_session);
375
376
int     fr_tls_session_alert(request_t *request, fr_tls_session_t *tls_session, uint8_t level, uint8_t description);
377
378
unlang_action_t fr_tls_session_async_handshake_push(request_t *request, fr_tls_session_t *tls_session);
379
380
fr_tls_session_t *fr_tls_session_alloc_client(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx, request_t *request);
381
382
fr_tls_session_t *fr_tls_session_alloc_server(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx, request_t *request, size_t dynamic_mtu, bool client_cert);
383
384
unlang_action_t fr_tls_new_session_push(request_t *request, fr_tls_conf_t const *tls_conf);
385
386
unlang_action_t fr_tls_session_fail_session(request_t *request, fr_tls_session_t *tls_session);
387
388
#ifdef __cplusplus
389
}
390
#endif
391
#endif /* WITH_TLS */