The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
dtls.c
Go to the documentation of this file.
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: 2024e63690676be1101c2b4bc5e3dfcca370311c $
19 *
20 * @file lib/tls/dtls.c
21 * @brief Datagram Transport Layer Security (DTLS)
22 *
23 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 2024e63690676be1101c2b4bc5e3dfcca370311c $")
26USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
27
28#ifdef WITH_TLS
29#define LOG_PREFIX "tls"
30
31#include <openssl/hmac.h>
32#include <openssl/rand.h>
33#include <freeradius-devel/protocol/tls/freeradius.h>
34
35#include "base.h"
36#include "dtls.h"
37#include "log.h"
38#include "strerror.h"
39
40/** The key the DTLS cookies are generated with
41 *
42 * One per process, generated at startup. Rotating it would invalidate
43 * outstanding cookies, which costs a client one extra round trip, and nothing
44 * yet needs that.
45 */
46static uint8_t dtls_cookie_key[32];
47
48/** Build the cookie from #fr_socket_t in the #fr_tls_connection_t
49 *
50 * RFC 6347 section 4.2.1 suggests this construction:
51 *
52 * Cookie = HMAC(Secret, Client-IP, Client-Parameters)
53 *
54 * The application hands DTLS a #fr_socket_t, which identifies a particular network connection. This should
55 * be the same for all DTLS calls, and should point to something which has a lifetime shared with the DTLS
56 * connection.
57 *
58 * OpenSSL does not do this work itself. It's two pairs of cookie callbacks differ. For the stateless pair,
59 * used by SSL_stateless(), "the integrity of the entire cookie ... is automatically verified by HMAC", while
60 * for this pair (used for the DTLS HelloVerifyRequest), "the integrity of the cookie is not verified by
61 * OpenSSL. This is an application responsibility."
62 *
63 * @param[in] ssl the cookie is for.
64 * @param[out] out buffer of at least EVP_MAX_MD_SIZE octets.
65 * @param[out] out_len how much of `out` was written.
66 * @return
67 * - 0 on success.
68 * - -1 on failure.
69 */
70static int dtls_cookie_hmac(SSL *ssl, uint8_t *out, unsigned int *out_len)
71{
72 fr_tls_session_t *tls_session = fr_tls_session(ssl);
73
74 /*
75 * Nothing identifies the peer, so there is nothing to bind the
76 * cookie to, and a cookie which proves nothing is worse than
77 * refusing to make one.
78 */
79 if (!tls_session->cookie) {
80 fr_strerror_const("No cookie value set on the session");
81 return -1;
82 }
83
84#ifdef __COVERITY__
85 /*
86 * Coverity doesn't see HMAC() write the digest. Without the
87 * memset, Coverity reports out as uninitialised in the callers.
88 */
89 memset(out, 0, EVP_MAX_MD_SIZE);
90#endif
91
92 /*
93 * Hash the entire #fr_socket_t, as the application should set this once, and then never change
94 * it. This method means that we do exactly the same work for IPv4, Ipv6, and (potentially) Unix
95 * sockets with datagrams.
96 */
97 if (!HMAC(EVP_sha256(), dtls_cookie_key, sizeof(dtls_cookie_key),
98 (uint8_t const *) tls_session->cookie, sizeof(*tls_session->cookie),
99 out, out_len)) {
100 fr_tls_strerror_printf(NULL);
101 return -1;
102 }
103
104 return 0;
105}
106
107/** Generate the cookie which goes into a HelloVerifyRequest
108 *
109 * @param[in] ssl the cookie is for.
110 * @param[out] cookie to write. At most DTLS1_COOKIE_LENGTH octets.
111 * @param[out] cookie_len how much was written.
112 * @return
113 * - 1 on success, which is what OpenSSL wants.
114 * - 0 on failure, which aborts the handshake.
115 */
116int fr_dtls_cookie_generate_cb(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
117{
118 uint8_t hmac[EVP_MAX_MD_SIZE];
119 unsigned int hmac_len;
120
121 static_assert(EVP_MAX_MD_SIZE <= DTLS1_COOKIE_LENGTH,
122 "A SHA256 digest has to fit in a DTLS cookie");
123
124 if (dtls_cookie_hmac(ssl, hmac, &hmac_len) < 0) return 0;
125
126 memcpy(cookie, hmac, hmac_len);
127 *cookie_len = hmac_len;
128
129 return 1;
130}
131
132/** Check the cookie which came back in the second ClientHello
133 *
134 * @param[in] ssl the cookie is for.
135 * @param[in] cookie the client echoed back.
136 * @param[in] cookie_len its length.
137 * @return
138 * - 1 when the cookie is ours and is for this peer.
139 * - 0 otherwise, which makes OpenSSL ask for another one.
140 */
141int fr_dtls_cookie_verify_cb(SSL *ssl, unsigned char const *cookie, unsigned int cookie_len)
142{
143 uint8_t hmac[EVP_MAX_MD_SIZE];
144 unsigned int hmac_len;
145
146 if (dtls_cookie_hmac(ssl, hmac, &hmac_len) < 0) return 0;
147
148 if (cookie_len != hmac_len) return 0;
149
150 /*
151 * A constant time compare, so that a wrong cookie says only
152 * that it was wrong.
153 */
154 return CRYPTO_memcmp(cookie, hmac, hmac_len) == 0;
155}
156
157/** Generate the per-process cookie key
158 *
159 * Called once from fr_openssl_init().
160 *
161 * @return
162 * - 0 on success.
163 * - -1 on failure.
164 */
165int fr_dtls_cookie_init(void)
166{
167 if (RAND_bytes(dtls_cookie_key, sizeof(dtls_cookie_key)) != 1) {
168 fr_tls_strerror_printf(NULL);
169 return -1;
170 }
171
172 return 0;
173}
174
175/** Set up the parts of a session which only a datagram transport needs
176 *
177 * Called once per session, after the session knows its MTU, and only when the
178 * context was built with DTLS_method().
179 *
180 * @param[in] tls_session to configure.
181 * @param[in] conf the configuration
182 * @return
183 * - 0 on success.
184 * - -1 on failure.
185 */
186int fr_dtls_session_init(fr_tls_session_t *tls_session, fr_tls_conf_t const *conf)
187{
188 fr_assert(tls_session->socket_type == SOCK_DGRAM);
189
190 /*
191 * OpenSSL asks the BIO for the MTU of the socket underneath
192 * it. There is no socket under a memory BIO, so the question
193 * has no answer, and OpenSSL has to be told not to ask and
194 * given the figure instead.
195 *
196 <* Without both of these the handshake does not complete.
197 */
198 SSL_set_options(tls_session->ssl, SSL_OP_NO_QUERY_MTU);
199
200 /*
201 * @todo - we should have a way for the application to update the MTU based on expected headers.
202 * i.e. the MTU here should be the "raw" full-packet MTU, not the MTU of the application-layer
203 * contents.
204 *
205 * That's because the admin often can find out the raw MTU, or even guess, but knowing how large
206 * the application MTU is depends on IP version, etc.
207 */
208 tls_session->mtu = conf->fragment_size;
209 SSL_set_mtu(tls_session->ssl, tls_session->mtu);
210
211 /*
212 * OpenSSL writes one datagram per BIO_write(), sized to the
213 * MTU above. The buffer those writes land in is a byte
214 * stream, so without this the boundaries between them are
215 * lost, and the application would send a whole flight as one
216 * oversized datagram.
217 */
218 if (fr_tls_bio_dbuff_datagram_init(tls_session->from_ssl) < 0) {
219 fr_strerror_const("Failed initialising the datagram queue");
220 return -1;
221 }
222
223 return 0;
224}
225
226/** The retransmission timer has fired
227 *
228 * OpenSSL holds the flight it last sent, and DTLSv1_handle_timeout() puts it
229 * back into the outgoing buffer with fresh record sequence numbers. Writing
230 * it is just like any other write.
231 *
232 * @param[in] tl the timer fired on. Unused.
233 * @param[in] now Unused.
234 * @param[in] uctx the fr_tls_connection_t.
235 */
236static void _fr_dtls_timer_expired(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
237{
238 fr_tls_connection_t *conn = talloc_get_type_abort(uctx, fr_tls_connection_t);
239 request_t *request = conn->request;
240 int ret;
241
242 fr_assert(request != NULL);
243
244 RDEBUG2("(TLS) %s - DTLS retransmission timer fired", conn->name);
245
246 /*
247 * DTLSv1_handle_timeout() asks OpenSSL to retransmit the pending data, and writes those records
248 * via fr_tls_session_msg_cb(). That callback reads the request back off the SSL *, which means
249 * that the request has to be bound across the call. Without the bind, the callback logs the
250 * retransmitted flight with no request.
251 *
252 * A return of < 0 means the handshake has given up, which
253 * OpenSSL reports rather than this code deciding it.
254 */
255 fr_tls_session_request_bind(conn->tls_session->ssl, request);
256 ret = DTLSv1_handle_timeout(conn->tls_session->ssl);
257 fr_tls_session_request_unbind(conn->tls_session->ssl);
258
259 if (ret < 0) {
260 fr_tls_log_perror(request, "DTLS handshake timed out");
261 fr_tls_session_error_add(request, FR_ERROR_VALUE_DTLS_HANDSHAKE_TIMEOUT);
262 fr_tls_connection_failed(conn, TLS_CONNECTION_FAIL_TLS);
263 return;
264 }
265
266 if (fr_tls_connection_write(conn) < 0) {
267 fr_tls_connection_failed(conn, TLS_CONNECTION_FAIL_APPLICATION);
268 return;
269 }
270
271 /*
272 * The interval doubles on each retransmission, so the next one
273 * is not the same as the last one.
274 */
275 fr_dtls_timer_update(conn);
276}
277
278/** Arm or disarm the retransmission timer
279 *
280 * OpenSSL changes the timer value on every round. When there's
281 * nothing outstanding, the timer is disarmed.
282 *
283 * @param[in] conn to update the timer for.
284 */
285void fr_dtls_timer_update(fr_tls_connection_t *conn)
286{
287 request_t *request = conn->request;
288 struct timeval tv;
289
290 /*
291 * A stream connection has no timer list, and nothing to time.
292 */
293 if (!conn->tl) return;
294
295 /*
296 * Anything other than 1 means no timer is wanted.
297 */
298 if (DTLSv1_get_timeout(conn->tls_session->ssl, &tv) != 1) {
299 FR_TIMER_DELETE(&conn->timer_ev);
300 return;
301 }
302
303 FR_TIMER_DELETE(&conn->timer_ev);
304
305 if (fr_timer_in(conn, conn->tl, &conn->timer_ev, fr_time_delta_from_timeval(&tv),
306 false, _fr_dtls_timer_expired, conn) < 0) {
307 REDEBUG("(TLS) %s - Failed arming the DTLS retransmission timer", conn->name);
308 fr_tls_connection_failed(conn, TLS_CONNECTION_FAIL_APPLICATION);
309 }
310}
311
312#endif /* WITH_TLS */
static uint8_t request[4]
#define USES_APPLE_DEPRECATED_API
Definition build.h:590
#define RCSID(id)
Definition build.h:603
#define UNUSED
Definition build.h:415
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
static rs_t * conf
Definition radsniff.c:52
static fr_time_delta_t fr_time_delta_from_timeval(struct timeval const *tv)
Definition time.h:597
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define FR_TIMER_DELETE(_ev_p)
Definition timer.h:105
#define fr_timer_in(...)
Definition timer.h:87
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_sbuff_err_t char ** out
Definition value.h:1061