Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/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: c96f959939e38a987611a74bd39c4b779f3b1f0e $
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: c96f959939e38a987611a74bd39c4b779f3b1f0e $")
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
37
#include "cache.h"
38
#include "conf.h"
39
#include "index.h"
40
#include "verify.h"
41
42
#ifdef __cplusplus
43
extern "C" {
44
#endif
45
46
/*
47
 *  A single TLS record may be up to 16384 octets in length, but a
48
 *  TLS message may span multiple TLS records, and a TLS
49
 *  certificate message may in principle be as long as 16MB.
50
 *
51
 *  However, note that in order to protect against reassembly
52
 *  lockup and denial of service attacks, it may be desirable for
53
 *  an implementation to set a maximum size for one such group of
54
 *  TLS messages.
55
 *
56
 *  The TLS Message Length field is four octets, and provides the
57
 *  total length of the TLS message or set of messages that is
58
 *  being fragmented; this simplifies buffer allocation.
59
 */
60
0
#define FR_TLS_MAX_RECORD_SIZE 16384
61
62
/*
63
 * FIXME: Dynamic allocation of buffer to overcome FR_TLS_MAX_RECORD_SIZE overflows.
64
 *  or configure TLS not to exceed FR_TLS_MAX_RECORD_SIZE.
65
 */
66
typedef struct {
67
  uint8_t   data[FR_TLS_MAX_RECORD_SIZE];
68
  size_t    used;
69
} fr_tls_record_t;
70
71
typedef enum {
72
  TLS_INFO_ORIGIN_RECORD_RECEIVED,
73
  TLS_INFO_ORIGIN_RECORD_SENT
74
} fr_tls_info_origin_t;
75
76
typedef struct {
77
  int   origin;
78
  int   content_type;
79
  uint8_t   handshake_type;
80
  uint8_t   alert_level;
81
  uint8_t   alert_description;
82
  bool    initialized;
83
84
  char    info_description[256];
85
  size_t    record_len;
86
  int   version;
87
} fr_tls_info_t;
88
89
/** Result of the last operation on the session
90
 *
91
 * This is needed to record the result of an asynchronous
92
 */
93
typedef enum {
94
  FR_TLS_RESULT_IN_PROGRESS = 0x00,   //!< Handshake round in progress.
95
  FR_TLS_RESULT_ERROR   = 0x01,   //!< Handshake failed.
96
  FR_TLS_RESULT_SUCCESS   = 0x02    //!< Handshake round succeed.
97
} fr_tls_result_t;
98
99
/** Tracks the state of a TLS session
100
 *
101
 * Currently used for RADSEC and EAP-TLS + dependents (EAP-TTLS, EAP-PEAP etc...).
102
 *
103
 * In the case of EAP-TLS + dependents a #eap_tls_session_t struct is used to track
104
 * the transfer of TLS records.
105
 */
106
struct fr_tls_session_s {
107
  SSL_CTX     *ctx;       //!< TLS configuration context.
108
  SSL       *ssl;       //!< This SSL session.
109
  SSL_SESSION   *session;     //!< Session resumption data.
110
  fr_tls_result_t   result;       //!< Result of the last handshake round.
111
  fr_tls_info_t   info;       //!< Information about the state of the TLS session.
112
113
  BIO       *into_ssl;      //!< Basic I/O input to OpenSSL.
114
  BIO       *from_ssl;      //!< Basic I/O output from OpenSSL.
115
  fr_tls_record_t   clean_in;     //!< Cleartext data that needs to be encrypted.
116
  fr_tls_record_t   clean_out;      //!< Cleartext data that's been encrypted.
117
  fr_tls_record_t   dirty_in;     //!< Encrypted data to decrypt.
118
  fr_tls_record_t   dirty_out;      //!< Encrypted data that's been decrypted.
119
  int     last_ret;     //!< Last result returned by SSL_read().
120
121
  void      (*record_init)(fr_tls_record_t *buf);
122
  void      (*record_close)(fr_tls_record_t *buf);
123
  unsigned int    (*record_from_buff)(fr_tls_record_t *buf, void const *ptr, unsigned int size);
124
  unsigned int    (*record_to_buff)(fr_tls_record_t *buf, void *ptr, unsigned int size);
125
126
  size_t      mtu;        //!< Maximum record fragment size.
127
128
  void      *opaque;      //!< Used to store module specific data.
129
130
  fr_tls_cache_t    *cache;       //!< Current session resumption state.
131
  bool      allow_session_resumption; //!< Whether session resumption is allowed.
132
  bool      verify_client_cert;   //!< Whether client cert verification has been requested.
133
134
  fr_tls_verify_t   validate;     //!< Current session certificate validation state.
135
136
  bool      invalid;      //!< Whether heartbleed attack was detected.
137
138
  bool      client_cert_ok;     //!< whether or not the client certificate was validated
139
  bool      can_pause;      //!< If true, it's ok to pause the request
140
                ///< using the OpenSSL async API.
141
142
  uint8_t     alerts_sent;
143
  bool      pending_alert;
144
  uint8_t     pending_alert_level;
145
  uint8_t     pending_alert_description;
146
147
  fr_pair_list_t    extra_pairs;      //!< Pairs to add to cache and certificate validation
148
                ///< calls.  These will be duplicated for every call.
149
};
150
151
/** Return the tls config associated with a tls_session
152
 *
153
 * @param[in] ssl to retrieve the configuration from.
154
 * @return #fr_tls_conf_t associated with the session.
155
 */
156
static inline fr_tls_conf_t *fr_tls_session_conf(SSL *ssl)
157
0
{
158
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF), fr_tls_conf_t);
159
0
}
Unexecuted instantiation: base.c:fr_tls_session_conf
Unexecuted instantiation: cache.c:fr_tls_session_conf
Unexecuted instantiation: conf.c:fr_tls_session_conf
Unexecuted instantiation: pairs.c:fr_tls_session_conf
Unexecuted instantiation: session.c:fr_tls_session_conf
Unexecuted instantiation: strerror.c:fr_tls_session_conf
Unexecuted instantiation: virtual_server.c:fr_tls_session_conf
160
161
/** Return the tls_session associated with a SSL *
162
 *
163
 * @param[in] ssl to retrieve the configuration from.
164
 * @return #fr_tls_conf_t associated with the session.
165
 */
166
static inline fr_tls_session_t *fr_tls_session(SSL *ssl)
167
0
{
168
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TLS_SESSION), fr_tls_session_t);
169
0
}
Unexecuted instantiation: base.c:fr_tls_session
Unexecuted instantiation: cache.c:fr_tls_session
Unexecuted instantiation: conf.c:fr_tls_session
Unexecuted instantiation: pairs.c:fr_tls_session
Unexecuted instantiation: session.c:fr_tls_session
Unexecuted instantiation: strerror.c:fr_tls_session
Unexecuted instantiation: virtual_server.c:fr_tls_session
170
171
/** Check to see if a request is bound to a session
172
 *
173
 * @param[in] ssl session to check for requests.
174
 * @return
175
 *  - true if a request is bound to this session.
176
 *  - false if a request is not bound to this session.
177
 */
178
static inline CC_HINT(nonnull) bool fr_tls_session_request_bound(SSL *ssl)
179
0
{
180
0
  return (SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST) != NULL);
181
0
}
Unexecuted instantiation: base.c:fr_tls_session_request_bound
Unexecuted instantiation: cache.c:fr_tls_session_request_bound
Unexecuted instantiation: conf.c:fr_tls_session_request_bound
Unexecuted instantiation: pairs.c:fr_tls_session_request_bound
Unexecuted instantiation: session.c:fr_tls_session_request_bound
Unexecuted instantiation: strerror.c:fr_tls_session_request_bound
Unexecuted instantiation: virtual_server.c:fr_tls_session_request_bound
182
183
/** Return the request associated with a ssl session
184
 *
185
 * @param[in] ssl session to retrieve the configuration from.
186
 * @return #request associated with the session.
187
 */
188
static inline request_t *fr_tls_session_request(SSL const *ssl)
189
0
{
190
0
  request_t *request = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
191
192
0
  if (!request) return NULL;
193
194
0
  return talloc_get_type_abort(SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST), request_t);
195
0
}
Unexecuted instantiation: base.c:fr_tls_session_request
Unexecuted instantiation: cache.c:fr_tls_session_request
Unexecuted instantiation: conf.c:fr_tls_session_request
Unexecuted instantiation: pairs.c:fr_tls_session_request
Unexecuted instantiation: session.c:fr_tls_session_request
Unexecuted instantiation: strerror.c:fr_tls_session_request
Unexecuted instantiation: virtual_server.c:fr_tls_session_request
196
197
static inline CC_HINT(nonnull) void _fr_tls_session_request_bind(char const *file, int line,
198
                 SSL *ssl, request_t *request)
199
0
{
200
0
  int ret;
201
202
0
  RDEBUG3("%s[%d] - Binding SSL * (%p) to request (%p)", file, line, ssl, request);
203
204
0
#ifndef NDEBUG
205
0
  {
206
0
    request_t *old;
207
0
    old = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
208
0
    if (old) {
209
0
      (void)talloc_get_type_abort(old, request_t);
210
0
      fr_assert(0);
211
0
    }
212
0
  }
213
0
#endif
214
0
  ret = SSL_set_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST, request);
215
0
  if (unlikely(ret == 0)) {
216
0
    fr_assert(0);
217
0
    return;
218
0
  }
219
0
}
Unexecuted instantiation: base.c:_fr_tls_session_request_bind
Unexecuted instantiation: cache.c:_fr_tls_session_request_bind
Unexecuted instantiation: conf.c:_fr_tls_session_request_bind
Unexecuted instantiation: pairs.c:_fr_tls_session_request_bind
Unexecuted instantiation: session.c:_fr_tls_session_request_bind
Unexecuted instantiation: strerror.c:_fr_tls_session_request_bind
Unexecuted instantiation: virtual_server.c:_fr_tls_session_request_bind
220
/** Place a request pointer in the SSL * for retrieval by callbacks
221
 *
222
 * @note A request must not already be bound to the SSL *
223
 *
224
 * @param[in] ssl   to be bound.
225
 * @param[in] request   to bind to the tls_session.
226
 */
227
0
 #define fr_tls_session_request_bind(_ssl, _request) _fr_tls_session_request_bind(__FILE__, __LINE__, _ssl, _request)
228
229
static inline CC_HINT(nonnull) void _fr_tls_session_request_unbind(char const *file, int line, SSL *ssl)
230
0
{
231
0
  request_t *request = fr_tls_session_request(ssl);
232
0
  int   ret;
233
234
0
#ifndef NDEBUG
235
0
  (void)talloc_get_type_abort(request, request_t);
236
0
#endif
237
238
0
  RDEBUG3("%s[%d] - Unbinding SSL * (%p) from request (%p)", file, line, ssl, request);
239
0
  ret = SSL_set_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST, NULL);
240
0
  if (unlikely(ret == 0)) {
241
0
    fr_assert(0);
242
0
    return;
243
0
  }
244
0
}
Unexecuted instantiation: base.c:_fr_tls_session_request_unbind
Unexecuted instantiation: cache.c:_fr_tls_session_request_unbind
Unexecuted instantiation: conf.c:_fr_tls_session_request_unbind
Unexecuted instantiation: pairs.c:_fr_tls_session_request_unbind
Unexecuted instantiation: session.c:_fr_tls_session_request_unbind
Unexecuted instantiation: strerror.c:_fr_tls_session_request_unbind
Unexecuted instantiation: virtual_server.c:_fr_tls_session_request_unbind
245
/** Remove a request pointer from the tls_session
246
 *
247
 * @note A request must be bound to the tls_session
248
 *
249
 * @param[in] ssl session containing the request pointer.
250
 */
251
0
#define fr_tls_session_request_unbind(_ssl) _fr_tls_session_request_unbind(__FILE__, __LINE__, _ssl)
252
253
/** Add extra pairs to the temporary subrequests
254
 *
255
 * @param[in] child   to add extra pairs to.
256
 * @param[in] tls_session to add extra pairs from.
257
 */
258
static inline CC_HINT(nonnull)
259
void fr_tls_session_extra_pairs_copy_to_child(request_t *child, fr_tls_session_t *tls_session)
260
0
{
261
0
  if (!fr_pair_list_empty(&tls_session->extra_pairs)) {
262
0
    MEM(fr_pair_list_copy(child->request_ctx, &child->request_pairs, &tls_session->extra_pairs) >= 0);
263
0
  }
264
0
}
Unexecuted instantiation: base.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: cache.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: conf.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: pairs.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: session.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: strerror.c:fr_tls_session_extra_pairs_copy_to_child
Unexecuted instantiation: virtual_server.c:fr_tls_session_extra_pairs_copy_to_child
265
266
/** Add an additional pair (copying it) to the list of extra pairs
267
 *
268
 * @param[in] tls_session to add extra pairs to.
269
 * @param[in] vp    to add to tls_session.
270
 */
271
static inline CC_HINT(nonnull)
272
void fr_tls_session_extra_pair_add(fr_tls_session_t *tls_session, fr_pair_t *vp)
273
0
{
274
0
  fr_pair_t *copy;
275
0
276
0
  MEM(copy = fr_pair_copy(tls_session, vp));
277
0
  fr_pair_append(&tls_session->extra_pairs, copy);
278
0
}
Unexecuted instantiation: base.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: cache.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: conf.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: pairs.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: session.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: strerror.c:fr_tls_session_extra_pair_add
Unexecuted instantiation: virtual_server.c:fr_tls_session_extra_pair_add
279
280
/** Add an additional pair to the list of extra pairs
281
 *
282
 * @param[in] tls_session to add extra pairs to.
283
 * @param[in] vp    to add to tls_session.
284
 */
285
static inline CC_HINT(nonnull)
286
void fr_tls_session_extra_pair_add_shallow(fr_tls_session_t *tls_session, fr_pair_t *vp)
287
0
{
288
0
  fr_assert(talloc_parent(vp) == tls_session);
289
0
  fr_pair_append(&tls_session->extra_pairs, vp);
290
0
}
Unexecuted instantiation: base.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: cache.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: conf.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: pairs.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: session.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: strerror.c:fr_tls_session_extra_pair_add_shallow
Unexecuted instantiation: virtual_server.c:fr_tls_session_extra_pair_add_shallow
291
292
293
int     fr_tls_session_password_cb(char *buf, int num, int rwflag, void *userdata);
294
295
unsigned int  fr_tls_session_psk_client_cb(SSL *ssl, UNUSED char const *hint,
296
               char *identity, unsigned int max_identity_len,
297
               unsigned char *psk, unsigned int max_psk_len);
298
299
unsigned int  fr_tls_session_psk_server_cb(SSL *ssl, const char *identity,
300
               unsigned char *psk, unsigned int max_psk_len);
301
302
void    fr_tls_session_info_cb(SSL const *s, int where, int ret);
303
304
void    fr_tls_session_msg_cb(int write_p, int msg_version, int content_type,
305
              void const *buf, size_t len, SSL *ssl, void *arg);
306
307
void    fr_tls_session_keylog_cb(const SSL *ssl, const char *line);
308
309
int   fr_tls_session_pairs_from_x509_cert(fr_pair_list_t *pair_list, TALLOC_CTX *ctx,
310
                    request_t *request, X509 *cert, bool der_decode) CC_HINT(nonnull);
311
312
int   fr_tls_session_client_hello_cb(SSL *ssl, int *al, void *arg);
313
314
int   fr_tls_session_recv(request_t *request, fr_tls_session_t *tls_session);
315
316
int     fr_tls_session_send(request_t *request, fr_tls_session_t *tls_session);
317
318
int     fr_tls_session_alert(request_t *request, fr_tls_session_t *tls_session, uint8_t level, uint8_t description);
319
320
unlang_action_t fr_tls_session_async_handshake_push(request_t *request, fr_tls_session_t *tls_session);
321
322
fr_tls_session_t *fr_tls_session_alloc_client(TALLOC_CTX *ctx, SSL_CTX *ssl_ctx);
323
324
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);
325
326
unlang_action_t fr_tls_new_session_push(request_t *request, fr_tls_conf_t const *tls_conf);
327
328
#ifdef __cplusplus
329
}
330
#endif
331
#endif /* WITH_TLS */