104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt/*
204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
3cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt * Copyright (c) 2012-2013, 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 Shmidtstruct radius_das_data {
2004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int sock;
2104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *shared_secret;
2204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t shared_secret_len;
2304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct hostapd_ip_addr client_addr;
2404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	unsigned int time_window;
2504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int require_event_timestamp;
2604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	void *ctx;
2704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	enum radius_das_res (*disconnect)(void *ctx,
2804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					  struct radius_das_attrs *attr);
2904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt};
3004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
3104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
3204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
3304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 struct radius_msg *msg,
3404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 const char *abuf,
3504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt						 int from_port)
3604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
3704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_hdr *hdr;
3804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_msg *reply;
3904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 allowed[] = {
4004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_USER_NAME,
4113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		RADIUS_ATTR_NAS_IP_ADDRESS,
4204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_CALLING_STATION_ID,
4313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		RADIUS_ATTR_NAS_IDENTIFIER,
4404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_ACCT_SESSION_ID,
45432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_EVENT_TIMESTAMP,
4704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
4804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
4913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#ifdef CONFIG_IPV6
5013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		RADIUS_ATTR_NAS_IPV6_ADDRESS,
5113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#endif /* CONFIG_IPV6 */
5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		0
5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	};
5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int error = 405;
5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 attr;
5604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	enum radius_das_res res;
5704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_attrs attrs;
5804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *buf;
5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t len;
6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char tmp[100];
6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 sta_addr[ETH_ALEN];
6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	attr = radius_msg_find_unlisted_attr(msg, allowed);
6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (attr) {
6704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
6804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "Disconnect-Request from %s:%d", attr,
6904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
7004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 401;
7104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
7204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
7304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&attrs, 0, sizeof(attrs));
7504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
7713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
7813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		if (len != 4) {
7913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
8013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				   abuf, from_port);
8113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			error = 407;
8213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			goto fail;
8313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		}
8413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_ip_addr = buf;
8513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
8613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
8713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#ifdef CONFIG_IPV6
8813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
8913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
9013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		if (len != 16) {
9113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
9213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				   abuf, from_port);
9313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			error = 407;
9413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			goto fail;
9513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		}
9613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_ipv6_addr = buf;
9713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
9813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#endif /* CONFIG_IPV6 */
9913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
10013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
10113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
10213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_identifier = buf;
10313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_identifier_len = len;
10413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
10513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
10604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
10704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (len >= sizeof(tmp))
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			len = sizeof(tmp) - 1;
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		os_memcpy(tmp, buf, len);
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tmp[len] = '\0';
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hwaddr_aton2(tmp, sta_addr) < 0) {
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "'%s' from %s:%d", tmp, abuf, from_port);
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			error = 407;
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.sta_addr = sta_addr;
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
12004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name = buf;
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name_len = len;
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id = buf;
13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id_len = len;
13104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
13204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
133432d603c922e970f55866c63212d29c997438977Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
134432d603c922e970f55866c63212d29c997438977Dmitry Shmidt				    &buf, &len, NULL) == 0) {
135432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		attrs.acct_multi_session_id = buf;
136432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		attrs.acct_multi_session_id_len = len;
137432d603c922e970f55866c63212d29c997438977Dmitry Shmidt	}
138432d603c922e970f55866c63212d29c997438977Dmitry Shmidt
13904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
14004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
14104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui = buf;
14204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui_len = len;
14304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
14404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
14504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = das->disconnect(das->ctx, &attrs);
14604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (res) {
14704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_NAS_MISMATCH:
14804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
14904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
15004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 403;
15104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
15204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SESSION_NOT_FOUND:
15304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
15404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "%s:%d", abuf, from_port);
15504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 503;
15604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
157432d603c922e970f55866c63212d29c997438977Dmitry Shmidt	case RADIUS_DAS_MULTI_SESSION_MATCH:
158432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		wpa_printf(MSG_INFO,
159432d603c922e970f55866c63212d29c997438977Dmitry Shmidt			   "DAS: Multiple sessions match for request from %s:%d",
160432d603c922e970f55866c63212d29c997438977Dmitry Shmidt			   abuf, from_port);
161432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		error = 508;
162432d603c922e970f55866c63212d29c997438977Dmitry Shmidt		break;
16304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SUCCESS:
16404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 0;
16504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
16604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
16804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
17004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
17104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply == NULL)
17204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
17304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (error) {
17561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
17661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       error)) {
17761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
17861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return NULL;
17961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
18004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
18104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return reply;
18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das = eloop_ctx;
18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 buf[1500];
19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	union {
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_storage ss;
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in sin;
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#ifdef CONFIG_IPV6
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in6 sin6;
19504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#endif /* CONFIG_IPV6 */
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} from;
19704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char abuf[50];
19804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int from_port = 0;
19904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	socklen_t fromlen;
20004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int len;
20104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_msg *msg, *reply = NULL;
20204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_hdr *hdr;
20304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *rbuf;
20404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u32 val;
20504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int res;
20604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct os_time now;
20704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
20804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	fromlen = sizeof(from);
20904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = recvfrom(sock, buf, sizeof(buf), 0,
21004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		       (struct sockaddr *) &from.ss, &fromlen);
21104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 0) {
21204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
21304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
21404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
21504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
21704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	from_port = ntohs(from.sin.sin_port);
21804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
22004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   len, abuf, from_port);
22104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
22204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
22304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
22404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
22504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
22604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	msg = radius_msg_parse(buf, len);
22704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (msg == NULL) {
22804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
22904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d failed", abuf, from_port);
23004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
23104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
23204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (wpa_debug_level <= MSG_MSGDUMP)
23404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_msg_dump(msg);
23504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_verify_das_req(msg, das->shared_secret,
23704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       das->shared_secret_len)) {
23804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
23904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
24004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
24104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
24204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_get_time(&now);
24404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
24504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  (u8 *) &val, 4);
24604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (res == 4) {
24704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u32 timestamp = ntohl(val);
2485460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt		if ((unsigned int) abs(now.sec - timestamp) >
2495460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt		    das->time_window) {
25004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
25104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp (%u; local time %u) in "
25204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "packet from %s:%d - drop",
25304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   timestamp, (unsigned int) now.sec,
25404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port);
25504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
25604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
25704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else if (das->require_event_timestamp) {
25804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
25904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
26004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
26104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
26204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
26304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
26404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
26504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (hdr->code) {
26604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_DISCONNECT_REQUEST:
26704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_das_disconnect(das, msg, abuf, from_port);
26804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
26904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_COA_REQUEST:
27004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* TODO */
27104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
27204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       hdr->identifier);
27304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (reply == NULL)
27404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
27504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
27604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* Unsupported Service */
27761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
27861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       405)) {
27961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
28061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			reply = NULL;
28161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			break;
28261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
28304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
28404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	default:
28504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
28604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "packet from %s:%d",
28704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   hdr->code, abuf, from_port);
28804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
28904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
29004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply) {
29104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
29204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
29304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!radius_msg_add_attr_int32(reply,
29404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       RADIUS_ATTR_EVENT_TIMESTAMP,
29504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       now.sec)) {
29604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
29704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp attribute");
29804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
29904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (radius_msg_finish_das_resp(reply, das->shared_secret,
30104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       das->shared_secret_len, hdr) <
30204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    0) {
30304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
30404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Message-Authenticator attribute");
30504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
30604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (wpa_debug_level <= MSG_MSGDUMP)
30804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			radius_msg_dump(reply);
30904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rbuf = radius_msg_get_buf(reply);
31104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		res = sendto(das->sock, wpabuf_head(rbuf),
31204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     wpabuf_len(rbuf), 0,
31304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     (struct sockaddr *) &from.ss, fromlen);
31404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (res < 0) {
31504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
31604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port, strerror(errno));
31704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
31804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
31904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
32104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(msg);
32204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(reply);
32304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
32404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32604949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int radius_das_open_socket(int port)
32704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
32804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int s;
32904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sockaddr_in addr;
33004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	s = socket(PF_INET, SOCK_DGRAM, 0);
33204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (s < 0) {
333cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt		wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
33404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
33504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
33604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&addr, 0, sizeof(addr));
33804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_family = AF_INET;
33904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_port = htons(port);
34004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
341cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt		wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
34204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(s);
34304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
34404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
34504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return s;
34704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
34804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35004949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct radius_das_data *
35104949598a23f501be6eec21697465fd46a28840aDmitry Shmidtradius_das_init(struct radius_das_conf *conf)
35204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
35304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das;
35404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (conf->port == 0 || conf->shared_secret == NULL ||
35604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    conf->client_addr == NULL)
35704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
35804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das = os_zalloc(sizeof(*das));
36004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
36104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
36204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
36304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->time_window = conf->time_window;
36404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->require_event_timestamp = conf->require_event_timestamp;
36504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->ctx = conf->ctx;
36604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->disconnect = conf->disconnect;
36704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
36804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(&das->client_addr, conf->client_addr,
36904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  sizeof(das->client_addr));
37004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
37104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret = os_malloc(conf->shared_secret_len);
37204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->shared_secret == NULL) {
37304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
37404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
37504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
37604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(das->shared_secret, conf->shared_secret,
37704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  conf->shared_secret_len);
37804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret_len = conf->shared_secret_len;
37904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
38004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->sock = radius_das_open_socket(conf->port);
38104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock < 0) {
38204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
38304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "DAS");
38404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
38504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
38604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
38704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
38804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
38904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	{
39004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
39104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
39204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
39304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
39404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return das;
39504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
39604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
39704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
39804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid radius_das_deinit(struct radius_das_data *das)
39904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
40004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
40104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
40204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
40304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock >= 0) {
40404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eloop_unregister_read_sock(das->sock);
40504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(das->sock);
40604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
40704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
40804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das->shared_secret);
40904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das);
41004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
411