Home | History | Annotate | Line # | Download | only in radius
      1 /*
      2  * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
      3  * Copyright (c) 2012-2013, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This software may be distributed under the terms of the BSD license.
      6  * See README for more details.
      7  */
      8 
      9 #include "includes.h"
     10 #include <net/if.h>
     11 
     12 #include "utils/common.h"
     13 #include "utils/eloop.h"
     14 #include "utils/ip_addr.h"
     15 #include "radius.h"
     16 #include "radius_das.h"
     17 
     18 
     19 struct radius_das_data {
     20 	int sock;
     21 	u8 *shared_secret;
     22 	size_t shared_secret_len;
     23 	struct hostapd_ip_addr client_addr;
     24 	unsigned int time_window;
     25 	int require_event_timestamp;
     26 	int require_message_authenticator;
     27 	void *ctx;
     28 	enum radius_das_res (*disconnect)(void *ctx,
     29 					  struct radius_das_attrs *attr);
     30 	enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
     31 };
     32 
     33 
     34 static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
     35 						 struct radius_msg *msg,
     36 						 const char *abuf,
     37 						 int from_port)
     38 {
     39 	struct radius_hdr *hdr;
     40 	struct radius_msg *reply;
     41 	u8 allowed[] = {
     42 		RADIUS_ATTR_USER_NAME,
     43 		RADIUS_ATTR_NAS_IP_ADDRESS,
     44 		RADIUS_ATTR_CALLING_STATION_ID,
     45 		RADIUS_ATTR_NAS_IDENTIFIER,
     46 		RADIUS_ATTR_ACCT_SESSION_ID,
     47 		RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
     48 		RADIUS_ATTR_EVENT_TIMESTAMP,
     49 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
     50 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
     51 #ifdef CONFIG_IPV6
     52 		RADIUS_ATTR_NAS_IPV6_ADDRESS,
     53 #endif /* CONFIG_IPV6 */
     54 		0
     55 	};
     56 	int error = 405;
     57 	u8 attr;
     58 	enum radius_das_res res;
     59 	struct radius_das_attrs attrs;
     60 	u8 *buf;
     61 	size_t len;
     62 	char tmp[100];
     63 	u8 sta_addr[ETH_ALEN];
     64 
     65 	hdr = radius_msg_get_hdr(msg);
     66 
     67 	attr = radius_msg_find_unlisted_attr(msg, allowed);
     68 	if (attr) {
     69 		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
     70 			   "Disconnect-Request from %s:%d", attr,
     71 			   abuf, from_port);
     72 		error = 401;
     73 		goto fail;
     74 	}
     75 
     76 	os_memset(&attrs, 0, sizeof(attrs));
     77 
     78 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
     79 				    &buf, &len, NULL) == 0) {
     80 		if (len != 4) {
     81 			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
     82 				   abuf, from_port);
     83 			error = 407;
     84 			goto fail;
     85 		}
     86 		attrs.nas_ip_addr = buf;
     87 	}
     88 
     89 #ifdef CONFIG_IPV6
     90 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
     91 				    &buf, &len, NULL) == 0) {
     92 		if (len != 16) {
     93 			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
     94 				   abuf, from_port);
     95 			error = 407;
     96 			goto fail;
     97 		}
     98 		attrs.nas_ipv6_addr = buf;
     99 	}
    100 #endif /* CONFIG_IPV6 */
    101 
    102 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
    103 				    &buf, &len, NULL) == 0) {
    104 		attrs.nas_identifier = buf;
    105 		attrs.nas_identifier_len = len;
    106 	}
    107 
    108 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
    109 				    &buf, &len, NULL) == 0) {
    110 		if (len >= sizeof(tmp))
    111 			len = sizeof(tmp) - 1;
    112 		os_memcpy(tmp, buf, len);
    113 		tmp[len] = '\0';
    114 		if (hwaddr_aton2(tmp, sta_addr) < 0) {
    115 			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
    116 				   "'%s' from %s:%d", tmp, abuf, from_port);
    117 			error = 407;
    118 			goto fail;
    119 		}
    120 		attrs.sta_addr = sta_addr;
    121 	}
    122 
    123 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
    124 				    &buf, &len, NULL) == 0) {
    125 		attrs.user_name = buf;
    126 		attrs.user_name_len = len;
    127 	}
    128 
    129 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
    130 				    &buf, &len, NULL) == 0) {
    131 		attrs.acct_session_id = buf;
    132 		attrs.acct_session_id_len = len;
    133 	}
    134 
    135 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
    136 				    &buf, &len, NULL) == 0) {
    137 		attrs.acct_multi_session_id = buf;
    138 		attrs.acct_multi_session_id_len = len;
    139 	}
    140 
    141 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
    142 				    &buf, &len, NULL) == 0) {
    143 		attrs.cui = buf;
    144 		attrs.cui_len = len;
    145 	}
    146 
    147 	res = das->disconnect(das->ctx, &attrs);
    148 	switch (res) {
    149 	case RADIUS_DAS_NAS_MISMATCH:
    150 		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
    151 			   abuf, from_port);
    152 		error = 403;
    153 		break;
    154 	case RADIUS_DAS_SESSION_NOT_FOUND:
    155 		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
    156 			   "%s:%d", abuf, from_port);
    157 		error = 503;
    158 		break;
    159 	case RADIUS_DAS_MULTI_SESSION_MATCH:
    160 		wpa_printf(MSG_INFO,
    161 			   "DAS: Multiple sessions match for request from %s:%d",
    162 			   abuf, from_port);
    163 		error = 508;
    164 		break;
    165 	case RADIUS_DAS_COA_FAILED:
    166 		/* not used with Disconnect-Request */
    167 		error = 405;
    168 		break;
    169 	case RADIUS_DAS_SUCCESS:
    170 		error = 0;
    171 		break;
    172 	}
    173 
    174 fail:
    175 	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
    176 			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
    177 	if (reply == NULL)
    178 		return NULL;
    179 
    180 	if (!radius_msg_add_msg_auth(reply)) {
    181 		radius_msg_free(reply);
    182 		return NULL;
    183 	}
    184 
    185 	if (error) {
    186 		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
    187 					       error)) {
    188 			radius_msg_free(reply);
    189 			return NULL;
    190 		}
    191 	}
    192 
    193 	return reply;
    194 }
    195 
    196 
    197 static struct radius_msg * radius_das_coa(struct radius_das_data *das,
    198 					  struct radius_msg *msg,
    199 					  const char *abuf, int from_port)
    200 {
    201 	struct radius_hdr *hdr;
    202 	struct radius_msg *reply;
    203 	u8 allowed[] = {
    204 		RADIUS_ATTR_USER_NAME,
    205 		RADIUS_ATTR_NAS_IP_ADDRESS,
    206 		RADIUS_ATTR_CALLING_STATION_ID,
    207 		RADIUS_ATTR_NAS_IDENTIFIER,
    208 		RADIUS_ATTR_ACCT_SESSION_ID,
    209 		RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
    210 		RADIUS_ATTR_EVENT_TIMESTAMP,
    211 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
    212 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
    213 #ifdef CONFIG_HS20
    214 		RADIUS_ATTR_VENDOR_SPECIFIC,
    215 #endif /* CONFIG_HS20 */
    216 #ifdef CONFIG_IPV6
    217 		RADIUS_ATTR_NAS_IPV6_ADDRESS,
    218 #endif /* CONFIG_IPV6 */
    219 		0
    220 	};
    221 	int error = 405;
    222 	u8 attr;
    223 	enum radius_das_res res;
    224 	struct radius_das_attrs attrs;
    225 	u8 *buf;
    226 	size_t len;
    227 	char tmp[100];
    228 	u8 sta_addr[ETH_ALEN];
    229 
    230 	hdr = radius_msg_get_hdr(msg);
    231 
    232 	if (!das->coa) {
    233 		wpa_printf(MSG_INFO, "DAS: CoA not supported");
    234 		goto fail;
    235 	}
    236 
    237 	attr = radius_msg_find_unlisted_attr(msg, allowed);
    238 	if (attr) {
    239 		wpa_printf(MSG_INFO,
    240 			   "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
    241 			   attr, abuf, from_port);
    242 		error = 401;
    243 		goto fail;
    244 	}
    245 
    246 	os_memset(&attrs, 0, sizeof(attrs));
    247 
    248 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
    249 				    &buf, &len, NULL) == 0) {
    250 		if (len != 4) {
    251 			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
    252 				   abuf, from_port);
    253 			error = 407;
    254 			goto fail;
    255 		}
    256 		attrs.nas_ip_addr = buf;
    257 	}
    258 
    259 #ifdef CONFIG_IPV6
    260 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
    261 				    &buf, &len, NULL) == 0) {
    262 		if (len != 16) {
    263 			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
    264 				   abuf, from_port);
    265 			error = 407;
    266 			goto fail;
    267 		}
    268 		attrs.nas_ipv6_addr = buf;
    269 	}
    270 #endif /* CONFIG_IPV6 */
    271 
    272 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
    273 				    &buf, &len, NULL) == 0) {
    274 		attrs.nas_identifier = buf;
    275 		attrs.nas_identifier_len = len;
    276 	}
    277 
    278 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
    279 				    &buf, &len, NULL) == 0) {
    280 		if (len >= sizeof(tmp))
    281 			len = sizeof(tmp) - 1;
    282 		os_memcpy(tmp, buf, len);
    283 		tmp[len] = '\0';
    284 		if (hwaddr_aton2(tmp, sta_addr) < 0) {
    285 			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
    286 				   "'%s' from %s:%d", tmp, abuf, from_port);
    287 			error = 407;
    288 			goto fail;
    289 		}
    290 		attrs.sta_addr = sta_addr;
    291 	}
    292 
    293 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
    294 				    &buf, &len, NULL) == 0) {
    295 		attrs.user_name = buf;
    296 		attrs.user_name_len = len;
    297 	}
    298 
    299 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
    300 				    &buf, &len, NULL) == 0) {
    301 		attrs.acct_session_id = buf;
    302 		attrs.acct_session_id_len = len;
    303 	}
    304 
    305 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
    306 				    &buf, &len, NULL) == 0) {
    307 		attrs.acct_multi_session_id = buf;
    308 		attrs.acct_multi_session_id_len = len;
    309 	}
    310 
    311 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
    312 				    &buf, &len, NULL) == 0) {
    313 		attrs.cui = buf;
    314 		attrs.cui_len = len;
    315 	}
    316 
    317 #ifdef CONFIG_HS20
    318 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
    319 				    &buf, &len, NULL) == 0) {
    320 		if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA ||
    321 		    buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING ||
    322 		    buf[5] < 6) {
    323 			wpa_printf(MSG_INFO,
    324 				   "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
    325 				   attr, abuf, from_port);
    326 			error = 401;
    327 			goto fail;
    328 		}
    329 		attrs.hs20_t_c_filtering = &buf[6];
    330 	}
    331 
    332 	if (!attrs.hs20_t_c_filtering) {
    333 			wpa_printf(MSG_INFO,
    334 				   "DAS: No supported authorization change attribute in CoA-Request from %s:%d",
    335 				   abuf, from_port);
    336 			error = 402;
    337 			goto fail;
    338 	}
    339 #endif /* CONFIG_HS20 */
    340 
    341 	res = das->coa(das->ctx, &attrs);
    342 	switch (res) {
    343 	case RADIUS_DAS_NAS_MISMATCH:
    344 		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
    345 			   abuf, from_port);
    346 		error = 403;
    347 		break;
    348 	case RADIUS_DAS_SESSION_NOT_FOUND:
    349 		wpa_printf(MSG_INFO,
    350 			   "DAS: Session not found for request from %s:%d",
    351 			   abuf, from_port);
    352 		error = 503;
    353 		break;
    354 	case RADIUS_DAS_MULTI_SESSION_MATCH:
    355 		wpa_printf(MSG_INFO,
    356 			   "DAS: Multiple sessions match for request from %s:%d",
    357 			   abuf, from_port);
    358 		error = 508;
    359 		break;
    360 	case RADIUS_DAS_COA_FAILED:
    361 		wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d",
    362 			   abuf, from_port);
    363 		error = 407;
    364 		break;
    365 	case RADIUS_DAS_SUCCESS:
    366 		error = 0;
    367 		break;
    368 	}
    369 
    370 fail:
    371 	reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK :
    372 			       RADIUS_CODE_COA_ACK, hdr->identifier);
    373 	if (!reply)
    374 		return NULL;
    375 
    376 	if (!radius_msg_add_msg_auth(reply)) {
    377 		radius_msg_free(reply);
    378 		return NULL;
    379 	}
    380 
    381 	if (error &&
    382 	    !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
    383 		radius_msg_free(reply);
    384 		return NULL;
    385 	}
    386 
    387 	return reply;
    388 }
    389 
    390 
    391 static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
    392 {
    393 	struct radius_das_data *das = eloop_ctx;
    394 	u8 buf[1500];
    395 	union {
    396 		struct sockaddr_storage ss;
    397 		struct sockaddr_in sin;
    398 #ifdef CONFIG_IPV6
    399 		struct sockaddr_in6 sin6;
    400 #endif /* CONFIG_IPV6 */
    401 	} from;
    402 	char abuf[50];
    403 	int from_port = 0;
    404 	socklen_t fromlen;
    405 	int len;
    406 	struct radius_msg *msg, *reply = NULL;
    407 	struct radius_hdr *hdr;
    408 	struct wpabuf *rbuf;
    409 	u32 val;
    410 	int res;
    411 	struct os_time now;
    412 
    413 	fromlen = sizeof(from);
    414 	len = recvfrom(sock, buf, sizeof(buf), 0,
    415 		       (struct sockaddr *) &from.ss, &fromlen);
    416 	if (len < 0) {
    417 		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
    418 		return;
    419 	}
    420 
    421 	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
    422 	from_port = ntohs(from.sin.sin_port);
    423 
    424 	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
    425 		   len, abuf, from_port);
    426 	if (das->client_addr.u.v4.s_addr &&
    427 	    das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
    428 		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
    429 		return;
    430 	}
    431 
    432 	msg = radius_msg_parse(buf, len);
    433 	if (msg == NULL) {
    434 		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
    435 			   "from %s:%d failed", abuf, from_port);
    436 		return;
    437 	}
    438 
    439 	if (wpa_debug_level <= MSG_MSGDUMP)
    440 		radius_msg_dump(msg);
    441 
    442 	if (radius_msg_verify_das_req(msg, das->shared_secret,
    443 				       das->shared_secret_len,
    444 				       das->require_message_authenticator)) {
    445 		wpa_printf(MSG_DEBUG,
    446 			   "DAS: Invalid authenticator or Message-Authenticator in packet from %s:%d - drop",
    447 			   abuf, from_port);
    448 		goto fail;
    449 	}
    450 
    451 	os_get_time(&now);
    452 	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
    453 				  (u8 *) &val, 4);
    454 	if (res == 4) {
    455 		u32 timestamp = ntohl(val);
    456 		if ((unsigned int) abs((int) (now.sec - timestamp)) >
    457 		    das->time_window) {
    458 			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
    459 				   "Event-Timestamp (%u; local time %u) in "
    460 				   "packet from %s:%d - drop",
    461 				   timestamp, (unsigned int) now.sec,
    462 				   abuf, from_port);
    463 			goto fail;
    464 		}
    465 	} else if (das->require_event_timestamp) {
    466 		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
    467 			   "from %s:%d - drop", abuf, from_port);
    468 		goto fail;
    469 	}
    470 
    471 	hdr = radius_msg_get_hdr(msg);
    472 
    473 	switch (hdr->code) {
    474 	case RADIUS_CODE_DISCONNECT_REQUEST:
    475 		reply = radius_das_disconnect(das, msg, abuf, from_port);
    476 		break;
    477 	case RADIUS_CODE_COA_REQUEST:
    478 		reply = radius_das_coa(das, msg, abuf, from_port);
    479 		break;
    480 	default:
    481 		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
    482 			   "packet from %s:%d",
    483 			   hdr->code, abuf, from_port);
    484 	}
    485 
    486 	if (reply) {
    487 		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
    488 
    489 		if (!radius_msg_add_attr_int32(reply,
    490 					       RADIUS_ATTR_EVENT_TIMESTAMP,
    491 					       now.sec)) {
    492 			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
    493 				   "Event-Timestamp attribute");
    494 		}
    495 
    496 		if (radius_msg_finish_das_resp(reply, das->shared_secret,
    497 					       das->shared_secret_len, hdr) <
    498 		    0) {
    499 			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
    500 				   "Message-Authenticator attribute");
    501 		}
    502 
    503 		if (wpa_debug_level <= MSG_MSGDUMP)
    504 			radius_msg_dump(reply);
    505 
    506 		rbuf = radius_msg_get_buf(reply);
    507 		res = sendto(das->sock, wpabuf_head(rbuf),
    508 			     wpabuf_len(rbuf), 0,
    509 			     (struct sockaddr *) &from.ss, fromlen);
    510 		if (res < 0) {
    511 			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
    512 				   abuf, from_port, strerror(errno));
    513 		}
    514 	}
    515 
    516 fail:
    517 	radius_msg_free(msg);
    518 	radius_msg_free(reply);
    519 }
    520 
    521 
    522 static int radius_das_open_socket(int port)
    523 {
    524 	int s;
    525 	struct sockaddr_in addr;
    526 
    527 	s = socket(PF_INET, SOCK_DGRAM, 0);
    528 	if (s < 0) {
    529 		wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
    530 		return -1;
    531 	}
    532 
    533 	os_memset(&addr, 0, sizeof(addr));
    534 	addr.sin_family = AF_INET;
    535 	addr.sin_port = htons(port);
    536 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    537 		wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
    538 		close(s);
    539 		return -1;
    540 	}
    541 
    542 	return s;
    543 }
    544 
    545 
    546 struct radius_das_data *
    547 radius_das_init(struct radius_das_conf *conf)
    548 {
    549 	struct radius_das_data *das;
    550 
    551 	if (conf->port == 0 || conf->shared_secret == NULL ||
    552 	    conf->client_addr == NULL)
    553 		return NULL;
    554 
    555 	das = os_zalloc(sizeof(*das));
    556 	if (das == NULL)
    557 		return NULL;
    558 
    559 	das->time_window = conf->time_window;
    560 	das->require_event_timestamp = conf->require_event_timestamp;
    561 	das->require_message_authenticator =
    562 		conf->require_message_authenticator;
    563 	das->ctx = conf->ctx;
    564 	das->disconnect = conf->disconnect;
    565 	das->coa = conf->coa;
    566 
    567 	os_memcpy(&das->client_addr, conf->client_addr,
    568 		  sizeof(das->client_addr));
    569 
    570 	das->shared_secret = os_memdup(conf->shared_secret,
    571 				       conf->shared_secret_len);
    572 	if (das->shared_secret == NULL) {
    573 		radius_das_deinit(das);
    574 		return NULL;
    575 	}
    576 	das->shared_secret_len = conf->shared_secret_len;
    577 
    578 	das->sock = radius_das_open_socket(conf->port);
    579 	if (das->sock < 0) {
    580 		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
    581 			   "DAS");
    582 		radius_das_deinit(das);
    583 		return NULL;
    584 	}
    585 
    586 	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
    587 	{
    588 		radius_das_deinit(das);
    589 		return NULL;
    590 	}
    591 
    592 	return das;
    593 }
    594 
    595 
    596 void radius_das_deinit(struct radius_das_data *das)
    597 {
    598 	if (das == NULL)
    599 		return;
    600 
    601 	if (das->sock >= 0) {
    602 		eloop_unregister_read_sock(das->sock);
    603 		close(das->sock);
    604 	}
    605 
    606 	os_free(das->shared_secret);
    607 	os_free(das);
    608 }
    609