]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
RADIUS DAS: Add support for Disconnect-Request
authorJouni Malinen <j@w1.fi>
Sun, 17 Jun 2012 16:30:01 +0000 (19:30 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 17 Jun 2012 16:30:01 +0000 (19:30 +0300)
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>

src/ap/hostapd.c
src/radius/radius_das.c
src/radius/radius_das.h

index 50239b0b6032b269309613a6f9ba23bac48f5f8b..3f007ff240d4ae0b95024431954536db673aae83 100644 (file)
@@ -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 "
index d3c144a825c76029d8b7d2aee52418ad282a0aab..61e3518b6f60c6ebb01a50cea094b009cb6ca243 100644 (file)
@@ -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));
index c3d501d8579d6c0c8ca9dec384ab4c99ac130462..d0719eddc5e207b56c9f35d683c2b5e63a7baafb 100644 (file)
 
 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 *