Radcli library 2.0.0
A simple radius library -- legacy API reference
Loading...
Searching...
No Matches
tls.c
1/*
2 * Copyright (c) 2014-2026, Nikos Mavrogiannopoulos. All rights reserved.
3 * Copyright (c) 2015, Red Hat, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <config.h>
27#include <includes.h>
28#include <radcli/radcli.h>
29#include <radcli/radcli2.h> /* RADCLI_DISCONNECT_REQUEST/RADCLI_COA_REQUEST,
30 * for tls_recvfrom()'s DAE-over-RadSec demux */
31#include "util.h"
32#include "options.h"
33#include "tls.h"
34
35#ifdef HAVE_GNUTLS
36
49
50#include <gnutls/gnutls.h>
51#include <gnutls/dtls.h>
52#include <pthread.h>
53#include <time.h>
54#include <poll.h>
55
56#define DEFAULT_DTLS_SECRET "radius/dtls"
57#define DEFAULT_TLS_SECRET "radsec"
58
59typedef struct tls_int_st {
60 char hostname[256]; /* server's hostname */
61 unsigned port; /* server's port */
62 struct sockaddr_storage our_sockaddr;
63 gnutls_session_t session;
64 int sockfd;
65 unsigned init;
66 unsigned handshake_done; /* set only once gnutls_handshake() succeeds;
67 * guards deinit_session()'s gnutls_bye(),
68 * which is invalid on a session that never
69 * finished (or started) its handshake. */
70 unsigned need_restart;
71 unsigned skip_hostname_check; /* whether to verify hostname */
72 pthread_mutex_t lock;
73 time_t last_msg; /* last send OR receive -- when the next watchdog is due */
74 time_t last_recv; /* last receive only -- REQ-WATCHDOG-NET-003's dead-peer clock */
75} tls_int_st;
76
77typedef struct tls_st {
78 gnutls_psk_client_credentials_t psk_cred;
79 gnutls_certificate_credentials_t x509_cred;
80 struct tls_int_st ctx; /* one for ACCT and another for AUTH */
81 unsigned flags; /* the flags set on init */
82 rc_handle *rh; /* a pointer to our owner */
83} tls_st;
84
86
87static int restart_session(rc_handle *rh, tls_st *st);
88
89/*- rc_sockets_override.get_fd: return st's session socket, restarting the
90 * session first if it was marked for restart.
91 *
92 * @param ptr the tls_st for this session.
93 * @param our_sockaddr unused; part of the get_fd calling convention.
94 * @return the session socket, or -1 if a needed restart failed.
95 -*/
96static int tls_get_fd(void *ptr, struct sockaddr *our_sockaddr)
97{
98 tls_st *st = ptr;
99 if (st->ctx.need_restart != 0) {
100 if (restart_session(st->rh, st) < 0)
101 return -1;
102 }
103 return st->ctx.sockfd;
104}
105
106/*- rc_sockets_override.get_active_fd: return st's current session socket
107 * without attempting a restart.
108 *
109 * @param ptr the tls_st for this session.
110 * @return the session socket.
111 -*/
112static int tls_get_active_fd(void *ptr)
113{
114 tls_st *st = ptr;
115 return st->ctx.sockfd;
116}
117
118/* Used from the GNUTLS_E_AGAIN/GNUTLS_E_INTERRUPTED retry branches of
119 * tls_sendto()/tls_recvfrom(): GnuTLS requires retrying the record call
120 * with the same arguments once events is ready on the session fd. Waits
121 * up to the configured radius_timeout, safe against poll() itself being
122 * interrupted by a signal (retried against the same, non-extending
123 * deadline, so neither a signal nor a run of spurious EAGAINs can make
124 * the wait unbounded). */
125/*- Wait for a session socket to become ready for a retried GnuTLS record
126 * call, or mark the session for restart on timeout/error.
127 *
128 * @param st the session to wait on.
129 * @param events POLLIN or POLLOUT, matching the record call being retried.
130 * @param what a short verb ("send"/"receive") for the timeout log message.
131 * @return 1 if the caller should retry the gnutls_record_*() call, or -1
132 * if it should give up (errno set to EIO, session marked for restart).
133 -*/
134static int tls_wait_or_give_up(tls_st *st, short events, const char *what)
135{
136 double start_time = rc_getmtime();
137 int timeout = rc_conf_int_id(st->rh, OPT_RADIUS_TIMEOUT);
138
139 if (timeout <= 0)
140 timeout = 1;
141
142 for (; timeout > 0; timeout -= (int)(rc_getmtime() - start_time)) {
143 struct pollfd pfd = { st->ctx.sockfd, events, 0 };
144 int ret = poll(&pfd, 1, timeout * 1000);
145
146 if (ret > 0)
147 return 1;
148 if (ret == 0)
149 break;
150 if (errno != EINTR) {
151 rc_log(LOG_ERR, "%s: poll: %s", __func__, strerror(errno));
152 goto give_up;
153 }
154 /* poll() itself was interrupted; retry against the same
155 * deadline rather than treating it as a timeout. */
156 }
157 rc_log(LOG_ERR, "%s: timeout waiting to %s TLS data", __func__, what);
158give_up:
159 errno = EIO;
160 st->ctx.need_restart = 1;
161 return -1;
162}
163
164/*- rc_sockets_override.sendto: send buf over st's GnuTLS session,
165 * retrying on GNUTLS_E_AGAIN/GNUTLS_E_INTERRUPTED via
166 * tls_wait_or_give_up(), restarting the session first if needed.
167 *
168 * @param ptr the tls_st for this session.
169 * @param sockfd unused; part of the sendto calling convention.
170 * @param buf the data to send.
171 * @param len buf's length in bytes.
172 * @param flags unused; part of the sendto calling convention.
173 * @param dest_addr unused; part of the sendto calling convention.
174 * @param addrlen unused; part of the sendto calling convention.
175 * @return the number of bytes sent, or -1 on failure (errno set to EIO).
176 -*/
177static ssize_t tls_sendto(void *ptr, int sockfd,
178 const void *buf, size_t len,
179 int flags, const struct sockaddr *dest_addr,
180 socklen_t addrlen)
181{
182 tls_st *st = ptr;
183 int ret;
184
185 if (st->ctx.need_restart != 0) {
186 if (restart_session(st->rh, st) < 0) {
187 errno = EIO;
188 return -1;
189 }
190 }
191
192 for (;;) {
193 ret = gnutls_record_send(st->ctx.session, buf, len);
194 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
195 if (tls_wait_or_give_up(st, POLLOUT, "send") < 0)
196 return -1;
197 continue;
198 }
199
200 if (ret < 0) {
201 rc_log(LOG_ERR, "%s: error in sending: %s", __func__,
202 gnutls_strerror(ret));
203 errno = EIO;
204 st->ctx.need_restart = 1;
205 return -1;
206 }
207
208 break;
209 }
210
211 st->ctx.last_msg = time(0);
212 return ret;
213}
214
215/*- rc_sockets_override.lock: acquire st's session lock.
216 *
217 * @param ptr the tls_st for this session.
218 * @return pthread_mutex_lock()'s return value.
219 -*/
220static int tls_lock(void *ptr)
221{
222 tls_st *st = ptr;
223
224 return pthread_mutex_lock(&st->ctx.lock);
225}
226
227/*- rc_sockets_override.unlock: release st's session lock.
228 *
229 * @param ptr the tls_st for this session.
230 * @return pthread_mutex_unlock()'s return value.
231 -*/
232static int tls_unlock(void *ptr)
233{
234 tls_st *st = ptr;
235
236 return pthread_mutex_unlock(&st->ctx.lock);
237}
238
239/*- rc_sockets_override.recvfrom: read one reply from st's GnuTLS session,
240 * retrying on GNUTLS_E_AGAIN/GNUTLS_E_INTERRUPTED via tls_wait_or_give_up(),
241 * and diverting a DAE-over-RadSec CoA/Disconnect
242 * packet straight to lib/dae.c's pipeline instead of returning it here
243 * (RFC 6614 §2.1/§2.5, RFC 7360 §2.2: one connection carries every packet
244 * type).
245 *
246 * @param ptr the tls_st for this session.
247 * @param sockfd unused; part of the recvfrom calling convention.
248 * @param buf destination buffer for the received record.
249 * @param len buf's capacity in bytes.
250 * @param flags unused; part of the recvfrom calling convention.
251 * @param src_addr unused; part of the recvfrom calling convention.
252 * @param addrlen unused; part of the recvfrom calling convention.
253 * @return the number of bytes received, or -1 on failure (errno set to
254 * EINTR on a received alert, EIO otherwise).
255 -*/
256static ssize_t tls_recvfrom(void *ptr, int sockfd,
257 void *buf, size_t len,
258 int flags, struct sockaddr *src_addr,
259 socklen_t * addrlen)
260{
261 tls_st *st = ptr;
262 int ret;
263
264 for (;;) {
265 ret = gnutls_record_recv(st->ctx.session, buf, len);
266 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
267 if (tls_wait_or_give_up(st, POLLIN, "receive") < 0)
268 return -1;
269 continue;
270 }
271
272 /* RFC 6614 SS2.1/SS2.5, RFC 7360 SS2.2: one port, one connection
273 * carries every packet type. A Disconnect-Request/CoA-Request
274 * arriving here is never the reply this caller (radcli_transport_
275 * exchange(), waiting for an Access-Accept/Accounting-Response) is
276 * waiting for -- hand it to lib/dae.c's RadSec pipeline right here
277 * (the thread already holding this session's lock, mid-exchange,
278 * is exactly the thread that must not miss it) and keep waiting
279 * for the actual reply. */
280 if (ret >= 1 &&
281 (((const uint8_t *)buf)[0] == RADCLI_DISCONNECT_REQUEST ||
282 ((const uint8_t *)buf)[0] == RADCLI_COA_REQUEST)) {
283 radcli2_priv_dae_on_radsec_packet(st->rh, buf, (size_t)ret);
284 continue;
285 }
286 break;
287 }
288
289 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
290 rc_log(LOG_ERR, "%s: received alert: %s", __func__,
291 gnutls_alert_get_name(gnutls_alert_get(st->ctx.session)));
292 errno = EINTR;
293 return -1;
294 }
295
296 /* RFC6614 says: "After the TLS session is established, RADIUS packet payloads are
297 * exchanged over the encrypted TLS tunnel. In RADIUS/UDP, the
298 * packet size can be determined by evaluating the size of the
299 * datagram that arrived. Due to the stream nature of TCP and TLS,
300 * this does not hold true for RADIUS/TLS packet exchange.",
301 *
302 * That is correct in principle but it fails to associate the length with
303 * the TLS record boundaries. Here, when in TLS, we assume that a single TLS
304 * record holds a single radius packet. It wouldn't make sense anyway to send
305 * multiple TLS records for a single packet.
306 */
307
308 if (ret <= 0) {
309 rc_log(LOG_ERR, "%s: error in receiving: %s", __func__,
310 gnutls_strerror(ret));
311 errno = EIO;
312 st->ctx.need_restart = 1;
313 return -1;
314 }
315
316 st->ctx.last_msg = time(0);
317 st->ctx.last_recv = st->ctx.last_msg;
318 return ret;
319}
320
321/*- GnuTLS certificate-verification callback: verify the peer's
322 * certificate chain and, unless skip_hostname_check is set, that its
323 * hostname matches.
324 *
325 * @param session the GnuTLS session being handshaked; its tls_int_st is
326 * read back via gnutls_session_get_ptr().
327 * @return 0 if the certificate is acceptable, GNUTLS_E_CERTIFICATE_ERROR
328 * otherwise.
329 -*/
330static int cert_verify_callback(gnutls_session_t session)
331{
332 unsigned int status;
333 int ret;
334 struct tls_int_st *ctx;
335 gnutls_datum_t out;
336
337 /* read hostname */
338 ctx = gnutls_session_get_ptr(session);
339 if (ctx == NULL)
340 return GNUTLS_E_CERTIFICATE_ERROR;
341
342 if (ctx->skip_hostname_check)
343 ret = gnutls_certificate_verify_peers2(session, &status);
344 else
345 ret = gnutls_certificate_verify_peers3(session, ctx->hostname, &status);
346 if (ret < 0) {
347 rc_log(LOG_ERR, "%s: error in certificate verification: %s",
348 __func__, gnutls_strerror(ret));
349 return GNUTLS_E_CERTIFICATE_ERROR;
350 }
351
352 if (status != 0) {
353 ret =
354 gnutls_certificate_verification_status_print(status,
355 gnutls_certificate_type_get
356 (session),
357 &out, 0);
358 if (ret < 0) {
359 return GNUTLS_E_CERTIFICATE_ERROR;
360 }
361 rc_log(LOG_INFO, "%s: certificate: %s", __func__, out.data);
362 gnutls_free(out.data);
363 return GNUTLS_E_CERTIFICATE_ERROR;
364 }
365
366 return 0;
367}
368
369/*- Tear down a GnuTLS session: send close_notify (if the handshake
370 * completed), deinit the GnuTLS session, destroy the lock, and close the
371 * socket.
372 *
373 * @param ses the session to tear down; ses->init is left 0.
374 -*/
375static void deinit_session(tls_int_st *ses)
376{
377 if (ses->init != 0) {
378 int ret;
379 ses->init = 0;
380 if (ses->session) {
381 /* Send close_notify before closing the socket so the peer
382 * receives a proper TLS/DTLS shutdown alert. Only valid
383 * once the handshake actually completed -- e.g. a
384 * connect() failure leaves an initialized session with
385 * no negotiated cipher state, and gnutls_bye() on that
386 * is not meaningful. */
387 if (ses->sockfd != -1 && ses->handshake_done) {
388 do {
389 ret = gnutls_bye(ses->session, GNUTLS_SHUT_WR);
390 } while (ret == GNUTLS_E_INTERRUPTED);
391 }
392 gnutls_deinit(ses->session);
393 }
394 pthread_mutex_destroy(&ses->lock);
395 if (ses->sockfd != -1)
396 close(ses->sockfd);
397 }
398}
399
400/*- Resolve hostname, open a socket, and complete the GnuTLS (D)TLS
401 * handshake, filling in ses on success.
402 *
403 * @param rh a handle to parsed configuration.
404 * @param ses the session struct to initialize.
405 * @param hostname the server to resolve and connect to.
406 * @param port the server's TLS/DTLS port.
407 * @param our_sockaddr set to the local address the socket bound/connected
408 * from.
409 * @param timeout the handshake timeout in seconds.
410 * @param secflags PSK/certificate security flags controlling credential
411 * setup (see callers for the accepted bits).
412 * @return 0 on success, negative on failure (ses is left safe to pass to
413 * deinit_session()).
414 -*/
415static int init_session(rc_handle *rh, tls_int_st *ses,
416 const char *hostname, unsigned port,
417 struct sockaddr_storage *our_sockaddr,
418 int timeout,
419 unsigned secflags)
420{
421 int sockfd, ret, e, sock_flags;
422 struct addrinfo *info;
423 char *p;
424 unsigned flags = 0;
425 unsigned cred_set = 0;
426 tls_st *st = rh->so.ptr;
427
428 ses->sockfd = -1;
429 ses->init = 1;
430 ses->handshake_done = 0;
431
432 {
433 /* Recursive, not the default (non-recursive) type: DAE-over-
434 * RadSec's demux (lib/dae.c's radcli2_priv_dae_on_radsec_packet(),
435 * called inline from tls_recvfrom() below) can itself need to
436 * send an immediate reply -- a retransmission's cached ACK/NAK
437 * (PROCESS_DUP_ANSWERED), or an RFC 6614 SS2.5 406 NAK when
438 * dynamic authorization isn't enabled -- via this same rh's
439 * so.sendto()/so.lock(), while tls_recvfrom() itself is being
440 * called from inside radcli_transport_exchange() (lib/sendserver.c),
441 * which already holds this exact lock for its entire send-and-
442 * wait cycle, on this same thread. A plain mutex would deadlock
443 * (or be undefined behavior) the moment that reply path is taken
444 * from that call chain; a recursive one simply nests. */
445 pthread_mutexattr_t attr;
446
447 pthread_mutexattr_init(&attr);
448 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
449 pthread_mutex_init(&ses->lock, &attr);
450 pthread_mutexattr_destroy(&attr);
451 }
452 sockfd = socket(our_sockaddr->ss_family, (secflags&SEC_FLAG_DTLS)?SOCK_DGRAM:SOCK_STREAM, 0);
453 if (sockfd < 0) {
454 rc_log(LOG_ERR,
455 "%s: cannot open socket", __func__);
456 ret = -1;
457 goto cleanup;
458 }
459
460 if (our_sockaddr->ss_family == AF_INET)
461 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
462 else
463 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
464
465 ses->sockfd = sockfd;
466
467 /* Initialize DTLS */
468
469 flags = GNUTLS_CLIENT;
470 if (secflags&SEC_FLAG_DTLS)
471 flags |= GNUTLS_DATAGRAM;
472 ret = gnutls_init(&ses->session, flags);
473 if (ret < 0) {
474 rc_log(LOG_ERR,
475 "%s: error in gnutls_init(): %s", __func__, gnutls_strerror(ret));
476 ret = -1;
477 goto cleanup;
478 }
479
480 memcpy(&ses->our_sockaddr, our_sockaddr, sizeof(*our_sockaddr));
481 if (!(secflags&SEC_FLAG_DTLS)) {
482 if (timeout > 0) {
483 gnutls_handshake_set_timeout(ses->session, timeout*1000);
484 } else {
485 gnutls_handshake_set_timeout(ses->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
486 }
487 } else { /* DTLS */
488 if (timeout > 0)
489 gnutls_dtls_set_timeouts(ses->session, 1000, timeout*1000);
490 }
491
492 gnutls_transport_set_int(ses->session, sockfd);
493 gnutls_session_set_ptr(ses->session, ses);
494
495 p = rc_conf_str_id(rh, OPT_TLS_VERIFY_HOSTNAME);
496 if (p && (strcasecmp(p, "false") == 0 || strcasecmp(p, "no") == 0)) {
497 ses->skip_hostname_check = 1;
498 }
499
500 if (st && st->psk_cred) {
501 cred_set = 1;
502 gnutls_credentials_set(ses->session,
503 GNUTLS_CRD_PSK, st->psk_cred);
504
505 ret = gnutls_priority_set_direct(ses->session, "NORMAL:-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK:-VERS-TLS1.0", NULL);
506 if (ret < 0) {
507 ret = -1;
508 rc_log(LOG_ERR,
509 "%s: error in setting PSK priorities: %s",
510 __func__, gnutls_strerror(ret));
511 goto cleanup;
512 }
513 } else if (st) {
514 cred_set = 1;
515 if (st->x509_cred) {
516 gnutls_credentials_set(ses->session,
517 GNUTLS_CRD_CERTIFICATE,
518 st->x509_cred);
519 }
520
521 gnutls_set_default_priority(ses->session);
522 }
523
524 gnutls_server_name_set(ses->session, GNUTLS_NAME_DNS,
525 hostname, strlen(hostname));
526
527 info =
528 rc_getaddrinfo(hostname, PW_AI_AUTH);
529 if (info == NULL) {
530 ret = -1;
531 rc_log(LOG_ERR, "%s: cannot resolve %s", __func__,
532 hostname);
533 goto cleanup;
534 }
535
536 if (port != 0) {
537 if (info->ai_addr->sa_family == AF_INET)
538 ((struct sockaddr_in *)info->ai_addr)->sin_port =
539 htons(port);
540 else
541 ((struct sockaddr_in6 *)info->ai_addr)->sin6_port =
542 htons(port);
543 } else {
544 rc_log(LOG_ERR, "%s: no port specified for server %s",
545 __func__, hostname);
546 ret = -1;
547 goto cleanup;
548 }
549
550 strlcpy(ses->hostname, hostname, sizeof(ses->hostname));
551 ses->port = port;
552
553 if (cred_set == 0) {
554 rc_log(LOG_CRIT,
555 "%s: neither tls-ca-file or a PSK key are configured",
556 __func__);
557 ret = -1;
558 goto cleanup;
559 }
560
561 /* we connect since we are talking to a single server */
562 ret = connect(sockfd, info->ai_addr, info->ai_addrlen);
563 freeaddrinfo(info);
564 if (ret == -1) {
565 e = errno;
566 ret = -1;
567 rc_log(LOG_CRIT, "%s: cannot connect to %s: %s",
568 __func__, hostname, strerror(e));
569 goto cleanup;
570 }
571
572 /* Switch to non-blocking mode before the handshake, so that both
573 * gnutls_handshake() (bounded above via gnutls_handshake_set_timeout()/
574 * gnutls_dtls_set_timeouts()) and the post-handshake record I/O in
575 * tls_sendto()/tls_recvfrom() can actually observe GNUTLS_E_AGAIN and
576 * take the bounded poll()-and-retry path in tls_wait_or_give_up(),
577 * instead of blocking in the kernel with no timeout at all. */
578 sock_flags = fcntl(sockfd, F_GETFL, 0);
579 if (sock_flags == -1 ||
580 fcntl(sockfd, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
581 e = errno;
582 ret = -1;
583 rc_log(LOG_CRIT, "%s: cannot set socket non-blocking: %s",
584 __func__, strerror(e));
585 goto cleanup;
586 }
587
588 rc_log(LOG_DEBUG,
589 "%s: performing TLS/DTLS handshake with [%s]:%d",
590 __func__, hostname, port);
591 do {
592 ret = gnutls_handshake(ses->session);
593 if (ret == GNUTLS_E_LARGE_PACKET)
594 break;
595 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
596
597 if (ret < 0) {
598 rc_log(LOG_ERR, "%s: error in handshake: %s",
599 __func__, gnutls_strerror(ret));
600 ret = -1;
601 goto cleanup;
602 }
603
604 ses->handshake_done = 1;
605 /* A freshly completed handshake counts as session activity: without
606 * this, last_msg stays at its zeroed-struct initial value until the
607 * first actual send/receive, which radcli_ctx_get_poll()'s watchdog-
608 * deadline math (lib/dae.c) would otherwise read as "session has been
609 * idle since the epoch" and report an already-overdue deadline right
610 * after radcli_dae_start()'s eager connect (REQ-DAE-INIT-010). */
611 ses->last_msg = time(0);
612 ses->last_recv = ses->last_msg;
613 return 0;
614 cleanup:
615 deinit_session(ses);
616 return ret;
617
618}
619
620/*- Reconnect st's session in place, replacing its tls_int_st with a freshly
621 * established one. Every call site only ever calls this when the session is
622 * already known to need it (a send/recv failure already set need_restart,
623 * or rc_init_tls() preset it before the first connection), so this always
624 * reinitializes unconditionally -- no rate-limiting, nothing to bypass.
625 *
626 * @param rh a handle to parsed configuration.
627 * @param st the session to restart.
628 * @return 0 on success, -1 if reinitialization failed (st is left unchanged
629 * on failure).
630 -*/
631static int restart_session(rc_handle *rh, tls_st *st)
632{
633 /* init_session() assumes a zeroed struct: REQ-NET-NET-016 */
634 struct tls_int_st tmps = { 0 };
635 int ret;
636 int timeout;
637
638 timeout = rc_conf_int_id(rh, OPT_RADIUS_TIMEOUT);
639
640 /* reinitialize this session */
641 ret = init_session(rh, &tmps, st->ctx.hostname, st->ctx.port, &st->ctx.our_sockaddr, timeout, st->flags);
642 if (ret < 0) {
643 rc_log(LOG_ERR, "%s: error in re-initializing TLS session", __func__);
644 return -1;
645 }
646
647 if (tmps.sockfd == st->ctx.sockfd)
648 st->ctx.sockfd = -1;
649 deinit_session(&st->ctx);
650 memcpy(&st->ctx, &tmps, sizeof(tmps));
651 st->ctx.need_restart = 0;
652
653 return 0;
654}
655
656/*- Return the file descriptor of the TLS/DTLS session -- also usable as
657 * a test for whether TLS or DTLS are in use.
658 *
659 * @param rh a handle to parsed configuration.
660 * @return the file descriptor used by the TLS session, or -1 on error.
661 -*/
662int radcli2_priv_tls_fd(rc_handle * rh)
663{
664 tls_st *st;
665
666 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
667 return -1;
668
669 st = rh->so.ptr;
670
671 if (st->ctx.init != 0) {
672 return st->ctx.sockfd;
673 }
674 return -1;
675}
676
677/*- Return the time of the last message sent or received on rh's TLS/DTLS
678 * session (tls_int_st.last_msg, updated by every successful send/receive
679 * on this session, including radcli2_priv_tls_dae_send()). Used by lib/
680 * dae.c's radcli_ctx_get_poll() to compute a watchdog deadline
681 * (watchdog-interval) without lib/dae.c needing to see the private
682 * tls_int_st layout.
683 *
684 * @param rh a handle to parsed configuration.
685 * @return the last-activity timestamp, or 0 if rh's transport is not
686 * TLS/DTLS or the session is not yet initialized.
687 -*/
688time_t radcli2_priv_tls_last_msg(rc_handle * rh)
689{
690 tls_st *st;
691
692 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
693 return 0;
694
695 st = rh->so.ptr;
696
697 if (st->ctx.init != 0) {
698 return st->ctx.last_msg;
699 }
700 return 0;
701}
702
703/*- Return the time of the last record actually *received* on rh's TLS/DTLS
704 * session (tls_int_st.last_recv) -- unlike radcli2_priv_tls_last_msg(),
705 * never advanced by a send. Used by lib/dae.c's radcli2_priv_dae_send_watchdog()
706 * (REQ-WATCHDOG-NET-003) to detect a peer that has gone silent while the
707 * connection itself is still technically open: sending watchdogs into that
708 * silence would keep radcli2_priv_tls_last_msg() looking fresh forever,
709 * masking exactly the condition this is meant to catch.
710 *
711 * @param rh a handle to parsed configuration.
712 * @return the last-receive timestamp, or 0 if rh's transport is not
713 * TLS/DTLS or the session is not yet initialized.
714 -*/
715time_t radcli2_priv_tls_last_recv(rc_handle * rh)
716{
717 tls_st *st;
718
719 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
720 return 0;
721
722 st = rh->so.ptr;
723
724 if (st->ctx.init != 0) {
725 return st->ctx.last_recv;
726 }
727 return 0;
728}
729
730/*- Force rh's TLS/DTLS session to reconnect now, the same way an actual
731 * send/recv error already does (need_restart) -- used by lib/dae.c's
732 * radcli2_priv_dae_send_watchdog() (REQ-WATCHDOG-NET-003) when the peer is presumed
733 * dead from elapsed time alone, with no socket-level error to set
734 * need_restart on its own.
735 *
736 * @param rh a handle to parsed configuration.
737 * @return 0 on success, -1 if rh's transport is not TLS/DTLS or
738 * reconnection failed.
739 -*/
740int radcli2_priv_tls_force_reconnect(rc_handle * rh)
741{
742 tls_st *st;
743
744 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
745 return -1;
746
747 st = rh->so.ptr;
748 st->ctx.need_restart = 1;
749 return restart_session(rh, st);
750}
751
752/*- Probe an established TLS/DTLS session's liveness and reconnect if it is
753 * dead. Once watchdog-interval has elapsed since the session's last
754 * activity, sends an RFC 5997 Status-Server watchdog
755 * (radcli2_priv_dae_send_watchdog(), REQ-WATCHDOG-NET-001) -- which itself already
756 * detects and reconnects from a peer gone silent for 2.5x that interval
757 * (REQ-WATCHDOG-NET-003), so this one call covers both probing and recovering. A
758 * dead session is normally detected and reconnected transparently on the
759 * next request anyway; this exists for a caller that wants that detected
760 * proactively instead (e.g. from a dedicated watchdog thread), same as
761 * before this used a TLS heartbeat for it.
762 *
763 * @param rh a handle to parsed configuration.
764 * @return 0 on success or when TLS/DTLS is not in use, -1 if a
765 * known-broken session could not be re-established.
766 -*/
767int radcli2_priv_check_tls(rc_handle * rh)
768{
769 tls_st *st;
770 int interval;
771
772 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
773 return 0;
774
775 st = rh->so.ptr;
776
777 if (st->ctx.init == 0)
778 return 0;
779
780 if (st->ctx.need_restart != 0)
781 return restart_session(rh, st) < 0 ? -1 : 0;
782
783 interval = rc_conf_int_id(rh, OPT_WATCHDOG_INTERVAL);
784 if (interval > 0 && time(0) - st->ctx.last_msg >= interval)
785 radcli2_priv_dae_send_watchdog((radcli_ctx *)rh);
786
787 return 0;
788}
789
790/*- Force the TLS/DTLS handshake now, rather than waiting for the first
791 * ordinary rc_auth()/rc_acct() call to trigger it lazily (rc_init_tls()'s
792 * normal need_restart=1 deferral) -- used by radcli_dae_start() so
793 * enabling DAE makes the NAS reachable immediately, matching the UDP
794 * listener's own immediate bind().
795 *
796 * @param rh a handle to parsed configuration.
797 * @return 0 on success or if already connected and healthy, -1 if rh's
798 * transport is not TLS/DTLS or reconnection failed.
799 -*/
800int radcli2_priv_tls_ensure_connected(rc_handle *rh)
801{
802 tls_st *st;
803
804 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
805 return -1;
806
807 st = rh->so.ptr;
808
809 if (st->ctx.init != 0 && st->ctx.need_restart == 0)
810 return 0; /* already connected and healthy */
811
812 return restart_session(rh, st);
813}
814
815/*- Make one non-blocking attempt (never retries on GNUTLS_E_AGAIN, unlike
816 * the ordinary tls_recvfrom() path) to read one already-available record
817 * into buf (capacity cap), taking and releasing rh's session lock itself
818 * with a non-blocking trylock -- so a concurrent, already-in-flight
819 * radcli_transport_exchange() (which holds that lock for its entire
820 * send-and-wait cycle) simply means "nothing ready this call" rather than
821 * blocking the caller's event loop; that in-flight exchange's own
822 * tls_recvfrom() is what will actually see and demux the record in that
823 * case.
824 *
825 * Unlike a typical lock/read/unlock helper, this LEAVES rh's session lock
826 * HELD on success (return >0): the caller (lib/dae.c's
827 * radcli_ctx_dispatch()) must call radcli2_priv_tls_dae_poll_done() once
828 * it has entirely finished with that record, including any nested call
829 * into radcli2_priv_dae_on_radsec_packet(). This is deliberate, not an
830 * oversight: radcli2_priv_dae_on_radsec_packet() takes a second lock of
831 * its own (lib/dae.c's per-dae radsec_lock) and may, from inside that
832 * second lock, need the session lock again (a queued reply send) --
833 * consistently nesting the session lock *outside* radsec_lock on every
834 * call path (this one, and tls_recvfrom()'s inline demux, which already
835 * holds the session lock for its entire enclosing
836 * radcli_transport_exchange() call before radsec_lock is ever taken) is
837 * what avoids an AB-BA lock-order inversion between the two.
838 *
839 * @param rh a handle to parsed configuration.
840 * @param buf destination for the record.
841 * @param cap buf's capacity in bytes.
842 * @return the record length (>0, lock left held) on success, 0 (lock
843 * already released) if nothing was ready or the trylock was contended,
844 * -1 (lock already released) on a session error.
845 -*/
846int radcli2_priv_tls_dae_poll(rc_handle *rh, uint8_t *buf, size_t cap)
847{
848 tls_st *st;
849 int ret;
850
851 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
852 return 0;
853
854 st = rh->so.ptr;
855 if (st->ctx.init == 0 || st->ctx.need_restart != 0)
856 return 0; /* not connected -- nothing to poll; reconnecting is
857 * radcli_dae_start()'s/an ordinary request's job, not
858 * this opportunistic idle-time check's. */
859
860 /* A trylock, not a blocking lock: if radcli_transport_exchange() is
861 * mid-exchange on another thread, it already owns this session's
862 * only read path and will itself see and demux any DAE record that
863 * arrives while it holds the lock (tls_recvfrom()'s own inline
864 * demux, above) -- so contention here simply means "nothing new to
865 * report this call", never a stall of the caller's event loop. */
866 if (pthread_mutex_trylock(&st->ctx.lock) != 0)
867 return 0;
868
869 ret = gnutls_record_recv(st->ctx.session, buf, cap);
870
871 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
872 pthread_mutex_unlock(&st->ctx.lock);
873 return 0; /* nothing ready this call -- no retry, unlike tls_recvfrom() */
874 }
875
876 if (ret <= 0) {
877 rc_log(LOG_ERR, "%s: error in receiving: %s", __func__,
878 gnutls_strerror(ret));
879 st->ctx.need_restart = 1;
880 pthread_mutex_unlock(&st->ctx.lock);
881 return -1;
882 }
883
884 st->ctx.last_msg = time(0);
885 st->ctx.last_recv = st->ctx.last_msg;
886 /* Lock deliberately left held -- see this function's own doc comment
887 * above and radcli2_priv_tls_dae_poll_done() below. */
888 return ret;
889}
890
891/*- Release the lock radcli2_priv_tls_dae_poll() left held on success. -*/
892void radcli2_priv_tls_dae_poll_done(rc_handle *rh)
893{
894 tls_st *st;
895
896 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
897 return;
898 st = rh->so.ptr;
899 pthread_mutex_unlock(&st->ctx.lock);
900}
901
902/*- Make one non-blocking attempt to send a DAE-over-RadSec reply.
903 *
904 * Unlike tls_sendto() (used for ordinary requests, where blocking until
905 * radius_timeout elapses waiting for POLLOUT is the caller's own,
906 * accepted contract), this never waits: a poll()-driven application's
907 * dispatch action (lib/dae.c's radcli_ctx_dispatch(), invoked only
908 * because the descriptor was reported readable) must not turn into a
909 * multi-second stall just because sending a reply as a side effect would
910 * otherwise block. On GNUTLS_E_AGAIN/_INTERRUPTED, the caller is expected
911 * to queue buf and retry this same call later (lib/dae.c's bounded
912 * radsec_reply_queue) rather than wait here.
913 *
914 * The session lock is a plain (recursive) lock, not a trylock: every
915 * caller of this function already holds it via the recursive session
916 * lock nesting radcli2_priv_tls_dae_poll()'s doc comment above describes
917 * (this thread either came from tls_recvfrom()'s inline demux, which holds it
918 * for the whole enclosing radcli_transport_exchange() call, or from
919 * radcli_ctx_dispatch(), which holds it across radcli2_priv_dae_on_radsec_
920 * packet() precisely so this nests rather than deadlocking) -- so this
921 * never actually blocks waiting for another thread.
922 *
923 * @return the number of bytes GnuTLS accepted as one complete record
924 * (matching gnutls_record_send()'s own return convention) on success, 0
925 * if the send would block (nothing was sent; retry the identical buf/len
926 * later), -1 on a session error (also marks the session for
927 * reconnection, same as tls_sendto()'s own error handling).
928 -*/
929int radcli2_priv_tls_dae_send(rc_handle *rh, const void *buf, size_t len)
930{
931 tls_st *st;
932 int ret;
933
934 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
935 return -1;
936
937 st = rh->so.ptr;
938 if (st->ctx.init == 0 || st->ctx.need_restart != 0)
939 return -1;
940
941 pthread_mutex_lock(&st->ctx.lock);
942 ret = gnutls_record_send(st->ctx.session, buf, len);
943
944 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
945 pthread_mutex_unlock(&st->ctx.lock);
946 return 0;
947 }
948 if (ret < 0) {
949 rc_log(LOG_ERR, "%s: error in sending: %s", __func__, gnutls_strerror(ret));
950 st->ctx.need_restart = 1;
951 pthread_mutex_unlock(&st->ctx.lock);
952 return -1;
953 }
954
955 st->ctx.last_msg = time(0);
956 pthread_mutex_unlock(&st->ctx.lock);
957 return ret;
958}
959
960/*- One non-blocking attempt to read the reply an in-flight async
961 * request/reply exchange (lib/sendserver.c's radcli_transport_service_
962 * async()) is waiting for, over TLS/DTLS. Unlike radcli2_priv_tls_dae_poll(),
963 * this does NOT take rh's session lock itself: its only caller already
964 * holds it, acquired (via rc_sockets_override.lock, i.e. tls_lock()) by
965 * radcli_transport_send_async() when the exchange started and held across
966 * every radcli_transport_service_async() call until a terminal result --
967 * exactly the same lock, held for the same span, that a blocking
968 * radcli_transport_exchange() call already holds for its own, longer
969 * synchronous duration; this function just lets that duration be spread
970 * across several non-blocking calls instead. Never retries on
971 * GNUTLS_E_AGAIN (same "single attempt" contract as
972 * radcli2_priv_tls_dae_poll()). A Disconnect-Request/CoA-Request record
973 * arriving while waiting is dispatched inline via
974 * radcli2_priv_dae_on_radsec_packet(), exactly as tls_recvfrom()'s own
975 * inline demux does, and this then reports "not ready yet" rather than
976 * returning it as the pending reply.
977 *
978 * @param rh a handle to parsed configuration.
979 * @param buf destination for the record.
980 * @param cap buf's capacity in bytes.
981 * @return the record length (>0) on success, 0 if nothing is ready yet,
982 * -1 on a session error (also marks the session for reconnection, same
983 * as tls_recvfrom()'s own error handling).
984 -*/
985int radcli2_priv_tls_try_recv(rc_handle *rh, uint8_t *buf, size_t cap)
986{
987 tls_st *st;
988 int ret;
989
990 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
991 return -1;
992
993 st = rh->so.ptr;
994
995 ret = gnutls_record_recv(st->ctx.session, buf, cap);
996
997 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
998 return 0; /* nothing ready this call -- no retry, unlike tls_recvfrom() */
999
1000 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
1001 rc_log(LOG_ERR, "%s: received alert: %s", __func__,
1002 gnutls_alert_get_name(gnutls_alert_get(st->ctx.session)));
1003 return 0; /* transient, same as tls_recvfrom()'s own handling of this
1004 * alert -- not a reason to tear down the session */
1005 }
1006
1007 /* RFC 6614 SS2.1/SS2.5, RFC 7360 SS2.2: one connection carries every
1008 * packet type. A Disconnect-Request/CoA-Request arriving here is
1009 * never the reply this exchange is waiting for -- hand it to lib/
1010 * dae.c's RadSec pipeline right here, exactly as tls_recvfrom()'s own
1011 * inline demux does, and report "not ready yet"; a genuine reply
1012 * queued right behind it is picked up on the next call. */
1013 if (ret >= 1 &&
1014 (((const uint8_t *)buf)[0] == RADCLI_DISCONNECT_REQUEST ||
1015 ((const uint8_t *)buf)[0] == RADCLI_COA_REQUEST)) {
1016 radcli2_priv_dae_on_radsec_packet(st->rh, buf, (size_t)ret);
1017 return 0;
1018 }
1019
1020 if (ret <= 0) {
1021 rc_log(LOG_ERR, "%s: error in receiving: %s", __func__, gnutls_strerror(ret));
1022 st->ctx.need_restart = 1;
1023 return -1;
1024 }
1025
1026 st->ctx.last_msg = time(0);
1027 return ret;
1028}
1029
1030/*- This function will deinitialize a previously initialed DTLS or TLS session.
1031 *
1032 * @param rh the configuration handle.
1033 -*/
1034void rc_deinit_tls(rc_handle * rh)
1035{
1036 tls_st *st = rh->so.ptr;
1037 char *ns = NULL;
1038 int ns_def_hdl = 0;
1039
1040 if (st) {
1041 ns = rc_conf_str_id(rh, OPT_NAMESPACE); /* Check for namespace config */
1042 if (ns != NULL) {
1043 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
1044 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
1045 return;
1046 }
1047 }
1048 if (st->ctx.init != 0)
1049 deinit_session(&st->ctx);
1050 if (st->x509_cred)
1051 gnutls_certificate_free_credentials(st->x509_cred);
1052 if (st->psk_cred)
1053 gnutls_psk_free_client_credentials(st->psk_cred);
1054 if (ns != NULL) {
1055 if(-1 == rc_reset_netns(&ns_def_hdl))
1056 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
1057 }
1058 }
1059 free(st);
1060}
1061
1062/*- Initialize a configuration for TLS or DTLS
1063 *
1064 * This function will initialize the handle for TLS or DTLS.
1065 *
1066 * @param rh a handle to parsed configuration
1067 * @param flags must be zero or SEC_FLAG_DTLS
1068 * @return 0 on success, -1 on failure.
1069 -*/
1070int rc_init_tls(rc_handle * rh, unsigned flags)
1071{
1072 int ret;
1073 tls_st *st = NULL;
1074 struct sockaddr_storage our_sockaddr;
1075 const char *ca_file = rc_conf_str_id(rh, OPT_TLS_CA_FILE);
1076 const char *cert_file = rc_conf_str_id(rh, OPT_TLS_CERT_FILE);
1077 const char *key_file = rc_conf_str_id(rh, OPT_TLS_KEY_FILE);
1078 const char *pskkey = NULL;
1079 SERVER *authservers;
1080 char hostname[256]; /* server's hostname */
1081 unsigned port; /* server's port */
1082 char *ns = NULL;
1083 int ns_def_hdl = 0;
1084
1085 memset(&rh->so, 0, sizeof(rh->so));
1086
1087 ns = rc_conf_str_id(rh, OPT_NAMESPACE); /* Check for namespace config */
1088 if (ns != NULL) {
1089 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
1090 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
1091 return -1;
1092 }
1093 }
1094
1095 if (flags & SEC_FLAG_DTLS) {
1096 rh->so_type = RC_SOCKET_DTLS;
1097 rh->so.static_secret = DEFAULT_DTLS_SECRET;
1098 } else {
1099 rh->so_type = RC_SOCKET_TLS;
1100 rh->so.static_secret = DEFAULT_TLS_SECRET;
1101 }
1102
1103 rc_own_bind_addr(rh, &our_sockaddr);
1104
1105 st = calloc(1, sizeof(tls_st));
1106 if (st == NULL) {
1107 ret = -1;
1108 goto cleanup;
1109 }
1110
1111 st->rh = rh;
1112 st->flags = flags;
1113
1114 rh->so.ptr = st;
1115
1116 if (ca_file || (key_file && cert_file)) {
1117 ret = gnutls_certificate_allocate_credentials(&st->x509_cred);
1118 if (ret < 0) {
1119 ret = -1;
1120 rc_log(LOG_ERR,
1121 "%s: error in setting X.509 credentials: %s",
1122 __func__, gnutls_strerror(ret));
1123 goto cleanup;
1124 }
1125
1126 if (ca_file) {
1127 ret =
1128 gnutls_certificate_set_x509_trust_file(st->x509_cred,
1129 ca_file,
1130 GNUTLS_X509_FMT_PEM);
1131 if (ret < 0) {
1132 ret = -1;
1133 rc_log(LOG_ERR,
1134 "%s: error in setting X.509 trust file: %s: %s",
1135 __func__, gnutls_strerror(ret), ca_file);
1136 goto cleanup;
1137 }
1138 }
1139
1140 if (cert_file && key_file) {
1141 ret =
1142 gnutls_certificate_set_x509_key_file(st->x509_cred,
1143 cert_file,
1144 key_file,
1145 GNUTLS_X509_FMT_PEM);
1146 if (ret < 0) {
1147 ret = -1;
1148 rc_log(LOG_ERR,
1149 "%s: error in setting X.509 cert and key files: %s: %s - %s",
1150 __func__, gnutls_strerror(ret), cert_file, key_file);
1151 goto cleanup;
1152 }
1153 }
1154
1155 gnutls_certificate_set_verify_function(st->x509_cred,
1156 cert_verify_callback);
1157 }
1158
1159 /* Read the PSK key if any */
1160 authservers = radcli2_priv_conf_srv(rh, "authserver");
1161 if (authservers == NULL) {
1162 rc_log(LOG_ERR,
1163 "%s: cannot find authserver", __func__);
1164 ret = -1;
1165 goto cleanup;
1166 }
1167 if (authservers->max > 1) {
1168 ret = -1;
1169 rc_log(LOG_ERR,
1170 "%s: too many auth servers for TLS/DTLS; only one is allowed",
1171 __func__);
1172 goto cleanup;
1173 }
1174 strlcpy(hostname, authservers->name[0], sizeof(hostname));
1175 port = authservers->port[0];
1176
1177 if (rh->tls_psk_key != NULL) {
1178 /* radcli2.h's radcli_ctx_set_tls_psk(): identity/key set directly
1179 * as bytes, not parsed out of a "psk@user@hexkey" secret string --
1180 * takes priority over authservers->secret[0] below if both are
1181 * somehow set. */
1182 gnutls_datum_t rawkey;
1183
1184 ret = gnutls_psk_allocate_client_credentials(&st->psk_cred);
1185 if (ret < 0) {
1186 ret = -1;
1187 rc_log(LOG_ERR,
1188 "%s: error in setting PSK credentials: %s",
1189 __func__, gnutls_strerror(ret));
1190 goto cleanup;
1191 }
1192
1193 rawkey.data = rh->tls_psk_key;
1194 rawkey.size = rh->tls_psk_key_len;
1195
1196 ret = gnutls_psk_set_client_credentials(st->psk_cred,
1197 rh->tls_psk_identity ? rh->tls_psk_identity : "",
1198 &rawkey, GNUTLS_PSK_KEY_RAW);
1199 if (ret < 0) {
1200 ret = -1;
1201 rc_log(LOG_ERR,
1202 "%s: error in setting PSK key: %s",
1203 __func__, gnutls_strerror(ret));
1204 goto cleanup;
1205 }
1206
1207 goto psk_done;
1208 }
1209
1210 {
1211 /* Config-file equivalent of radcli_ctx_set_tls_psk(): identity as
1212 * plain text, key as hex text (RC_OPTION_TABLE's tls-psk-identity/
1213 * tls-psk-key, radcli-defs.h) -- takes priority over authservers->
1214 * secret[0]'s embedded "psk@username@hexkey" form below, but not
1215 * over rh->tls_psk_key set via the API call above. GNUTLS_PSK_KEY_HEX
1216 * lets gnutls parse the hex text directly, so no manual decoding
1217 * is needed here. */
1218 const char *psk_identity = rc_conf_str_id(rh, OPT_TLS_PSK_IDENTITY);
1219 const char *psk_key = rc_conf_str_id(rh, OPT_TLS_PSK_KEY);
1220
1221 if ((psk_identity != NULL) != (psk_key != NULL)) {
1222 ret = -1;
1223 rc_log(LOG_ERR,
1224 "%s: tls-psk-identity and tls-psk-key must both be set",
1225 __func__);
1226 goto cleanup;
1227 }
1228
1229 if (psk_identity != NULL && psk_key != NULL) {
1230 gnutls_datum_t hexkey;
1231
1232 hexkey.data = (uint8_t *)psk_key;
1233 hexkey.size = strlen(psk_key);
1234
1235 ret = gnutls_psk_allocate_client_credentials(&st->psk_cred);
1236 if (ret < 0) {
1237 ret = -1;
1238 rc_log(LOG_ERR,
1239 "%s: error in setting PSK credentials: %s",
1240 __func__, gnutls_strerror(ret));
1241 goto cleanup;
1242 }
1243
1244 ret = gnutls_psk_set_client_credentials(st->psk_cred,
1245 psk_identity, &hexkey,
1246 GNUTLS_PSK_KEY_HEX);
1247 if (ret < 0) {
1248 ret = -1;
1249 rc_log(LOG_ERR,
1250 "%s: error in setting PSK key: %s",
1251 __func__, gnutls_strerror(ret));
1252 goto cleanup;
1253 }
1254
1255 goto psk_done;
1256 }
1257 }
1258
1259 if (authservers->secret[0])
1260 pskkey = authservers->secret[0];
1261
1262 if (pskkey && pskkey[0] != 0) {
1263 char *p;
1264 char username[64];
1265 gnutls_datum_t hexkey;
1266 int username_len;
1267
1268 if (strncmp(pskkey, "psk@", 4) != 0) {
1269 ret = -1;
1270 rc_log(LOG_ERR,
1271 "%s: server secret is set but does not start with 'psk@'",
1272 __func__);
1273 goto cleanup;
1274 }
1275 pskkey+=4;
1276
1277 if ((p = strchr(pskkey, '@')) == NULL) {
1278 ret = -1;
1279 rc_log(LOG_ERR,
1280 "%s: PSK key is not in 'username@hexkey' format",
1281 __func__);
1282 goto cleanup;
1283 }
1284
1285 username_len = p - pskkey;
1286 if (username_len + 1 > sizeof(username)) {
1287 rc_log(LOG_ERR,
1288 "%s: PSK username too big", __func__);
1289 ret = -1;
1290 goto cleanup;
1291 }
1292
1293 strlcpy(username, pskkey, username_len + 1);
1294
1295 p++;
1296 hexkey.data = (uint8_t*)p;
1297 hexkey.size = strlen(p);
1298
1299 ret = gnutls_psk_allocate_client_credentials(&st->psk_cred);
1300 if (ret < 0) {
1301 ret = -1;
1302 rc_log(LOG_ERR,
1303 "%s: error in setting PSK credentials: %s",
1304 __func__, gnutls_strerror(ret));
1305 goto cleanup;
1306 }
1307
1308 ret =
1309 gnutls_psk_set_client_credentials(st->psk_cred,
1310 username, &hexkey,
1311 GNUTLS_PSK_KEY_HEX);
1312 if (ret < 0) {
1313 ret = -1;
1314 rc_log(LOG_ERR,
1315 "%s: error in setting PSK key: %s",
1316 __func__, gnutls_strerror(ret));
1317 goto cleanup;
1318 }
1319 }
1320
1321 psk_done:
1322 /* Defer TCP connect + TLS handshake to first use.
1323 * tls_sendto() checks need_restart != 0 and calls restart_session(),
1324 * which calls init_session() with these stored parameters. */
1325 strlcpy(st->ctx.hostname, hostname, sizeof(st->ctx.hostname));
1326 st->ctx.port = port;
1327 memcpy(&st->ctx.our_sockaddr, &our_sockaddr, sizeof(our_sockaddr));
1328 st->ctx.need_restart = 1;
1329
1330 rh->so.get_fd = tls_get_fd;
1331 rh->so.get_active_fd = tls_get_active_fd;
1332 rh->so.sendto = tls_sendto;
1333 rh->so.recvfrom = tls_recvfrom;
1334 rh->so.lock = tls_lock;
1335 rh->so.unlock = tls_unlock;
1336 if (ns != NULL) {
1337 if(-1 == rc_reset_netns(&ns_def_hdl)) {
1338 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
1339 ret = -1;
1340 goto cleanup;
1341 }
1342 }
1343 return 0;
1344 cleanup:
1345 if (st) {
1346 if (st->ctx.init != 0)
1347 deinit_session(&st->ctx);
1348 if (st->x509_cred)
1349 gnutls_certificate_free_credentials(st->x509_cred);
1350 if (st->psk_cred)
1351 gnutls_psk_free_client_credentials(st->psk_cred);
1352 }
1353 free(st);
1354 rh->so.ptr = NULL;
1355 if (ns != NULL) {
1356 if(-1 == rc_reset_netns(&ns_def_hdl))
1357 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
1358 }
1359 return ret;
1360}
1361
1362#else /* !HAVE_GNUTLS */
1363
1364/* No-GnuTLS-build stubs: TLS/DTLS is never in use, so these report that
1365 * unconditionally rather than implementing the HAVE_GNUTLS versions'
1366 * behavior above. */
1367
1368int radcli2_priv_tls_fd(rc_handle * rh)
1369{
1370 return -1;
1371}
1372
1373time_t radcli2_priv_tls_last_msg(rc_handle * rh)
1374{
1375 (void)rh;
1376 return 0;
1377}
1378
1379time_t radcli2_priv_tls_last_recv(rc_handle * rh)
1380{
1381 (void)rh;
1382 return 0;
1383}
1384
1385int radcli2_priv_tls_force_reconnect(rc_handle * rh)
1386{
1387 (void)rh;
1388 return -1;
1389}
1390
1391int radcli2_priv_check_tls(rc_handle * rh)
1392{
1393 return 0;
1394}
1395
1396int radcli2_priv_tls_ensure_connected(rc_handle *rh)
1397{
1398 (void)rh;
1399 return -1;
1400}
1401
1402int radcli2_priv_tls_dae_poll(rc_handle *rh, uint8_t *buf, size_t cap)
1403{
1404 (void)rh;
1405 (void)buf;
1406 (void)cap;
1407 return 0;
1408}
1409
1410void radcli2_priv_tls_dae_poll_done(rc_handle *rh)
1411{
1412 (void)rh;
1413}
1414
1415int radcli2_priv_tls_dae_send(rc_handle *rh, const void *buf, size_t len)
1416{
1417 (void)rh;
1418 (void)buf;
1419 (void)len;
1420 return -1;
1421}
1422
1423int radcli2_priv_tls_try_recv(rc_handle *rh, uint8_t *buf, size_t cap)
1424{
1425 (void)rh;
1426 (void)buf;
1427 (void)cap;
1428 return -1;
1429}
1430
1431#endif
1432
@ RC_SOCKET_DTLS
DTLS socket.
Definition radcli.h:115
@ RC_SOCKET_TLS
TLS socket.
Definition radcli.h:114