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,
4504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_EVENT_TIMESTAMP,
4604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
4704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
4813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#ifdef CONFIG_IPV6
4913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		RADIUS_ATTR_NAS_IPV6_ADDRESS,
5013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#endif /* CONFIG_IPV6 */
5104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		0
5204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	};
5304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int error = 405;
5404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 attr;
5504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	enum radius_das_res res;
5604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_attrs attrs;
5704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 *buf;
5804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	size_t len;
5904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char tmp[100];
6004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 sta_addr[ETH_ALEN];
6104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
6304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
6404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	attr = radius_msg_find_unlisted_attr(msg, allowed);
6504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (attr) {
6604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
6704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "Disconnect-Request from %s:%d", attr,
6804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
6904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 401;
7004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
7104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
7204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&attrs, 0, sizeof(attrs));
7404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
7513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
7613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
7713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		if (len != 4) {
7813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
7913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				   abuf, from_port);
8013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			error = 407;
8113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			goto fail;
8213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		}
8313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_ip_addr = buf;
8413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
8513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
8613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#ifdef CONFIG_IPV6
8713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
8813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
8913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		if (len != 16) {
9013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
9113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				   abuf, from_port);
9213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			error = 407;
9313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt			goto fail;
9413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		}
9513ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_ipv6_addr = buf;
9613ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
9713ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt#endif /* CONFIG_IPV6 */
9813ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
9913ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
10013ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt				    &buf, &len, NULL) == 0) {
10113ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_identifier = buf;
10213ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt		attrs.nas_identifier_len = len;
10313ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt	}
10413ca8d8ea51a1aa5e24c6c956473a11b0c7daed4Dmitry Shmidt
10504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
10604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
10704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (len >= sizeof(tmp))
10804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			len = sizeof(tmp) - 1;
10904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		os_memcpy(tmp, buf, len);
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		tmp[len] = '\0';
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (hwaddr_aton2(tmp, sta_addr) < 0) {
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "'%s' from %s:%d", tmp, abuf, from_port);
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			error = 407;
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.sta_addr = sta_addr;
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
12104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
12204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name = buf;
12304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.user_name_len = len;
12404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
12504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
12604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
12704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
12804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id = buf;
12904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.acct_session_id_len = len;
13004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
13104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
13304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				    &buf, &len, NULL) == 0) {
13404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui = buf;
13504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		attrs.cui_len = len;
13604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
13704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
13804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = das->disconnect(das->ctx, &attrs);
13904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (res) {
14004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_NAS_MISMATCH:
14104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
14204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   abuf, from_port);
14304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 403;
14404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
14504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SESSION_NOT_FOUND:
14604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
14704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "%s:%d", abuf, from_port);
14804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 503;
14904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
15004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_DAS_SUCCESS:
15104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		error = 0;
15204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
15304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
15404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
15504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
15604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
15704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
15804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply == NULL)
15904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
16004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
16104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (error) {
16261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
16361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       error)) {
16461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
16561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			return NULL;
16661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
16704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
16804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
16904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return reply;
17004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
17104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
17304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
17404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
17504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das = eloop_ctx;
17604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u8 buf[1500];
17704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	union {
17804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_storage ss;
17904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in sin;
18004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#ifdef CONFIG_IPV6
18104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		struct sockaddr_in6 sin6;
18204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt#endif /* CONFIG_IPV6 */
18304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} from;
18404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	char abuf[50];
18504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int from_port = 0;
18604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	socklen_t fromlen;
18704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int len;
18804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_msg *msg, *reply = NULL;
18904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_hdr *hdr;
19004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct wpabuf *rbuf;
19104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	u32 val;
19204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int res;
19304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct os_time now;
19404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
19504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	fromlen = sizeof(from);
19604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	len = recvfrom(sock, buf, sizeof(buf), 0,
19704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		       (struct sockaddr *) &from.ss, &fromlen);
19804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (len < 0) {
19904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
20004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
20104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
20204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
20304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
20404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	from_port = ntohs(from.sin.sin_port);
20504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
20604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
20704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		   len, abuf, from_port);
20804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
20904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
21004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
21104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
21204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
21304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	msg = radius_msg_parse(buf, len);
21404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (msg == NULL) {
21504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
21604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d failed", abuf, from_port);
21704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
21804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
21904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
22004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (wpa_debug_level <= MSG_MSGDUMP)
22104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_msg_dump(msg);
22204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
22304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (radius_msg_verify_das_req(msg, das->shared_secret,
22404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       das->shared_secret_len)) {
22504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
22604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
22704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
22804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
22904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
23004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_get_time(&now);
23104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
23204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				  (u8 *) &val, 4);
23304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (res == 4) {
23404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		u32 timestamp = ntohl(val);
2355460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt		if ((unsigned int) abs(now.sec - timestamp) >
2365460547a121207cf7a99eac45e05fcdd83be3161Dmitry Shmidt		    das->time_window) {
23704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
23804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp (%u; local time %u) in "
23904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "packet from %s:%d - drop",
24004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   timestamp, (unsigned int) now.sec,
24104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port);
24204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			goto fail;
24304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
24404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	} else if (das->require_event_timestamp) {
24504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
24604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "from %s:%d - drop", abuf, from_port);
24704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		goto fail;
24804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
24904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
25004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	hdr = radius_msg_get_hdr(msg);
25104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
25204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	switch (hdr->code) {
25304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_DISCONNECT_REQUEST:
25404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_das_disconnect(das, msg, abuf, from_port);
25504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
25604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	case RADIUS_CODE_COA_REQUEST:
25704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* TODO */
25804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		reply = radius_msg_new(RADIUS_CODE_COA_NAK,
25904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				       hdr->identifier);
26004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (reply == NULL)
26104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			break;
26204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
26304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		/* Unsupported Service */
26461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
26561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt					       405)) {
26661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			radius_msg_free(reply);
26761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			reply = NULL;
26861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt			break;
26961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt		}
27004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		break;
27104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	default:
27204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
27304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "packet from %s:%d",
27404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   hdr->code, abuf, from_port);
27504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
27604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
27704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (reply) {
27804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
27904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
28004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (!radius_msg_add_attr_int32(reply,
28104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       RADIUS_ATTR_EVENT_TIMESTAMP,
28204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       now.sec)) {
28304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
28404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Event-Timestamp attribute");
28504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
28604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
28704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (radius_msg_finish_das_resp(reply, das->shared_secret,
28804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt					       das->shared_secret_len, hdr) <
28904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		    0) {
29004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_DEBUG, "DAS: Failed to add "
29104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   "Message-Authenticator attribute");
29204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
29304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
29404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (wpa_debug_level <= MSG_MSGDUMP)
29504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			radius_msg_dump(reply);
29604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
29704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		rbuf = radius_msg_get_buf(reply);
29804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		res = sendto(das->sock, wpabuf_head(rbuf),
29904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     wpabuf_len(rbuf), 0,
30004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			     (struct sockaddr *) &from.ss, fromlen);
30104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		if (res < 0) {
30204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
30304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt				   abuf, from_port, strerror(errno));
30404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		}
30504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
30604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
30704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtfail:
30804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(msg);
30904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	radius_msg_free(reply);
31004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
31104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31304949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstatic int radius_das_open_socket(int port)
31404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
31504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	int s;
31604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct sockaddr_in addr;
31704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	s = socket(PF_INET, SOCK_DGRAM, 0);
31904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (s < 0) {
320cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt		wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
32104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
32204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
32304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
32404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memset(&addr, 0, sizeof(addr));
32504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_family = AF_INET;
32604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	addr.sin_port = htons(port);
32704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
328cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt		wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
32904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(s);
33004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return -1;
33104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
33204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return s;
33404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
33504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
33704949598a23f501be6eec21697465fd46a28840aDmitry Shmidtstruct radius_das_data *
33804949598a23f501be6eec21697465fd46a28840aDmitry Shmidtradius_das_init(struct radius_das_conf *conf)
33904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
34004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	struct radius_das_data *das;
34104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (conf->port == 0 || conf->shared_secret == NULL ||
34304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	    conf->client_addr == NULL)
34404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
34504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
34604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das = os_zalloc(sizeof(*das));
34704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
34804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
34904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->time_window = conf->time_window;
35104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->require_event_timestamp = conf->require_event_timestamp;
35204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->ctx = conf->ctx;
35304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->disconnect = conf->disconnect;
35404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(&das->client_addr, conf->client_addr,
35604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  sizeof(das->client_addr));
35704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
35804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret = os_malloc(conf->shared_secret_len);
35904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->shared_secret == NULL) {
36004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
36104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
36204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
36304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_memcpy(das->shared_secret, conf->shared_secret,
36404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		  conf->shared_secret_len);
36504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->shared_secret_len = conf->shared_secret_len;
36604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
36704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	das->sock = radius_das_open_socket(conf->port);
36804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock < 0) {
36904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
37004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt			   "DAS");
37104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
37204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
37304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
37404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
37504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
37604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	{
37704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		radius_das_deinit(das);
37804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return NULL;
37904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
38004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
38104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	return das;
38204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
38304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
38404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
38504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtvoid radius_das_deinit(struct radius_das_data *das)
38604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt{
38704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das == NULL)
38804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		return;
38904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
39004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	if (das->sock >= 0) {
39104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		eloop_unregister_read_sock(das->sock);
39204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt		close(das->sock);
39304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	}
39404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
39504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das->shared_secret);
39604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt	os_free(das);
39704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
398