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