xref: /freebsd/contrib/wpa/src/radius/radius_das.c (revision f05cddf940dbfc5b657f5e9beb9de2c31e509e5b)
1*f05cddf9SRui Paulo /*
2*f05cddf9SRui Paulo  * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
3*f05cddf9SRui Paulo  * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
4*f05cddf9SRui Paulo  *
5*f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6*f05cddf9SRui Paulo  * See README for more details.
7*f05cddf9SRui Paulo  */
8*f05cddf9SRui Paulo 
9*f05cddf9SRui Paulo #include "includes.h"
10*f05cddf9SRui Paulo #include <net/if.h>
11*f05cddf9SRui Paulo 
12*f05cddf9SRui Paulo #include "utils/common.h"
13*f05cddf9SRui Paulo #include "utils/eloop.h"
14*f05cddf9SRui Paulo #include "utils/ip_addr.h"
15*f05cddf9SRui Paulo #include "radius.h"
16*f05cddf9SRui Paulo #include "radius_das.h"
17*f05cddf9SRui Paulo 
18*f05cddf9SRui Paulo 
19*f05cddf9SRui Paulo extern int wpa_debug_level;
20*f05cddf9SRui Paulo 
21*f05cddf9SRui Paulo 
22*f05cddf9SRui Paulo struct radius_das_data {
23*f05cddf9SRui Paulo 	int sock;
24*f05cddf9SRui Paulo 	u8 *shared_secret;
25*f05cddf9SRui Paulo 	size_t shared_secret_len;
26*f05cddf9SRui Paulo 	struct hostapd_ip_addr client_addr;
27*f05cddf9SRui Paulo 	unsigned int time_window;
28*f05cddf9SRui Paulo 	int require_event_timestamp;
29*f05cddf9SRui Paulo 	void *ctx;
30*f05cddf9SRui Paulo 	enum radius_das_res (*disconnect)(void *ctx,
31*f05cddf9SRui Paulo 					  struct radius_das_attrs *attr);
32*f05cddf9SRui Paulo };
33*f05cddf9SRui Paulo 
34*f05cddf9SRui Paulo 
35*f05cddf9SRui Paulo static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
36*f05cddf9SRui Paulo 						 struct radius_msg *msg,
37*f05cddf9SRui Paulo 						 const char *abuf,
38*f05cddf9SRui Paulo 						 int from_port)
39*f05cddf9SRui Paulo {
40*f05cddf9SRui Paulo 	struct radius_hdr *hdr;
41*f05cddf9SRui Paulo 	struct radius_msg *reply;
42*f05cddf9SRui Paulo 	u8 allowed[] = {
43*f05cddf9SRui Paulo 		RADIUS_ATTR_USER_NAME,
44*f05cddf9SRui Paulo 		RADIUS_ATTR_CALLING_STATION_ID,
45*f05cddf9SRui Paulo 		RADIUS_ATTR_ACCT_SESSION_ID,
46*f05cddf9SRui Paulo 		RADIUS_ATTR_EVENT_TIMESTAMP,
47*f05cddf9SRui Paulo 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
48*f05cddf9SRui Paulo 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
49*f05cddf9SRui Paulo 		0
50*f05cddf9SRui Paulo 	};
51*f05cddf9SRui Paulo 	int error = 405;
52*f05cddf9SRui Paulo 	u8 attr;
53*f05cddf9SRui Paulo 	enum radius_das_res res;
54*f05cddf9SRui Paulo 	struct radius_das_attrs attrs;
55*f05cddf9SRui Paulo 	u8 *buf;
56*f05cddf9SRui Paulo 	size_t len;
57*f05cddf9SRui Paulo 	char tmp[100];
58*f05cddf9SRui Paulo 	u8 sta_addr[ETH_ALEN];
59*f05cddf9SRui Paulo 
60*f05cddf9SRui Paulo 	hdr = radius_msg_get_hdr(msg);
61*f05cddf9SRui Paulo 
62*f05cddf9SRui Paulo 	attr = radius_msg_find_unlisted_attr(msg, allowed);
63*f05cddf9SRui Paulo 	if (attr) {
64*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
65*f05cddf9SRui Paulo 			   "Disconnect-Request from %s:%d", attr,
66*f05cddf9SRui Paulo 			   abuf, from_port);
67*f05cddf9SRui Paulo 		error = 401;
68*f05cddf9SRui Paulo 		goto fail;
69*f05cddf9SRui Paulo 	}
70*f05cddf9SRui Paulo 
71*f05cddf9SRui Paulo 	os_memset(&attrs, 0, sizeof(attrs));
72*f05cddf9SRui Paulo 
73*f05cddf9SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
74*f05cddf9SRui Paulo 				    &buf, &len, NULL) == 0) {
75*f05cddf9SRui Paulo 		if (len >= sizeof(tmp))
76*f05cddf9SRui Paulo 			len = sizeof(tmp) - 1;
77*f05cddf9SRui Paulo 		os_memcpy(tmp, buf, len);
78*f05cddf9SRui Paulo 		tmp[len] = '\0';
79*f05cddf9SRui Paulo 		if (hwaddr_aton2(tmp, sta_addr) < 0) {
80*f05cddf9SRui Paulo 			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
81*f05cddf9SRui Paulo 				   "'%s' from %s:%d", tmp, abuf, from_port);
82*f05cddf9SRui Paulo 			error = 407;
83*f05cddf9SRui Paulo 			goto fail;
84*f05cddf9SRui Paulo 		}
85*f05cddf9SRui Paulo 		attrs.sta_addr = sta_addr;
86*f05cddf9SRui Paulo 	}
87*f05cddf9SRui Paulo 
88*f05cddf9SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
89*f05cddf9SRui Paulo 				    &buf, &len, NULL) == 0) {
90*f05cddf9SRui Paulo 		attrs.user_name = buf;
91*f05cddf9SRui Paulo 		attrs.user_name_len = len;
92*f05cddf9SRui Paulo 	}
93*f05cddf9SRui Paulo 
94*f05cddf9SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
95*f05cddf9SRui Paulo 				    &buf, &len, NULL) == 0) {
96*f05cddf9SRui Paulo 		attrs.acct_session_id = buf;
97*f05cddf9SRui Paulo 		attrs.acct_session_id_len = len;
98*f05cddf9SRui Paulo 	}
99*f05cddf9SRui Paulo 
100*f05cddf9SRui Paulo 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
101*f05cddf9SRui Paulo 				    &buf, &len, NULL) == 0) {
102*f05cddf9SRui Paulo 		attrs.cui = buf;
103*f05cddf9SRui Paulo 		attrs.cui_len = len;
104*f05cddf9SRui Paulo 	}
105*f05cddf9SRui Paulo 
106*f05cddf9SRui Paulo 	res = das->disconnect(das->ctx, &attrs);
107*f05cddf9SRui Paulo 	switch (res) {
108*f05cddf9SRui Paulo 	case RADIUS_DAS_NAS_MISMATCH:
109*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
110*f05cddf9SRui Paulo 			   abuf, from_port);
111*f05cddf9SRui Paulo 		error = 403;
112*f05cddf9SRui Paulo 		break;
113*f05cddf9SRui Paulo 	case RADIUS_DAS_SESSION_NOT_FOUND:
114*f05cddf9SRui Paulo 		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
115*f05cddf9SRui Paulo 			   "%s:%d", abuf, from_port);
116*f05cddf9SRui Paulo 		error = 503;
117*f05cddf9SRui Paulo 		break;
118*f05cddf9SRui Paulo 	case RADIUS_DAS_SUCCESS:
119*f05cddf9SRui Paulo 		error = 0;
120*f05cddf9SRui Paulo 		break;
121*f05cddf9SRui Paulo 	}
122*f05cddf9SRui Paulo 
123*f05cddf9SRui Paulo fail:
124*f05cddf9SRui Paulo 	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
125*f05cddf9SRui Paulo 			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
126*f05cddf9SRui Paulo 	if (reply == NULL)
127*f05cddf9SRui Paulo 		return NULL;
128*f05cddf9SRui Paulo 
129*f05cddf9SRui Paulo 	if (error) {
130*f05cddf9SRui Paulo 		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
131*f05cddf9SRui Paulo 					       error)) {
132*f05cddf9SRui Paulo 			radius_msg_free(reply);
133*f05cddf9SRui Paulo 			return NULL;
134*f05cddf9SRui Paulo 		}
135*f05cddf9SRui Paulo 	}
136*f05cddf9SRui Paulo 
137*f05cddf9SRui Paulo 	return reply;
138*f05cddf9SRui Paulo }
139*f05cddf9SRui Paulo 
140*f05cddf9SRui Paulo 
141*f05cddf9SRui Paulo static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
142*f05cddf9SRui Paulo {
143*f05cddf9SRui Paulo 	struct radius_das_data *das = eloop_ctx;
144*f05cddf9SRui Paulo 	u8 buf[1500];
145*f05cddf9SRui Paulo 	union {
146*f05cddf9SRui Paulo 		struct sockaddr_storage ss;
147*f05cddf9SRui Paulo 		struct sockaddr_in sin;
148*f05cddf9SRui Paulo #ifdef CONFIG_IPV6
149*f05cddf9SRui Paulo 		struct sockaddr_in6 sin6;
150*f05cddf9SRui Paulo #endif /* CONFIG_IPV6 */
151*f05cddf9SRui Paulo 	} from;
152*f05cddf9SRui Paulo 	char abuf[50];
153*f05cddf9SRui Paulo 	int from_port = 0;
154*f05cddf9SRui Paulo 	socklen_t fromlen;
155*f05cddf9SRui Paulo 	int len;
156*f05cddf9SRui Paulo 	struct radius_msg *msg, *reply = NULL;
157*f05cddf9SRui Paulo 	struct radius_hdr *hdr;
158*f05cddf9SRui Paulo 	struct wpabuf *rbuf;
159*f05cddf9SRui Paulo 	u32 val;
160*f05cddf9SRui Paulo 	int res;
161*f05cddf9SRui Paulo 	struct os_time now;
162*f05cddf9SRui Paulo 
163*f05cddf9SRui Paulo 	fromlen = sizeof(from);
164*f05cddf9SRui Paulo 	len = recvfrom(sock, buf, sizeof(buf), 0,
165*f05cddf9SRui Paulo 		       (struct sockaddr *) &from.ss, &fromlen);
166*f05cddf9SRui Paulo 	if (len < 0) {
167*f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
168*f05cddf9SRui Paulo 		return;
169*f05cddf9SRui Paulo 	}
170*f05cddf9SRui Paulo 
171*f05cddf9SRui Paulo 	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
172*f05cddf9SRui Paulo 	from_port = ntohs(from.sin.sin_port);
173*f05cddf9SRui Paulo 
174*f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
175*f05cddf9SRui Paulo 		   len, abuf, from_port);
176*f05cddf9SRui Paulo 	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
177*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
178*f05cddf9SRui Paulo 		return;
179*f05cddf9SRui Paulo 	}
180*f05cddf9SRui Paulo 
181*f05cddf9SRui Paulo 	msg = radius_msg_parse(buf, len);
182*f05cddf9SRui Paulo 	if (msg == NULL) {
183*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
184*f05cddf9SRui Paulo 			   "from %s:%d failed", abuf, from_port);
185*f05cddf9SRui Paulo 		return;
186*f05cddf9SRui Paulo 	}
187*f05cddf9SRui Paulo 
188*f05cddf9SRui Paulo 	if (wpa_debug_level <= MSG_MSGDUMP)
189*f05cddf9SRui Paulo 		radius_msg_dump(msg);
190*f05cddf9SRui Paulo 
191*f05cddf9SRui Paulo 	if (radius_msg_verify_das_req(msg, das->shared_secret,
192*f05cddf9SRui Paulo 				       das->shared_secret_len)) {
193*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
194*f05cddf9SRui Paulo 			   "from %s:%d - drop", abuf, from_port);
195*f05cddf9SRui Paulo 		goto fail;
196*f05cddf9SRui Paulo 	}
197*f05cddf9SRui Paulo 
198*f05cddf9SRui Paulo 	os_get_time(&now);
199*f05cddf9SRui Paulo 	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
200*f05cddf9SRui Paulo 				  (u8 *) &val, 4);
201*f05cddf9SRui Paulo 	if (res == 4) {
202*f05cddf9SRui Paulo 		u32 timestamp = ntohl(val);
203*f05cddf9SRui Paulo 		if (abs(now.sec - timestamp) > das->time_window) {
204*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
205*f05cddf9SRui Paulo 				   "Event-Timestamp (%u; local time %u) in "
206*f05cddf9SRui Paulo 				   "packet from %s:%d - drop",
207*f05cddf9SRui Paulo 				   timestamp, (unsigned int) now.sec,
208*f05cddf9SRui Paulo 				   abuf, from_port);
209*f05cddf9SRui Paulo 			goto fail;
210*f05cddf9SRui Paulo 		}
211*f05cddf9SRui Paulo 	} else if (das->require_event_timestamp) {
212*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
213*f05cddf9SRui Paulo 			   "from %s:%d - drop", abuf, from_port);
214*f05cddf9SRui Paulo 		goto fail;
215*f05cddf9SRui Paulo 	}
216*f05cddf9SRui Paulo 
217*f05cddf9SRui Paulo 	hdr = radius_msg_get_hdr(msg);
218*f05cddf9SRui Paulo 
219*f05cddf9SRui Paulo 	switch (hdr->code) {
220*f05cddf9SRui Paulo 	case RADIUS_CODE_DISCONNECT_REQUEST:
221*f05cddf9SRui Paulo 		reply = radius_das_disconnect(das, msg, abuf, from_port);
222*f05cddf9SRui Paulo 		break;
223*f05cddf9SRui Paulo 	case RADIUS_CODE_COA_REQUEST:
224*f05cddf9SRui Paulo 		/* TODO */
225*f05cddf9SRui Paulo 		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
226*f05cddf9SRui Paulo 				       hdr->identifier);
227*f05cddf9SRui Paulo 		if (reply == NULL)
228*f05cddf9SRui Paulo 			break;
229*f05cddf9SRui Paulo 
230*f05cddf9SRui Paulo 		/* Unsupported Service */
231*f05cddf9SRui Paulo 		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
232*f05cddf9SRui Paulo 					       405)) {
233*f05cddf9SRui Paulo 			radius_msg_free(reply);
234*f05cddf9SRui Paulo 			reply = NULL;
235*f05cddf9SRui Paulo 			break;
236*f05cddf9SRui Paulo 		}
237*f05cddf9SRui Paulo 		break;
238*f05cddf9SRui Paulo 	default:
239*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
240*f05cddf9SRui Paulo 			   "packet from %s:%d",
241*f05cddf9SRui Paulo 			   hdr->code, abuf, from_port);
242*f05cddf9SRui Paulo 	}
243*f05cddf9SRui Paulo 
244*f05cddf9SRui Paulo 	if (reply) {
245*f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
246*f05cddf9SRui Paulo 
247*f05cddf9SRui Paulo 		if (!radius_msg_add_attr_int32(reply,
248*f05cddf9SRui Paulo 					       RADIUS_ATTR_EVENT_TIMESTAMP,
249*f05cddf9SRui Paulo 					       now.sec)) {
250*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
251*f05cddf9SRui Paulo 				   "Event-Timestamp attribute");
252*f05cddf9SRui Paulo 		}
253*f05cddf9SRui Paulo 
254*f05cddf9SRui Paulo 		if (radius_msg_finish_das_resp(reply, das->shared_secret,
255*f05cddf9SRui Paulo 					       das->shared_secret_len, hdr) <
256*f05cddf9SRui Paulo 		    0) {
257*f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
258*f05cddf9SRui Paulo 				   "Message-Authenticator attribute");
259*f05cddf9SRui Paulo 		}
260*f05cddf9SRui Paulo 
261*f05cddf9SRui Paulo 		if (wpa_debug_level <= MSG_MSGDUMP)
262*f05cddf9SRui Paulo 			radius_msg_dump(reply);
263*f05cddf9SRui Paulo 
264*f05cddf9SRui Paulo 		rbuf = radius_msg_get_buf(reply);
265*f05cddf9SRui Paulo 		res = sendto(das->sock, wpabuf_head(rbuf),
266*f05cddf9SRui Paulo 			     wpabuf_len(rbuf), 0,
267*f05cddf9SRui Paulo 			     (struct sockaddr *) &from.ss, fromlen);
268*f05cddf9SRui Paulo 		if (res < 0) {
269*f05cddf9SRui Paulo 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
270*f05cddf9SRui Paulo 				   abuf, from_port, strerror(errno));
271*f05cddf9SRui Paulo 		}
272*f05cddf9SRui Paulo 	}
273*f05cddf9SRui Paulo 
274*f05cddf9SRui Paulo fail:
275*f05cddf9SRui Paulo 	radius_msg_free(msg);
276*f05cddf9SRui Paulo 	radius_msg_free(reply);
277*f05cddf9SRui Paulo }
278*f05cddf9SRui Paulo 
279*f05cddf9SRui Paulo 
280*f05cddf9SRui Paulo static int radius_das_open_socket(int port)
281*f05cddf9SRui Paulo {
282*f05cddf9SRui Paulo 	int s;
283*f05cddf9SRui Paulo 	struct sockaddr_in addr;
284*f05cddf9SRui Paulo 
285*f05cddf9SRui Paulo 	s = socket(PF_INET, SOCK_DGRAM, 0);
286*f05cddf9SRui Paulo 	if (s < 0) {
287*f05cddf9SRui Paulo 		perror("socket");
288*f05cddf9SRui Paulo 		return -1;
289*f05cddf9SRui Paulo 	}
290*f05cddf9SRui Paulo 
291*f05cddf9SRui Paulo 	os_memset(&addr, 0, sizeof(addr));
292*f05cddf9SRui Paulo 	addr.sin_family = AF_INET;
293*f05cddf9SRui Paulo 	addr.sin_port = htons(port);
294*f05cddf9SRui Paulo 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
295*f05cddf9SRui Paulo 		perror("bind");
296*f05cddf9SRui Paulo 		close(s);
297*f05cddf9SRui Paulo 		return -1;
298*f05cddf9SRui Paulo 	}
299*f05cddf9SRui Paulo 
300*f05cddf9SRui Paulo 	return s;
301*f05cddf9SRui Paulo }
302*f05cddf9SRui Paulo 
303*f05cddf9SRui Paulo 
304*f05cddf9SRui Paulo struct radius_das_data *
305*f05cddf9SRui Paulo radius_das_init(struct radius_das_conf *conf)
306*f05cddf9SRui Paulo {
307*f05cddf9SRui Paulo 	struct radius_das_data *das;
308*f05cddf9SRui Paulo 
309*f05cddf9SRui Paulo 	if (conf->port == 0 || conf->shared_secret == NULL ||
310*f05cddf9SRui Paulo 	    conf->client_addr == NULL)
311*f05cddf9SRui Paulo 		return NULL;
312*f05cddf9SRui Paulo 
313*f05cddf9SRui Paulo 	das = os_zalloc(sizeof(*das));
314*f05cddf9SRui Paulo 	if (das == NULL)
315*f05cddf9SRui Paulo 		return NULL;
316*f05cddf9SRui Paulo 
317*f05cddf9SRui Paulo 	das->time_window = conf->time_window;
318*f05cddf9SRui Paulo 	das->require_event_timestamp = conf->require_event_timestamp;
319*f05cddf9SRui Paulo 	das->ctx = conf->ctx;
320*f05cddf9SRui Paulo 	das->disconnect = conf->disconnect;
321*f05cddf9SRui Paulo 
322*f05cddf9SRui Paulo 	os_memcpy(&das->client_addr, conf->client_addr,
323*f05cddf9SRui Paulo 		  sizeof(das->client_addr));
324*f05cddf9SRui Paulo 
325*f05cddf9SRui Paulo 	das->shared_secret = os_malloc(conf->shared_secret_len);
326*f05cddf9SRui Paulo 	if (das->shared_secret == NULL) {
327*f05cddf9SRui Paulo 		radius_das_deinit(das);
328*f05cddf9SRui Paulo 		return NULL;
329*f05cddf9SRui Paulo 	}
330*f05cddf9SRui Paulo 	os_memcpy(das->shared_secret, conf->shared_secret,
331*f05cddf9SRui Paulo 		  conf->shared_secret_len);
332*f05cddf9SRui Paulo 	das->shared_secret_len = conf->shared_secret_len;
333*f05cddf9SRui Paulo 
334*f05cddf9SRui Paulo 	das->sock = radius_das_open_socket(conf->port);
335*f05cddf9SRui Paulo 	if (das->sock < 0) {
336*f05cddf9SRui Paulo 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
337*f05cddf9SRui Paulo 			   "DAS");
338*f05cddf9SRui Paulo 		radius_das_deinit(das);
339*f05cddf9SRui Paulo 		return NULL;
340*f05cddf9SRui Paulo 	}
341*f05cddf9SRui Paulo 
342*f05cddf9SRui Paulo 	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
343*f05cddf9SRui Paulo 	{
344*f05cddf9SRui Paulo 		radius_das_deinit(das);
345*f05cddf9SRui Paulo 		return NULL;
346*f05cddf9SRui Paulo 	}
347*f05cddf9SRui Paulo 
348*f05cddf9SRui Paulo 	return das;
349*f05cddf9SRui Paulo }
350*f05cddf9SRui Paulo 
351*f05cddf9SRui Paulo 
352*f05cddf9SRui Paulo void radius_das_deinit(struct radius_das_data *das)
353*f05cddf9SRui Paulo {
354*f05cddf9SRui Paulo 	if (das == NULL)
355*f05cddf9SRui Paulo 		return;
356*f05cddf9SRui Paulo 
357*f05cddf9SRui Paulo 	if (das->sock >= 0) {
358*f05cddf9SRui Paulo 		eloop_unregister_read_sock(das->sock);
359*f05cddf9SRui Paulo 		close(das->sock);
360*f05cddf9SRui Paulo 	}
361*f05cddf9SRui Paulo 
362*f05cddf9SRui Paulo 	os_free(das->shared_secret);
363*f05cddf9SRui Paulo 	os_free(das);
364*f05cddf9SRui Paulo }
365