Parcourir la source

RADIUS DAS: Add support for Disconnect-Request

Calling-Station-Id, Acct-Session-Id, and User-Name attributes in a
Disconnect-Request message can now be used to indicate which station is
to be disconnected.

Signed-hostap: Jouni Malinen <j@w1.fi>
Jouni Malinen il y a 12 ans
Parent
commit
8047a95809
3 fichiers modifiés avec 150 ajouts et 4 suppressions
  1. 71 0
      src/ap/hostapd.c
  2. 62 4
      src/radius/radius_das.c
  3. 17 0
      src/radius/radius_das.h

+ 71 - 0
src/ap/hostapd.c

@@ -511,6 +511,75 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
 }
 
 
+#ifndef CONFIG_NO_RADIUS
+
+static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
+				    struct radius_das_attrs *attr)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
+					      struct radius_das_attrs *attr)
+{
+	struct sta_info *sta = NULL;
+	char buf[128];
+
+	if (attr->sta_addr)
+		sta = ap_get_sta(hapd, attr->sta_addr);
+
+	if (sta == NULL && attr->acct_session_id &&
+	    attr->acct_session_id_len == 17) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			os_snprintf(buf, sizeof(buf), "%08X-%08X",
+				    sta->acct_session_id_hi,
+				    sta->acct_session_id_lo);
+			if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
+				break;
+		}
+	}
+
+	if (sta == NULL && attr->user_name) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			u8 *identity;
+			size_t identity_len;
+			identity = ieee802_1x_get_identity(sta->eapol_sm,
+							   &identity_len);
+			if (identity &&
+			    identity_len == attr->user_name_len &&
+			    os_memcmp(identity, attr->user_name, identity_len)
+			    == 0)
+				break;
+		}
+	}
+
+	return sta;
+}
+
+
+static enum radius_das_res
+hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+
+	if (hostapd_das_nas_mismatch(hapd, attr))
+		return RADIUS_DAS_NAS_MISMATCH;
+
+	sta = hostapd_das_find_sta(hapd, attr);
+	if (sta == NULL)
+		return RADIUS_DAS_SESSION_NOT_FOUND;
+
+	hostapd_drv_sta_deauth(hapd, sta->addr,
+			       WLAN_REASON_PREV_AUTH_NOT_VALID);
+	ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+	return RADIUS_DAS_SUCCESS;
+}
+
+#endif /* CONFIG_NO_RADIUS */
 
 
 /**
@@ -642,6 +711,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 		das_conf.time_window = hapd->conf->radius_das_time_window;
 		das_conf.require_event_timestamp =
 			hapd->conf->radius_das_require_event_timestamp;
+		das_conf.ctx = hapd;
+		das_conf.disconnect = hostapd_das_disconnect;
 		hapd->radius_das = radius_das_init(&das_conf);
 		if (hapd->radius_das == NULL) {
 			wpa_printf(MSG_ERROR, "RADIUS DAS initialization "

+ 62 - 4
src/radius/radius_das.c

@@ -26,6 +26,9 @@ struct radius_das_data {
 	struct hostapd_ip_addr client_addr;
 	unsigned int time_window;
 	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
 };
 
 
@@ -47,6 +50,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
 	};
 	int error = 405;
 	u8 attr;
+	enum radius_das_res res;
+	struct radius_das_attrs attrs;
+	u8 *buf;
+	size_t len;
+	char tmp[100];
+	u8 sta_addr[ETH_ALEN];
 
 	hdr = radius_msg_get_hdr(msg);
 
@@ -59,16 +68,63 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
 		goto fail;
 	}
 
-	/* TODO */
+	os_memset(&attrs, 0, sizeof(attrs));
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
+				    &buf, &len, NULL) == 0) {
+		if (len >= sizeof(tmp))
+			len = sizeof(tmp) - 1;
+		os_memcpy(tmp, buf, len);
+		tmp[len] = '\0';
+		if (hwaddr_aton2(tmp, sta_addr) < 0) {
+			wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
+				   "'%s' from %s:%d", tmp, abuf, from_port);
+			error = 407;
+			goto fail;
+		}
+		attrs.sta_addr = sta_addr;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
+				    &buf, &len, NULL) == 0) {
+		attrs.user_name = buf;
+		attrs.user_name_len = len;
+	}
+
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_session_id = buf;
+		attrs.acct_session_id_len = len;
+	}
 
-	goto fail;
+	res = das->disconnect(das->ctx, &attrs);
+	switch (res) {
+	case RADIUS_DAS_NAS_MISMATCH:
+		wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
+			   abuf, from_port);
+		error = 403;
+		break;
+	case RADIUS_DAS_SESSION_NOT_FOUND:
+		wpa_printf(MSG_INFO, "DAS: Session not found for request from "
+			   "%s:%d", abuf, from_port);
+		error = 503;
+		break;
+	case RADIUS_DAS_SUCCESS:
+		error = 0;
+		break;
+	}
 
 fail:
-	reply = radius_msg_new(RADIUS_CODE_DISCONNECT_NAK, hdr->identifier);
+	reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
+			       RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
 	if (reply == NULL)
 		return NULL;
 
-	radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error);
+	if (error) {
+		radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
+					  error);
+	}
+
 	return reply;
 }
 
@@ -240,6 +296,8 @@ radius_das_init(struct radius_das_conf *conf)
 
 	das->time_window = conf->time_window;
 	das->require_event_timestamp = conf->require_event_timestamp;
+	das->ctx = conf->ctx;
+	das->disconnect = conf->disconnect;
 
 	os_memcpy(&das->client_addr, conf->client_addr,
 		  sizeof(das->client_addr));

+ 17 - 0
src/radius/radius_das.h

@@ -11,6 +11,20 @@
 
 struct radius_das_data;
 
+enum radius_das_res {
+	RADIUS_DAS_SUCCESS,
+	RADIUS_DAS_NAS_MISMATCH,
+	RADIUS_DAS_SESSION_NOT_FOUND
+};
+
+struct radius_das_attrs {
+	const u8 *sta_addr;
+	const u8 *user_name;
+	size_t user_name_len;
+	const u8 *acct_session_id;
+	size_t acct_session_id_len;
+};
+
 struct radius_das_conf {
 	int port;
 	const u8 *shared_secret;
@@ -18,6 +32,9 @@ struct radius_das_conf {
 	const struct hostapd_ip_addr *client_addr;
 	unsigned int time_window;
 	int require_event_timestamp;
+	void *ctx;
+	enum radius_das_res (*disconnect)(void *ctx,
+					  struct radius_das_attrs *attr);
 };
 
 struct radius_das_data *