Radcli library 1.4.0
A simple radius library
Loading...
Searching...
No Matches
tls.c
1/*
2 * Copyright (c) 2014, 2015, 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
47#include <gnutls/gnutls.h>
48#include <gnutls/dtls.h>
49#include <pthread.h>
50#include <time.h>
51
52#define DEFAULT_DTLS_SECRET "radius/dtls"
53#define DEFAULT_TLS_SECRET "radsec"
54
55typedef struct tls_int_st {
56 char hostname[256]; /* server's hostname */
57 unsigned port; /* server's port */
58 struct sockaddr_storage our_sockaddr;
59 gnutls_session_t session;
60 int sockfd;
61 unsigned init;
62 unsigned need_restart;
63 unsigned skip_hostname_check; /* whether to verify hostname */
64 pthread_mutex_t lock;
65 time_t last_msg;
66 time_t last_restart;
67} tls_int_st;
68
69typedef struct tls_st {
70 gnutls_psk_client_credentials_t psk_cred;
71 gnutls_certificate_credentials_t x509_cred;
72 struct tls_int_st ctx; /* one for ACCT and another for AUTH */
73 unsigned flags; /* the flags set on init */
74 rc_handle *rh; /* a pointer to our owner */
75} tls_st;
76
77static void restart_session(rc_handle *rh, tls_st *st);
78
79static int tls_get_fd(void *ptr, struct sockaddr *our_sockaddr)
80{
81 tls_st *st = ptr;
82 return st->ctx.sockfd;
83}
84
85static ssize_t tls_sendto(void *ptr, int sockfd,
86 const void *buf, size_t len,
87 int flags, const struct sockaddr *dest_addr,
88 socklen_t addrlen)
89{
90 tls_st *st = ptr;
91 int ret;
92
93 if (st->ctx.need_restart != 0) {
94 restart_session(st->rh, st);
95 }
96
97 ret = gnutls_record_send(st->ctx.session, buf, len);
98 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) {
99 errno = EINTR;
100 return -1;
101 }
102
103 if (ret < 0) {
104 rc_log(LOG_ERR, "%s: error in sending: %s", __func__,
105 gnutls_strerror(ret));
106 errno = EIO;
107 st->ctx.need_restart = 1;
108 return -1;
109 }
110
111 st->ctx.last_msg = time(0);
112 return ret;
113}
114
115static int tls_lock(void *ptr)
116{
117 tls_st *st = ptr;
118
119 return pthread_mutex_lock(&st->ctx.lock);
120}
121
122static int tls_unlock(void *ptr)
123{
124 tls_st *st = ptr;
125
126 return pthread_mutex_unlock(&st->ctx.lock);
127}
128
129static ssize_t tls_recvfrom(void *ptr, int sockfd,
130 void *buf, size_t len,
131 int flags, struct sockaddr *src_addr,
132 socklen_t * addrlen)
133{
134 tls_st *st = ptr;
135 int ret;
136
137 ret = gnutls_record_recv(st->ctx.session, buf, len);
138 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED ||
139 ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED || ret == GNUTLS_E_HEARTBEAT_PONG_RECEIVED) {
140 errno = EINTR;
141 return -1;
142 }
143
144 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
145 rc_log(LOG_ERR, "%s: received alert: %s", __func__,
146 gnutls_alert_get_name(gnutls_alert_get(st->ctx.session)));
147 errno = EINTR;
148 return -1;
149 }
150
151 /* RFC6614 says: "After the TLS session is established, RADIUS packet payloads are
152 * exchanged over the encrypted TLS tunnel. In RADIUS/UDP, the
153 * packet size can be determined by evaluating the size of the
154 * datagram that arrived. Due to the stream nature of TCP and TLS,
155 * this does not hold true for RADIUS/TLS packet exchange.",
156 *
157 * That is correct in principle but it fails to associate the length with
158 * the TLS record boundaries. Here, when in TLS, we assume that a single TLS
159 * record holds a single radius packet. It wouldn't make sense anyway to send
160 * multiple TLS records for a single packet.
161 */
162
163 if (ret <= 0) {
164 rc_log(LOG_ERR, "%s: error in receiving: %s", __func__,
165 gnutls_strerror(ret));
166 errno = EIO;
167 st->ctx.need_restart = 1;
168 return -1;
169 }
170
171 st->ctx.last_msg = time(0);
172 return ret;
173}
174
175/* This function will verify the peer's certificate, and check
176 * if the hostname matches.
177 */
178static int cert_verify_callback(gnutls_session_t session)
179{
180 unsigned int status;
181 int ret;
182 struct tls_int_st *ctx;
183 gnutls_datum_t out;
184
185 /* read hostname */
186 ctx = gnutls_session_get_ptr(session);
187 if (ctx == NULL)
188 return GNUTLS_E_CERTIFICATE_ERROR;
189
190 if (ctx->skip_hostname_check)
191 ret = gnutls_certificate_verify_peers2(session, &status);
192 else
193 ret = gnutls_certificate_verify_peers3(session, ctx->hostname, &status);
194 if (ret < 0) {
195 rc_log(LOG_ERR, "%s: error in certificate verification: %s",
196 __func__, gnutls_strerror(ret));
197 return GNUTLS_E_CERTIFICATE_ERROR;
198 }
199
200 if (status != 0) {
201 ret =
202 gnutls_certificate_verification_status_print(status,
203 gnutls_certificate_type_get
204 (session),
205 &out, 0);
206 if (ret < 0) {
207 return GNUTLS_E_CERTIFICATE_ERROR;
208 }
209 rc_log(LOG_INFO, "%s: certificate: %s", __func__, out.data);
210 gnutls_free(out.data);
211 return GNUTLS_E_CERTIFICATE_ERROR;
212 }
213
214 return 0;
215}
216
217static void deinit_session(tls_int_st *ses)
218{
219 if (ses->init != 0) {
220 ses->init = 0;
221 pthread_mutex_destroy(&ses->lock);
222 if (ses->sockfd != -1)
223 close(ses->sockfd);
224 if (ses->session)
225 gnutls_deinit(ses->session);
226 }
227}
228
229static int init_session(rc_handle *rh, tls_int_st *ses,
230 const char *hostname, unsigned port,
231 struct sockaddr_storage *our_sockaddr,
232 int timeout,
233 unsigned secflags)
234{
235 int sockfd, ret, e;
236 struct addrinfo *info;
237 char *p;
238 unsigned flags = 0;
239 unsigned cred_set = 0;
240 tls_st *st = rh->so.ptr;
241
242 ses->sockfd = -1;
243 ses->init = 1;
244
245 pthread_mutex_init(&ses->lock, NULL);
246 sockfd = socket(our_sockaddr->ss_family, (secflags&SEC_FLAG_DTLS)?SOCK_DGRAM:SOCK_STREAM, 0);
247 if (sockfd < 0) {
248 rc_log(LOG_ERR,
249 "%s: cannot open socket", __func__);
250 ret = -1;
251 goto cleanup;
252 }
253
254 if (our_sockaddr->ss_family == AF_INET)
255 ((struct sockaddr_in *)our_sockaddr)->sin_port = 0;
256 else
257 ((struct sockaddr_in6 *)our_sockaddr)->sin6_port = 0;
258
259 ses->sockfd = sockfd;
260
261 /* Initialize DTLS */
262
263 flags = GNUTLS_CLIENT;
264 if (secflags&SEC_FLAG_DTLS)
265 flags |= GNUTLS_DATAGRAM;
266 ret = gnutls_init(&ses->session, flags);
267 if (ret < 0) {
268 rc_log(LOG_ERR,
269 "%s: error in gnutls_init(): %s", __func__, gnutls_strerror(ret));
270 ret = -1;
271 goto cleanup;
272 }
273
274 memcpy(&ses->our_sockaddr, our_sockaddr, sizeof(*our_sockaddr));
275 if (!(secflags&SEC_FLAG_DTLS)) {
276 if (timeout > 0) {
277 gnutls_handshake_set_timeout(ses->session, timeout*1000);
278 } else {
279 gnutls_handshake_set_timeout(ses->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
280 }
281 } else { /* DTLS */
282 if (timeout > 0)
283 gnutls_dtls_set_timeouts(ses->session, 1000, timeout*1000);
284 }
285
286 gnutls_transport_set_int(ses->session, sockfd);
287 gnutls_session_set_ptr(ses->session, ses);
288 /* we only initiate heartbeat messages */
289 gnutls_heartbeat_enable(ses->session, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND);
290
291 p = rc_conf_str(rh, "tls-verify-hostname");
292 if (p && (strcasecmp(p, "false") == 0 || strcasecmp(p, "no"))) {
293 ses->skip_hostname_check = 1;
294 }
295
296 if (st && st->psk_cred) {
297 cred_set = 1;
298 gnutls_credentials_set(ses->session,
299 GNUTLS_CRD_PSK, st->psk_cred);
300
301 ret = gnutls_priority_set_direct(ses->session, "NORMAL:-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK:-VERS-TLS1.0", NULL);
302 if (ret < 0) {
303 ret = -1;
304 rc_log(LOG_ERR,
305 "%s: error in setting PSK priorities: %s",
306 __func__, gnutls_strerror(ret));
307 goto cleanup;
308 }
309 } else if (st) {
310 cred_set = 1;
311 if (st->x509_cred) {
312 gnutls_credentials_set(ses->session,
313 GNUTLS_CRD_CERTIFICATE,
314 st->x509_cred);
315 }
316
317 gnutls_set_default_priority(ses->session);
318 }
319
320 gnutls_server_name_set(ses->session, GNUTLS_NAME_DNS,
321 hostname, strlen(hostname));
322
323 info =
324 rc_getaddrinfo(hostname, PW_AI_AUTH);
325 if (info == NULL) {
326 ret = -1;
327 rc_log(LOG_ERR, "%s: cannot resolve %s", __func__,
328 hostname);
329 goto cleanup;
330 }
331
332 if (port != 0) {
333 if (info->ai_addr->sa_family == AF_INET)
334 ((struct sockaddr_in *)info->ai_addr)->sin_port =
335 htons(port);
336 else
337 ((struct sockaddr_in6 *)info->ai_addr)->sin6_port =
338 htons(port);
339 } else {
340 rc_log(LOG_ERR, "%s: no port specified for server %s",
341 __func__, hostname);
342 ret = -1;
343 goto cleanup;
344 }
345
346 strlcpy(ses->hostname, hostname, sizeof(ses->hostname));
347 ses->port = port;
348
349 if (cred_set == 0) {
350 rc_log(LOG_CRIT,
351 "%s: neither tls-ca-file or a PSK key are configured",
352 __func__);
353 ret = -1;
354 goto cleanup;
355 }
356
357 /* we connect since we are talking to a single server */
358 ret = connect(sockfd, info->ai_addr, info->ai_addrlen);
359 freeaddrinfo(info);
360 if (ret == -1) {
361 e = errno;
362 ret = -1;
363 rc_log(LOG_CRIT, "%s: cannot connect to %s: %s",
364 __func__, hostname, strerror(e));
365 goto cleanup;
366 }
367
368 rc_log(LOG_DEBUG,
369 "%s: performing TLS/DTLS handshake with [%s]:%d",
370 __func__, hostname, port);
371 do {
372 ret = gnutls_handshake(ses->session);
373 if (ret == GNUTLS_E_LARGE_PACKET)
374 break;
375 } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
376
377 if (ret < 0) {
378 rc_log(LOG_ERR, "%s: error in handshake: %s",
379 __func__, gnutls_strerror(ret));
380 ret = -1;
381 goto cleanup;
382 }
383
384 return 0;
385 cleanup:
386 deinit_session(ses);
387 return ret;
388
389}
390
391/* The time after the last message was received, that
392 * we will try heartbeats */
393#define TIME_ALIVE 120
394
395static void restart_session(rc_handle *rh, tls_st *st)
396{
397 struct tls_int_st tmps;
398 time_t now = time(0);
399 int ret;
400 int timeout;
401
402 if (now - st->ctx.last_restart < TIME_ALIVE)
403 return;
404
405 st->ctx.last_restart = now;
406
407 timeout = rc_conf_int(rh, "radius_timeout");
408
409 /* reinitialize this session */
410 ret = init_session(rh, &tmps, st->ctx.hostname, st->ctx.port, &st->ctx.our_sockaddr, timeout, st->flags);
411 if (ret < 0) {
412 rc_log(LOG_ERR, "%s: error in re-initializing DTLS", __func__);
413 return;
414 }
415
416 if (tmps.sockfd == st->ctx.sockfd)
417 st->ctx.sockfd = -1;
418 deinit_session(&st->ctx);
419 memcpy(&st->ctx, &tmps, sizeof(tmps));
420 st->ctx.need_restart = 0;
421
422 return;
423}
424
433int rc_tls_fd(rc_handle * rh)
434{
435 tls_st *st;
436
437 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
438 return -1;
439
440 st = rh->so.ptr;
441
442 if (st->ctx.init != 0) {
443 return st->ctx.sockfd;
444 }
445 return -1;
446}
447
464int rc_check_tls(rc_handle * rh)
465{
466 tls_st *st;
467 time_t now = time(0);
468 int ret;
469
470 if (rh->so_type != RC_SOCKET_TLS && rh->so_type != RC_SOCKET_DTLS)
471 return 0;
472
473 st = rh->so.ptr;
474
475 if (st->ctx.init != 0) {
476 if (st->ctx.need_restart != 0) {
477 restart_session(rh, st);
478 } else if (now - st->ctx.last_msg > TIME_ALIVE) {
479 ret = gnutls_heartbeat_ping(st->ctx.session, 64, 4, GNUTLS_HEARTBEAT_WAIT);
480 if (ret < 0) {
481 restart_session(rh, st);
482 }
483 st->ctx.last_msg = now;
484 }
485 }
486 return 0;
487}
488
491/*- This function will deinitialize a previously initialed DTLS or TLS session.
492 *
493 * @param rh the configuration handle.
494 -*/
495void rc_deinit_tls(rc_handle * rh)
496{
497 tls_st *st = rh->so.ptr;
498 char *ns = NULL;
499 int ns_def_hdl = 0;
500
501 if (st) {
502 ns = rc_conf_str(rh, "namespace"); /* Check for namespace config */
503 if (ns != NULL) {
504 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
505 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
506 return;
507 }
508 }
509 if (st->ctx.init != 0)
510 deinit_session(&st->ctx);
511 if (st->x509_cred)
512 gnutls_certificate_free_credentials(st->x509_cred);
513 if (st->psk_cred)
514 gnutls_psk_free_client_credentials(st->psk_cred);
515 if (ns != NULL) {
516 if(-1 == rc_reset_netns(&ns_def_hdl))
517 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
518 }
519 }
520 free(st);
521}
522
523/*- Initialize a configuration for TLS or DTLS
524 *
525 * This function will initialize the handle for TLS or DTLS.
526 *
527 * @param rh a handle to parsed configuration
528 * @param flags must be zero or SEC_FLAG_DTLS
529 * @return 0 on success, -1 on failure.
530 -*/
531int rc_init_tls(rc_handle * rh, unsigned flags)
532{
533 int ret;
534 tls_st *st = NULL;
535 struct sockaddr_storage our_sockaddr;
536 const char *ca_file = rc_conf_str(rh, "tls-ca-file");
537 const char *cert_file = rc_conf_str(rh, "tls-cert-file");
538 const char *key_file = rc_conf_str(rh, "tls-key-file");
539 const char *pskkey = NULL;
540 SERVER *authservers;
541 char hostname[256]; /* server's hostname */
542 unsigned port; /* server's port */
543 char *ns = NULL;
544 int ns_def_hdl = 0;
545
546 memset(&rh->so, 0, sizeof(rh->so));
547
548 ns = rc_conf_str(rh, "namespace"); /* Check for namespace config */
549 if (ns != NULL) {
550 if(-1 == rc_set_netns(ns, &ns_def_hdl)) {
551 rc_log(LOG_ERR, "rc_send_server: namespace %s set failed", ns);
552 return -1;
553 }
554 }
555
556 if (flags & SEC_FLAG_DTLS) {
557 rh->so_type = RC_SOCKET_DTLS;
558 rh->so.static_secret = DEFAULT_DTLS_SECRET;
559 } else {
560 rh->so_type = RC_SOCKET_TLS;
561 rh->so.static_secret = DEFAULT_TLS_SECRET;
562 }
563
564 rc_own_bind_addr(rh, &our_sockaddr);
565
566 st = calloc(1, sizeof(tls_st));
567 if (st == NULL) {
568 ret = -1;
569 goto cleanup;
570 }
571
572 st->rh = rh;
573 st->flags = flags;
574
575 rh->so.ptr = st;
576
577 if (ca_file || (key_file && cert_file)) {
578 ret = gnutls_certificate_allocate_credentials(&st->x509_cred);
579 if (ret < 0) {
580 ret = -1;
581 rc_log(LOG_ERR,
582 "%s: error in setting X.509 credentials: %s",
583 __func__, gnutls_strerror(ret));
584 goto cleanup;
585 }
586
587 if (ca_file) {
588 ret =
589 gnutls_certificate_set_x509_trust_file(st->x509_cred,
590 ca_file,
591 GNUTLS_X509_FMT_PEM);
592 if (ret < 0) {
593 ret = -1;
594 rc_log(LOG_ERR,
595 "%s: error in setting X.509 trust file: %s: %s",
596 __func__, gnutls_strerror(ret), ca_file);
597 goto cleanup;
598 }
599 }
600
601 if (cert_file && key_file) {
602 ret =
603 gnutls_certificate_set_x509_key_file(st->x509_cred,
604 cert_file,
605 key_file,
606 GNUTLS_X509_FMT_PEM);
607 if (ret < 0) {
608 ret = -1;
609 rc_log(LOG_ERR,
610 "%s: error in setting X.509 cert and key files: %s: %s - %s",
611 __func__, gnutls_strerror(ret), cert_file, key_file);
612 goto cleanup;
613 }
614 }
615
616 gnutls_certificate_set_verify_function(st->x509_cred,
617 cert_verify_callback);
618 }
619
620 /* Read the PSK key if any */
621 authservers = rc_conf_srv(rh, "authserver");
622 if (authservers == NULL) {
623 rc_log(LOG_ERR,
624 "%s: cannot find authserver", __func__);
625 ret = -1;
626 goto cleanup;
627 }
628 if (authservers->max > 1) {
629 ret = -1;
630 rc_log(LOG_ERR,
631 "%s: too many auth servers for TLS/DTLS; only one is allowed",
632 __func__);
633 goto cleanup;
634 }
635 strlcpy(hostname, authservers->name[0], sizeof(hostname));
636 port = authservers->port[0];
637 if (authservers->secret[0])
638 pskkey = authservers->secret[0];
639
640 if (pskkey && pskkey[0] != 0) {
641 char *p;
642 char username[64];
643 gnutls_datum_t hexkey;
644 int username_len;
645
646 if (strncmp(pskkey, "psk@", 4) != 0) {
647 ret = -1;
648 rc_log(LOG_ERR,
649 "%s: server secret is set but does not start with 'psk@'",
650 __func__);
651 goto cleanup;
652 }
653 pskkey+=4;
654
655 if ((p = strchr(pskkey, '@')) == NULL) {
656 ret = -1;
657 rc_log(LOG_ERR,
658 "%s: PSK key is not in 'username@hexkey' format",
659 __func__);
660 goto cleanup;
661 }
662
663 username_len = p - pskkey;
664 if (username_len + 1 > sizeof(username)) {
665 rc_log(LOG_ERR,
666 "%s: PSK username too big", __func__);
667 ret = -1;
668 goto cleanup;
669 }
670
671 strlcpy(username, pskkey, username_len + 1);
672
673 p++;
674 hexkey.data = (uint8_t*)p;
675 hexkey.size = strlen(p);
676
677 ret = gnutls_psk_allocate_client_credentials(&st->psk_cred);
678 if (ret < 0) {
679 ret = -1;
680 rc_log(LOG_ERR,
681 "%s: error in setting PSK credentials: %s",
682 __func__, gnutls_strerror(ret));
683 goto cleanup;
684 }
685
686 ret =
687 gnutls_psk_set_client_credentials(st->psk_cred,
688 username, &hexkey,
689 GNUTLS_PSK_KEY_HEX);
690 if (ret < 0) {
691 ret = -1;
692 rc_log(LOG_ERR,
693 "%s: error in setting PSK key: %s",
694 __func__, gnutls_strerror(ret));
695 goto cleanup;
696 }
697 }
698
699 ret = init_session(rh, &st->ctx, hostname, port, &our_sockaddr, 0, flags);
700 if (ret < 0) {
701 ret = -1;
702 goto cleanup;
703 }
704
705 rh->so.get_fd = tls_get_fd;
706 rh->so.sendto = tls_sendto;
707 rh->so.recvfrom = tls_recvfrom;
708 rh->so.lock = tls_lock;
709 rh->so.unlock = tls_unlock;
710 if (ns != NULL) {
711 if(-1 == rc_reset_netns(&ns_def_hdl)) {
712 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
713 goto cleanup;
714 }
715 }
716 return 0;
717 cleanup:
718 if (st) {
719 if (st->ctx.init != 0)
720 deinit_session(&st->ctx);
721 if (st->x509_cred)
722 gnutls_certificate_free_credentials(st->x509_cred);
723 if (st->psk_cred)
724 gnutls_psk_free_client_credentials(st->psk_cred);
725 }
726 free(st);
727 if (ns != NULL) {
728 if(-1 == rc_reset_netns(&ns_def_hdl))
729 rc_log(LOG_ERR, "rc_send_server: namespace %s reset failed", ns);
730 }
731 return ret;
732}
733
734#endif
735
void rc_own_bind_addr(rc_handle *rh, struct sockaddr_storage *lia)
Definition ip_util.c:164
char * rc_conf_str(rc_handle const *rh, char const *optname)
Definition config.c:708
SERVER * rc_conf_srv(rc_handle const *rh, char const *optname)
Definition config.c:758
@ RC_SOCKET_DTLS
DTLS socket.
Definition radcli.h:103
@ RC_SOCKET_TLS
TLS socket.
Definition radcli.h:102
int rc_tls_fd(rc_handle *rh)
Definition tls.c:433
int rc_check_tls(rc_handle *rh)
Definition tls.c:464