radius_das.c revision 61d9df3e62aaa0e87ad05452fcb95142159a17b6
104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt/*
204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt *
504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * This software may be distributed under the terms of the BSD license.
604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * See README for more details.
704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt */
804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "includes.h"
1004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include <net/if.h>
1104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
1204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "utils/common.h"
1304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "utils/eloop.h"
1404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "utils/ip_addr.h"
1504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "radius.h"
1604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#include "radius_das.h"
1704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
1804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
1904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtextern int wpa_debug_level;
2004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
2104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
2204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct radius_das_data {
2304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int sock;
2404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *shared_secret;
2504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t shared_secret_len;
2604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct hostapd_ip_addr client_addr;
2704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int time_window;
2804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int require_event_timestamp;
2904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	void *ctx;
3004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	enum radius_das_res (*disconnect)(void *ctx,
3104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					  struct radius_das_attrs *attr);
3204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt};
3304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
3604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 struct radius_msg *msg,
3704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 const char *abuf,
3804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 int from_port)
3904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
4004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_hdr *hdr;
4104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_msg *reply;
4204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 allowed[] = {
4304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_USER_NAME,
4404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_CALLING_STATION_ID,
4504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_ACCT_SESSION_ID,
4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_EVENT_TIMESTAMP,
4704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
4804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
4904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		0
5004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	};
5104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int error = 405;
5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 attr;
5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	enum radius_das_res res;
5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_attrs attrs;
5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *buf;
5604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t len;
5704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char tmp[100];
5804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 sta_addr[ETH_ALEN];
5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	attr = radius_msg_find_unlisted_attr(msg, allowed);
6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (attr) {
6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "Disconnect-Request from %s:%d", attr,
6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
6704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 401;
6804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
6904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
7004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&attrs, 0, sizeof(attrs));
7204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
7404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
7504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (len >= sizeof(tmp))
7604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			len = sizeof(tmp) - 1;
7704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		os_memcpy(tmp, buf, len);
7804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tmp[len] = '\0';
7904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hwaddr_aton2(tmp, sta_addr) < 0) {
8004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
8104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "'%s' from %s:%d", tmp, abuf, from_port);
8204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			error = 407;
8304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
8404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
8504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.sta_addr = sta_addr;
8604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
8704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
8804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
8904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
9004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name = buf;
9104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name_len = len;
9204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
9304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
9404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
9504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
9604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id = buf;
9704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id_len = len;
9804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
9904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
10104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
10204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui = buf;
10304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui_len = len;
10404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
10504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = das->disconnect(das->ctx, &attrs);
10704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (res) {
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_NAS_MISMATCH:
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 403;
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SESSION_NOT_FOUND:
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "%s:%d", abuf, from_port);
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 503;
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SUCCESS:
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 0;
12004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
12104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply == NULL)
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (error) {
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       error)) {
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
13361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return NULL;
13461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
13504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
13604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return reply;
13804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
13904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
14004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
14104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
14204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
14304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das = eloop_ctx;
14404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 buf[1500];
14504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	union {
14604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_storage ss;
14704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in sin;
14804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#ifdef CONFIG_IPV6
14904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in6 sin6;
15004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#endif /* CONFIG_IPV6 */
15104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} from;
15204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char abuf[50];
15304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int from_port = 0;
15404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	socklen_t fromlen;
15504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int len;
15604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_msg *msg, *reply = NULL;
15704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_hdr *hdr;
15804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *rbuf;
15904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u32 val;
16004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int res;
16104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct os_time now;
16204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
16304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	fromlen = sizeof(from);
16404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = recvfrom(sock, buf, sizeof(buf), 0,
16504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		       (struct sockaddr *) &from.ss, &fromlen);
16604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 0) {
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
16804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
17004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
17204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	from_port = ntohs(from.sin.sin_port);
17304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
17504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   len, abuf, from_port);
17604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
17704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
17804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
17904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
18004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	msg = radius_msg_parse(buf, len);
18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (msg == NULL) {
18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d failed", abuf, from_port);
18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (wpa_debug_level <= MSG_MSGDUMP)
18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_msg_dump(msg);
19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_verify_das_req(msg, das->shared_secret,
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       das->shared_secret_len)) {
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
19504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
19704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_get_time(&now);
19904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
20004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  (u8 *) &val, 4);
20104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (res == 4) {
20204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u32 timestamp = ntohl(val);
20304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (abs(now.sec - timestamp) > das->time_window) {
20404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
20504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp (%u; local time %u) in "
20604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "packet from %s:%d - drop",
20704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   timestamp, (unsigned int) now.sec,
20804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port);
20904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
21004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
21104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else if (das->require_event_timestamp) {
21204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
21304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
21404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
21504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
21604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
21804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (hdr->code) {
22004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_DISCONNECT_REQUEST:
22104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_das_disconnect(das, msg, abuf, from_port);
22204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
22304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_COA_REQUEST:
22404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* TODO */
22504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
22604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       hdr->identifier);
22704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (reply == NULL)
22804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
22904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* Unsupported Service */
23161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
23261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       405)) {
23361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
23461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			reply = NULL;
23561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			break;
23661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
23704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
23804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	default:
23904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
24004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "packet from %s:%d",
24104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   hdr->code, abuf, from_port);
24204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
24304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply) {
24504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
24604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!radius_msg_add_attr_int32(reply,
24804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       RADIUS_ATTR_EVENT_TIMESTAMP,
24904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       now.sec)) {
25004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
25104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp attribute");
25204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
25304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
25404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (radius_msg_finish_das_resp(reply, das->shared_secret,
25504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       das->shared_secret_len, hdr) <
25604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    0) {
25704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
25804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Message-Authenticator attribute");
25904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
26004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
26104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (wpa_debug_level <= MSG_MSGDUMP)
26204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			radius_msg_dump(reply);
26304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
26404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rbuf = radius_msg_get_buf(reply);
26504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		res = sendto(das->sock, wpabuf_head(rbuf),
26604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     wpabuf_len(rbuf), 0,
26704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     (struct sockaddr *) &from.ss, fromlen);
26804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (res < 0) {
26904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
27004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port, strerror(errno));
27104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
27204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
27304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
27404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
27504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(msg);
27604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(reply);
27704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
27804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
27904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
28004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int radius_das_open_socket(int port)
28104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
28204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int s;
28304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sockaddr_in addr;
28404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
28504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	s = socket(PF_INET, SOCK_DGRAM, 0);
28604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (s < 0) {
28704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		perror("socket");
28804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
28904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
29004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
29104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&addr, 0, sizeof(addr));
29204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_family = AF_INET;
29304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_port = htons(port);
29404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
29504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		perror("bind");
29604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(s);
29704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
29804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
29904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return s;
30104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
30204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct radius_das_data *
30504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtradius_das_init(struct radius_das_conf *conf)
30604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
30704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das;
30804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (conf->port == 0 || conf->shared_secret == NULL ||
31004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    conf->client_addr == NULL)
31104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
31204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das = os_zalloc(sizeof(*das));
31404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
31504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
31604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->time_window = conf->time_window;
31804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->require_event_timestamp = conf->require_event_timestamp;
31904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->ctx = conf->ctx;
32004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->disconnect = conf->disconnect;
32104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(&das->client_addr, conf->client_addr,
32304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  sizeof(das->client_addr));
32404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret = os_malloc(conf->shared_secret_len);
32604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->shared_secret == NULL) {
32704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
32804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
32904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
33004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(das->shared_secret, conf->shared_secret,
33104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  conf->shared_secret_len);
33204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret_len = conf->shared_secret_len;
33304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->sock = radius_das_open_socket(conf->port);
33504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock < 0) {
33604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
33704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "DAS");
33804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
33904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
34004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
34104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
34304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	{
34404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
34504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
34604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
34704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return das;
34904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
35004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid radius_das_deinit(struct radius_das_data *das)
35304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
35404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
35504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
35604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock >= 0) {
35804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eloop_unregister_read_sock(das->sock);
35904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(das->sock);
36004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
36104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
36204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das->shared_secret);
36304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das);
36404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
365