/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 */ |