Radcli library 1.5.3
A simple radius library
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 "util.h"
30#include "tls.h"
31
32#ifdef HAVE_GNUTLS
33
46
47#include <gnutls/gnutls.h>
48#include <gnutls/dtls.h>
49#include <pthread.h>
50#include <time.h>
51#include <poll.h>
52
53#define DEFAULT_DTLS_SECRET "radius/dtls"
54#define DEFAULT_TLS_SECRET "radsec"
55
56typedef struct tls_int_st {
57 char hostname[256]; /* server's hostname */
58 unsigned port; /* server's port */
59 struct sockaddr_storage our_sockaddr;
60 gnutls_session_t session;
61 int sockfd;
62 unsigned init;
63 unsigned handshake_done; /* set only once gnutls_handshake() succeeds;
64 * guards deinit_session()'s gnutls_bye(),
65 * which is invalid on a session that never
66 * finished (or started) its handshake. */
67 unsigned need_restart;
68 unsigned skip_hostname_check; /* whether to verify hostname */
69 pthread_mutex_t lock;
70 time_t last_msg;
71 time_t last_restart;
72} tls_int_st;
73
74typedef struct tls_st {
75 gnutls_psk_client_credentials_t psk_cred;
76 gnutls_certificate_credentials_t x509_cred;
77 struct tls_int_st ctx; /* one for ACCT and another for AUTH */
78 unsigned flags; /* the flags set on init */
79 rc_handle *rh; /* a pointer to our owner */
80} tls_st;
81
83static int restart_session(rc_handle *rh, tls_st *st);
85
87static int tls_get_fd(void *ptr, struct sockaddr *our_sockaddr)
88{
89 tls_st *st = ptr;
90 if (st->ctx.need_restart != 0) {
91 if (restart_session(st->rh, st) < 0)
92 return -1;
93 }
94 return st->ctx.sockfd;
95}
97
99static int tls_get_active_fd(void *ptr)
100{
101 tls_st *st = ptr;
102 return st->ctx.sockfd;
103}
105
106/* Used from the GNUTLS_E_AGAIN/GNUTLS_E_INTERRUPTED retry branches of
107 * tls_sendto()/tls_recvfrom(): GnuTLS requires retrying the record call
108 * with the same arguments once @events is ready on the session fd.
109 * Waits up to the configured radius_timeout, safe against poll() itself
110 * being interrupted by a signal (retried against the same, non-extending
111 * deadline, so neither a signal nor a run of spurious EAGAINs can make
112 * the wait unbounded). On timeout or error, logs, marks the session for
113 * restart and sets errno=EIO.
114 *
115 * Returns 1 if the caller should retry the gnutls_record_*() call, or -1
116 * if it should give up (matching the calling convention of tls_sendto()/
117 * tls_recvfrom() themselves).
118 */
120static int tls_wait_or_give_up(tls_st *st, short events, const char *what)
121{
122 double start_time = rc_getmtime();
123 int timeout = rc_conf_int(st->rh, "radius_timeout");
124
125 if (timeout <= 0)
126 timeout = 1;
127
128 for (; timeout > 0; timeout -= (int)(rc_getmtime() - start_time)) {
129 struct pollfd pfd = { st->ctx.sockfd, events, 0 };
130 int ret = poll(&pfd, 1, timeout * 1000);
131
132 if (ret > 0)
133 return 1;
134 if (ret == 0)
135 break;
136 if (errno != EINTR) {
137 rc_log(LOG_ERR, "%s: poll: %s", __func__, strerror(errno));
138 goto give_up;
139 }
140 /* poll() itself was interrupted; retry against the same
141 * deadline rather than treating it as a timeout. */
142 }
143 rc_log(LOG_ERR, "%s: timeout waiting to %s TLS data", __func__, what);
144give_up:
145 errno = EIO;
146 st->ctx.need_restart = 1;
147 return -1;
148}
150
152static ssize_t tls_sendto(void *ptr, int sockfd,
153 const void *buf, size_t len,
154 int flags, const struct sockaddr *dest_addr,
155 socklen_t addrlen)
156{
157 tls_st *st = ptr;
158 int ret;
159
160 if (st->ctx.need_restart != 0) {
161 if (restart_session(st->rh, st) < 0) {
162 errno = EIO;
163 return -1;
164 }
165 }
166
167 for (;;) {
168 ret = gnutls_record_send(st->ctx.session, buf, len);
169 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
170 if (tls_wait_or_give_up(st, POLLOUT, "send") < 0)
171 return -1;
172 continue;
173 }
174
175 if (ret < 0) {
176 rc_log(LOG_ERR, "%s: error in sending: %s", __func__,
177 gnutls_strerror(ret));
178 errno = EIO;
179 st->ctx.need_restart = 1;
180 return -1;
181 }
182
183 break;
184 }
185
186 st->ctx.last_msg = time(0);
187 return ret;
188}
190
192static int tls_lock(void *ptr)
193{
194 tls_st *st = ptr;
195
196 return pthread_mutex_lock(&st->ctx.lock);
197}
199
201static int tls_unlock(void *ptr)
202{
203 tls_st *st = ptr;
204
205 return pthread_mutex_unlock(&st->ctx.lock);
206}
208
210static ssize_t tls_recvfrom(void *ptr, int sockfd,
211 void *buf, size_t len,
212 int flags, struct sockaddr *src_addr,
213 socklen_t * addrlen)
214{
215 tls_st *st = ptr;
216 int ret;
217
218 for (;;) {
219 ret = gnutls_record_recv(st->ctx.session, buf, len);
220 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED ||
221 ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED || ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
222 if (tls_wait_or_give_up(st, POLLIN, "receive") < 0)
223 return -1;
224 continue;
225 }
226 break;
227 }
228
229 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
230 rc_log(LOG_ERR, "%s: received alert: %s", __func__,
231 gnutls_alert_get_name(gnutls_alert_get(st->ctx.session)));
232 errno = EINTR;
233 return -1;
234 }
235
236 /* RFC6614 says: "After the TLS session is established, RADIUS packet payloads are
237 * exchanged over the encrypted TLS tunnel. In RADIUS/UDP, the
238 * packet size can be determined by evaluating the size of the
239 * datagram that arrived. Due to the stream nature of TCP and TLS,
240 * this does not hold true for RADIUS/TLS packet exchange.",
241 *
242 * That is correct in principle but it fails to associate the length with
243 * the TLS record boundaries. Here, when in TLS, we assume that a single TLS
244 * record holds a single radius packet. It wouldn't make sense anyway to send
245 * multiple TLS records for a single packet.
246 */
247
248 if (ret <= 0) {
249 rc_log(LOG_ERR, "%s: error in receiving: %s", __func__,
250 gnutls_strerror(ret));
251 errno = EIO;
252 st->ctx.need_restart = 1;
253 return -1;
254 }
255
256 st->ctx.last_msg = time(0);
257 return ret;
258}
260
261/* This function will verify the peer's certificate, and check
262 * if the hostname matches.
263 */
265static int cert_verify_callback(gnutls_session_t session)
266{
267 unsigned int status;
268 int ret;
269 struct tls_int_st *ctx;
270 gnutls_datum_t out;
271
272 /* read hostname */
273 ctx = gnutls_session_get_ptr(session);
274 if (ctx == NULL)
275 return GNUTLS_E_CERTIFICATE_ERROR;
276
277 if (ctx->skip_hostname_check)
278 ret = gnutls_certificate_verify_peers2(session, &status);
279 else
280 ret = gnutls_certificate_verify_peers3(session, ctx->hostname, &status);
281 if (ret < 0) {
282 rc_log(LOG_ERR, "%s: error in certificate verification: %s",
283 __func__, gnutls_strerror(ret));
284 return GNUTLS_E_CERTIFICATE_ERROR;
285 }
286
287 if (status != 0) {
288 ret =
289 gnutls_certificate_verification_status_print(status,
290 gnutls_certificate_type_get
291 (session),
292 &out, 0);
293 if (ret < 0) {
294 return GNUTLS_E_CERTIFICATE_ERROR;
295 }
296 rc_log(LOG_INFO, "%s: certificate: %s", __func__, out.data);
297 gnutls_free(out.data);
298 return GNUTLS_E_CERTIFICATE_ERROR;
299 }
300
301 return 0;
302}
304
306static void deinit_session(tls_int_st *ses)
307{
308 if (ses->init != 0) {
309 int ret;
310 ses->init = 0;
311 if (ses->session) {
312 /* Send close_notify before closing the socket so the peer
313 * receives a proper TLS/DTLS shutdown alert. Only valid
314 * once the handshake actually completed -- e.g. a
315 * connect() failure leaves an initialized session with
316 * no negotiated cipher state, and gnutls_bye() on that
317 * is not meaningful. */
318 if (ses->sockfd != -1 && ses->handshake_done) {
319 do {
320 ret = gnutls_bye(ses->session, GNUTLS_SHUT_WR);
321 } while (ret == GNUTLS_E_INTERRUPTED);
322 }
323 gnutls_deinit(ses->session);
324 }
325 pthread_mutex_destroy(&ses->lock);
326 if (ses->sockfd != -1)
327 close(ses->sockfd);
328 }
329}
331
333static int init_session(rc_handle *rh, tls_int_st *ses,
334 const char *hostname, unsigned port,
335 struct sockaddr_storage *our_sockaddr,
336 int timeout,
337 unsigned secflags)
338{
339 int sockfd, ret, e, sock_flags;
340 struct addrinfo *info;
341 char *p;
342 unsigned flags = 0;
343 unsigned cred_set = 0;
344 tls_st *st = rh->so.ptr;
345
346 ses->sockfd = -1;
347 ses->init = 1;
348 ses->handshake_done = 0;
349
350 pthread_mutex_init(&ses->lock, NULL);
351 sockfd = socket(our_sockaddr->ss_family, (secflags&SEC_FLAG_DTLS)?SOCK_DGRAM:SOCK_STREAM, 0);
352 if (sockfd < 0) {
353 rc_log(LOG_ERR,
354 "%s: cannot open socket", __func__);
355 ret = -1;
356 goto cleanup;
357 }
358
359 if (our_sockaddr->ss_family == AF_INET)
360 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
361 else
362 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
363
364 ses->sockfd = sockfd;
365
366 /* Initialize DTLS */
367
368 flags = GNUTLS_CLIENT;
369 if (secflags&SEC_FLAG_DTLS)
370 flags |= GNUTLS_DATAGRAM;
371 ret = gnutls_init(&ses->session, flags);
372 if (ret < 0) {
373 rc_log(LOG_ERR,
374 "%s: error in gnutls_init(): %s", __func__, gnutls_strerror(ret));
375 ret = -1;
376 goto cleanup;
377 }
378
379 memcpy(&ses->our_sockaddr, our_sockaddr, sizeof(*our_sockaddr));
380 if (!(secflags&SEC_FLAG_DTLS)) {
381 if (timeout > 0) {
382 gnutls_handshake_set_timeout(ses->session, timeout*1000);
383 } else {
384 gnutls_handshake_set_timeout(ses->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
385 }
386 } else { /* DTLS */
387 if (timeout > 0)
388 gnutls_dtls_set_timeouts(ses->session, 1000, timeout*1000);
389 }
390
391 gnutls_transport_set_int(ses->session, sockfd);
392 gnutls_session_set_ptr(ses->session, ses);
393 /* we only initiate heartbeat messages */
394 gnutls_heartbeat_enable(ses->session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND);
395
396 p = rc_conf_str(rh, "tls-verify-hostname");
397 if (p && (strcasecmp(p, "false") == 0 || strcasecmp(p, "no") == 0)) {
398 ses->skip_hostname_check = 1;
399 }
400
401 if (st && st->psk_cred) {
402 cred_set = 1;
403 gnutls_credentials_set(ses->session,
404 GNUTLS_CRD_PSK, st->psk_cred);
405
406 ret = gnutls_priority_set_direct(ses->session, "NORMAL:-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK:-VERS-TLS1.0", NULL);
407 if (ret < 0) {
408 ret = -1;
409 rc_log(LOG_ERR,
410 "%s: error in setting PSK priorities: %s",
411 __func__, gnutls_strerror(ret));
412 goto cleanup;
413 }
414 } else if (st) {
415 cred_set = 1;
416 if (st->x509_cred) {
417 gnutls_credentials_set(ses->session,
418 GNUTLS_CRD_CERTIFICATE,
419 st->x509_cred);
420 }
421
422 gnutls_set_default_priority(ses->session);
423 }
424
425 gnutls_server_name_set(ses->session, GNUTLS_NAME_DNS,
426 hostname, strlen(hostname));
427
428 info =
429 rc_getaddrinfo(hostname, PW_AI_AUTH);
430 if (info == NULL) {
431 ret = -1;
432 rc_log(LOG_ERR, "%s: cannot resolve %s", __func__,
433 hostname);
434 goto cleanup;
435 }
436
437 if (port != 0) {
438 if (info->ai_addr->sa_family == AF_INET)
439 ((struct sockaddr_in *)info->ai_addr)->sin_port =
440 htons(port);
441 else
442 ((struct sockaddr_in6 *)info->ai_addr)->sin6_port =
443 htons(port);
444 } else {
445 rc_log(LOG_ERR, "%s: no port specified for server %s",
446 __func__, hostname);
447 ret = -1;
448 goto cleanup;
449 }
450
451 strlcpy(ses->hostname, hostname, sizeof(ses->hostname));
452 ses->port = port;
453
454 if (cred_set == 0) {
455 rc_log(LOG_CRIT,
456 "%s: neither tls-ca-file or a PSK key are configured",
457 __func__);
458 ret = -1;
459 goto cleanup;
460 }
461
462 /* we connect since we are talking to a single server */
463 ret = connect(sockfd, info->ai_addr, info->ai_addrlen);
464 freeaddrinfo(info);
465 if (ret == -1) {
466 e = errno;
467 ret = -1;
468 rc_log(LOG_CRIT, "%s: cannot connect to %s: %s",
469 __func__, hostname, strerror(e));
470 goto cleanup;
471 }
472
473 /* Switch to non-blocking mode before the handshake, so that both
474 * gnutls_handshake() (bounded above via gnutls_handshake_set_timeout()/
475 * gnutls_dtls_set_timeouts()) and the post-handshake record I/O in
476 * tls_sendto()/tls_recvfrom() can actually observe GNUTLS_E_AGAIN and
477 * take the bounded poll()-and-retry path in tls_wait_or_give_up(),
478 * instead of blocking in the kernel with no timeout at all. */
479 sock_flags = fcntl(sockfd, F_GETFL, 0);
480 if (sock_flags == -1 ||
481 fcntl(sockfd, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
482 e = errno;
483 ret = -1;
484 rc_log(LOG_CRIT, "%s: cannot set socket non-blocking: %s",
485 __func__, strerror(e));
486 goto cleanup;
487 }
488
489 rc_log(LOG_DEBUG,
490 "%s: performing TLS/DTLS handshake with [%s]:%d",
491 __func__, hostname, port);
492 do {
493 ret = gnutls_handshake(ses->session);
494 if (ret == GNUTLS_E_LARGE_PACKET)
495 break;
496 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
497
498 if (ret < 0) {
499 rc_log(LOG_ERR, "%s: error in handshake: %s",
500 __func__, gnutls_strerror(ret));
501 ret = -1;
502 goto cleanup;
503 }
504
505 ses->handshake_done = 1;
506 return 0;
507 cleanup:
508 deinit_session(ses);
509 return ret;
510
511}
513
514/* The time after the last message was received, that
515 * we will try heartbeats */
516#define TIME_ALIVE 120
517
519static int restart_session(rc_handle *rh, tls_st *st)
520{
521 /* init_session() assumes a zeroed struct: REQ-NET-NET-016 */
522 struct tls_int_st tmps = { 0 };
523 time_t now = time(0);
524 int ret;
525 int timeout;
526
527 /* Bypass the time guard when need_restart is set: the session is
528 * known to be broken (a send or recv already failed), so we must
529 * attempt reconnection regardless of how recently we last tried.
530 * When need_restart is 0 (proactive check from rc_check_tls via a
531 * failed heartbeat), keep the guard to avoid rapid reconnect loops. */
532 if (now - st->ctx.last_restart < TIME_ALIVE && !st->ctx.need_restart)
533 return -1;
534
535 st->ctx.last_restart = now;
536
537 timeout = rc_conf_int(rh, "radius_timeout");
538
539 /* reinitialize this session */
540 ret = init_session(rh, &tmps, st->ctx.hostname, st->ctx.port, &st->ctx.our_sockaddr, timeout, st->flags);
541 if (ret < 0) {
542 rc_log(LOG_ERR, "%s: error in re-initializing TLS session", __func__);
543 return -1;
544 }
545
546 if (tmps.sockfd == st->ctx.sockfd)
547 st->ctx.sockfd = -1;
548 deinit_session(&st->ctx);
549 memcpy(&st->ctx, &tmps, sizeof(tmps));
550 st->ctx.need_restart = 0;
551
552 return 0;
553}
555
564int rc_tls_fd(rc_handle * rh)
565{
566 tls_st *st;
567
568 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
569 return -1;
570
571 st = rh->so.ptr;
572
573 if (st->ctx.init != 0) {
574 return st->ctx.sockfd;
575 }
576 return -1;
577}
578
597int rc_check_tls(rc_handle * rh)
598{
599 tls_st *st;
600 time_t now = time(0);
601 int ret;
602
603 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
604 return 0;
605
606 st = rh->so.ptr;
607
608 if (st->ctx.init != 0) {
609 if (st->ctx.need_restart != 0) {
610 restart_session(rh, st);
611 } else if (now - st->ctx.last_msg > TIME_ALIVE) {
612 ret = gnutls_heartbeat_ping(st->ctx.session, 64, 4, GNUTLS_HEARTBEAT_WAIT);
613 if (ret < 0) {
614 restart_session(rh, st);
615 }
616 st->ctx.last_msg = now;
617 }
618 }
619 return 0;
620}
621
623
624/*- This function will deinitialize a previously initialed DTLS or TLS session.
625 *
626 * @param rh the configuration handle.
627 -*/
628void rc_deinit_tls(rc_handle * rh)
629{
630 tls_st *st = rh->so.ptr;
631 char *ns = NULL;
632 int ns_def_hdl = 0;
633
634 if (st) {
635 ns = rc_conf_str(rh, "namespace"); /* Check for namespace config */
636 if (ns != NULL) {
637 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
638 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
639 return;
640 }
641 }
642 if (st->ctx.init != 0)
643 deinit_session(&st->ctx);
644 if (st->x509_cred)
645 gnutls_certificate_free_credentials(st->x509_cred);
646 if (st->psk_cred)
647 gnutls_psk_free_client_credentials(st->psk_cred);
648 if (ns != NULL) {
649 if(-1 == rc_reset_netns(&ns_def_hdl))
650 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
651 }
652 }
653 free(st);
654}
655
656/*- Initialize a configuration for TLS or DTLS
657 *
658 * This function will initialize the handle for TLS or DTLS.
659 *
660 * @param rh a handle to parsed configuration
661 * @param flags must be zero or SEC_FLAG_DTLS
662 * @return 0 on success, -1 on failure.
663 -*/
664int rc_init_tls(rc_handle * rh, unsigned flags)
665{
666 int ret;
667 tls_st *st = NULL;
668 struct sockaddr_storage our_sockaddr;
669 const char *ca_file = rc_conf_str(rh, "tls-ca-file");
670 const char *cert_file = rc_conf_str(rh, "tls-cert-file");
671 const char *key_file = rc_conf_str(rh, "tls-key-file");
672 const char *pskkey = NULL;
673 SERVER *authservers;
674 char hostname[256]; /* server's hostname */
675 unsigned port; /* server's port */
676 char *ns = NULL;
677 int ns_def_hdl = 0;
678
679 memset(&rh->so, 0, sizeof(rh->so));
680
681 ns = rc_conf_str(rh, "namespace"); /* Check for namespace config */
682 if (ns != NULL) {
683 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
684 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
685 return -1;
686 }
687 }
688
689 if (flags & SEC_FLAG_DTLS) {
690 rh->so_type = RC_SOCKET_DTLS;
691 rh->so.static_secret = DEFAULT_DTLS_SECRET;
692 } else {
693 rh->so_type = RC_SOCKET_TLS;
694 rh->so.static_secret = DEFAULT_TLS_SECRET;
695 }
696
697 rc_own_bind_addr(rh, &our_sockaddr);
698
699 st = calloc(1, sizeof(tls_st));
700 if (st == NULL) {
701 ret = -1;
702 goto cleanup;
703 }
704
705 st->rh = rh;
706 st->flags = flags;
707
708 rh->so.ptr = st;
709
710 if (ca_file || (key_file && cert_file)) {
711 ret = gnutls_certificate_allocate_credentials(&st->x509_cred);
712 if (ret < 0) {
713 ret = -1;
714 rc_log(LOG_ERR,
715 "%s: error in setting X.509 credentials: %s",
716 __func__, gnutls_strerror(ret));
717 goto cleanup;
718 }
719
720 if (ca_file) {
721 ret =
722 gnutls_certificate_set_x509_trust_file(st->x509_cred,
723 ca_file,
724 GNUTLS_X509_FMT_PEM);
725 if (ret < 0) {
726 ret = -1;
727 rc_log(LOG_ERR,
728 "%s: error in setting X.509 trust file: %s: %s",
729 __func__, gnutls_strerror(ret), ca_file);
730 goto cleanup;
731 }
732 }
733
734 if (cert_file && key_file) {
735 ret =
736 gnutls_certificate_set_x509_key_file(st->x509_cred,
737 cert_file,
738 key_file,
739 GNUTLS_X509_FMT_PEM);
740 if (ret < 0) {
741 ret = -1;
742 rc_log(LOG_ERR,
743 "%s: error in setting X.509 cert and key files: %s: %s - %s",
744 __func__, gnutls_strerror(ret), cert_file, key_file);
745 goto cleanup;
746 }
747 }
748
749 gnutls_certificate_set_verify_function(st->x509_cred,
750 cert_verify_callback);
751 }
752
753 /* Read the PSK key if any */
754 authservers = rc_conf_srv(rh, "authserver");
755 if (authservers == NULL) {
756 rc_log(LOG_ERR,
757 "%s: cannot find authserver", __func__);
758 ret = -1;
759 goto cleanup;
760 }
761 if (authservers->max > 1) {
762 ret = -1;
763 rc_log(LOG_ERR,
764 "%s: too many auth servers for TLS/DTLS; only one is allowed",
765 __func__);
766 goto cleanup;
767 }
768 strlcpy(hostname, authservers->name[0], sizeof(hostname));
769 port = authservers->port[0];
770 if (authservers->secret[0])
771 pskkey = authservers->secret[0];
772
773 if (pskkey && pskkey[0] != 0) {
774 char *p;
775 char username[64];
776 gnutls_datum_t hexkey;
777 int username_len;
778
779 if (strncmp(pskkey, "psk@", 4) != 0) {
780 ret = -1;
781 rc_log(LOG_ERR,
782 "%s: server secret is set but does not start with 'psk@'",
783 __func__);
784 goto cleanup;
785 }
786 pskkey+=4;
787
788 if ((p = strchr(pskkey, '@')) == NULL) {
789 ret = -1;
790 rc_log(LOG_ERR,
791 "%s: PSK key is not in 'username@hexkey' format",
792 __func__);
793 goto cleanup;
794 }
795
796 username_len = p - pskkey;
797 if (username_len + 1 > sizeof(username)) {
798 rc_log(LOG_ERR,
799 "%s: PSK username too big", __func__);
800 ret = -1;
801 goto cleanup;
802 }
803
804 strlcpy(username, pskkey, username_len + 1);
805
806 p++;
807 hexkey.data = (uint8_t*)p;
808 hexkey.size = strlen(p);
809
810 ret = gnutls_psk_allocate_client_credentials(&st->psk_cred);
811 if (ret < 0) {
812 ret = -1;
813 rc_log(LOG_ERR,
814 "%s: error in setting PSK credentials: %s",
815 __func__, gnutls_strerror(ret));
816 goto cleanup;
817 }
818
819 ret =
820 gnutls_psk_set_client_credentials(st->psk_cred,
821 username, &hexkey,
822 GNUTLS_PSK_KEY_HEX);
823 if (ret < 0) {
824 ret = -1;
825 rc_log(LOG_ERR,
826 "%s: error in setting PSK key: %s",
827 __func__, gnutls_strerror(ret));
828 goto cleanup;
829 }
830 }
831
832 /* Defer TCP connect + TLS handshake to first use.
833 * tls_sendto() checks need_restart != 0 and calls restart_session(),
834 * which calls init_session() with these stored parameters. */
835 strlcpy(st->ctx.hostname, hostname, sizeof(st->ctx.hostname));
836 st->ctx.port = port;
837 memcpy(&st->ctx.our_sockaddr, &our_sockaddr, sizeof(our_sockaddr));
838 st->ctx.need_restart = 1;
839
840 rh->so.get_fd = tls_get_fd;
841 rh->so.get_active_fd = tls_get_active_fd;
842 rh->so.sendto = tls_sendto;
843 rh->so.recvfrom = tls_recvfrom;
844 rh->so.lock = tls_lock;
845 rh->so.unlock = tls_unlock;
846 if (ns != NULL) {
847 if(-1 == rc_reset_netns(&ns_def_hdl)) {
848 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
849 ret = -1;
850 goto cleanup;
851 }
852 }
853 return 0;
854 cleanup:
855 if (st) {
856 if (st->ctx.init != 0)
857 deinit_session(&st->ctx);
858 if (st->x509_cred)
859 gnutls_certificate_free_credentials(st->x509_cred);
860 if (st->psk_cred)
861 gnutls_psk_free_client_credentials(st->psk_cred);
862 }
863 free(st);
864 rh->so.ptr = NULL;
865 if (ns != NULL) {
866 if(-1 == rc_reset_netns(&ns_def_hdl))
867 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
868 }
869 return ret;
870}
871
872#endif
873
int rc_conf_int(rc_handle const *rh, char const *optname)
Get the value of a config option as an integer.
Definition config.c:864
char * rc_conf_str(rc_handle const *rh, char const *optname)
Get the value of a config option.
Definition config.c:817
SERVER * rc_conf_srv(rc_handle const *rh, char const *optname)
Get the value of a config option.
Definition config.c:875
@ RC_SOCKET_DTLS
DTLS socket.
Definition radcli.h:108
@ RC_SOCKET_TLS
TLS socket.
Definition radcli.h:107
int rc_tls_fd(rc_handle *rh)
Returns the file descriptor of the TLS/DTLS session.
Definition tls.c:564
int rc_check_tls(rc_handle *rh)
Check established TLS/DTLS channels for operation and reconnect if needed.
Definition tls.c:597
Public API of the radcli library.