]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
HS 2.0: CoA-Request processing for Terms and Conditions filtering
authorJouni Malinen <jouni@codeaurora.org>
Tue, 24 Apr 2018 22:23:30 +0000 (01:23 +0300)
committerJouni Malinen <j@w1.fi>
Wed, 25 Apr 2018 09:57:46 +0000 (12:57 +0300)
Extend RADIUS DAS to support CoA-Request packets for the case where the
HS 2.0 Terms And Conditions filtering VSA is used to remove filtering.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
src/ap/hostapd.c
src/ap/hs20.c
src/ap/hs20.h
src/ap/ieee802_1x.c
src/common/wpa_ctrl.h
src/radius/radius_das.c
src/radius/radius_das.h

index d2eb0441c417baf8c11d442f47b1665161cf1272..49cceb88b6ba9dd40b5b695216af058f230f48cc 100644 (file)
@@ -49,6 +49,7 @@
 #include "rrm.h"
 #include "fils_hlp.h"
 #include "acs.h"
+#include "hs20.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -900,6 +901,48 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
        return RADIUS_DAS_SUCCESS;
 }
 
+
+#ifdef CONFIG_HS20
+static enum radius_das_res
+hostapd_das_coa(void *ctx, struct radius_das_attrs *attr)
+{
+       struct hostapd_data *hapd = ctx;
+       struct sta_info *sta;
+       int multi;
+
+       if (hostapd_das_nas_mismatch(hapd, attr))
+               return RADIUS_DAS_NAS_MISMATCH;
+
+       sta = hostapd_das_find_sta(hapd, attr, &multi);
+       if (!sta) {
+               if (multi) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: Multiple sessions match - not supported");
+                       return RADIUS_DAS_MULTI_SESSION_MATCH;
+               }
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
+               return RADIUS_DAS_SESSION_NOT_FOUND;
+       }
+
+       wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+                  " - CoA", MAC2STR(sta->addr));
+
+       if (attr->hs20_t_c_filtering) {
+               if (attr->hs20_t_c_filtering[0] & BIT(0)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request");
+                       return RADIUS_DAS_COA_FAILED;
+               }
+
+               hs20_t_c_filtering(hapd, sta, 0);
+       }
+
+       return RADIUS_DAS_SUCCESS;
+}
+#else /* CONFIG_HS20 */
+#define hostapd_das_coa NULL
+#endif /* CONFIG_HS20 */
+
 #endif /* CONFIG_NO_RADIUS */
 
 
@@ -1074,6 +1117,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                        conf->radius_das_require_message_authenticator;
                das_conf.ctx = hapd;
                das_conf.disconnect = hostapd_das_disconnect;
+               das_conf.coa = hostapd_das_coa;
                hapd->radius_das = radius_das_init(&das_conf);
                if (hapd->radius_das == NULL) {
                        wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
index 79523f91e9603a4c55356c8ba835f1407cd70eec..9770c34d2509cf59f7210892adc17730c94c569d 100644 (file)
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "sta_info.h"
 #include "hs20.h"
 
 
@@ -218,3 +220,26 @@ int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
 
        return ret;
 }
+
+
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+                       int enabled)
+{
+       if (enabled) {
+               wpa_printf(MSG_DEBUG,
+                          "HS 2.0: Terms and Conditions filtering required for "
+                          MACSTR, MAC2STR(sta->addr));
+               sta->hs20_t_c_filtering = 1;
+               /* TODO: Enable firewall filtering for the STA */
+               wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR,
+                       MAC2STR(sta->addr));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "HS 2.0: Terms and Conditions filtering not required for "
+                          MACSTR, MAC2STR(sta->addr));
+               sta->hs20_t_c_filtering = 0;
+               /* TODO: Disable firewall filtering for the STA */
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr));
+       }
+}
index 70ecc94730e683f4694127e8fc2624e6490387bf..bf3980628fce9929dbbac00451399b3b5141149d 100644 (file)
@@ -20,5 +20,7 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
                                          const struct wpabuf *payload);
 int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
                                   const u8 *addr);
+void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
+                       int enabled);
 
 #endif /* HS20_H */
index 630787de4b2c5bb4aac3059574f80f5f0734d9fa..51043ba6a63705b3c9e1adb0de3e6890a8013896 100644 (file)
@@ -1632,14 +1632,7 @@ static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd,
        wpa_printf(MSG_DEBUG,
                   "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x",
                   pos[0], pos[1], pos[2], pos[3]);
-       if (pos[0] & BIT(0)) {
-               wpa_printf(MSG_DEBUG,
-                          "HS 2.0: Terms and Conditions filtering required");
-               sta->hs20_t_c_filtering = 1;
-               /* TODO: Enable firewall filtering for the STA */
-       } else {
-               sta->hs20_t_c_filtering = 0;
-       }
+       hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0));
 }
 
 #endif /* CONFIG_HS20 */
index a9da1cd80bd21861fb7460ae7d77557b185f4ef5..c7a7c1e275c9a3ffe83af86c811f2c0688037420 100644 (file)
@@ -298,6 +298,9 @@ extern "C" {
 #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
 #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
 
+#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
+#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
+
 #define AP_EVENT_ENABLED "AP-ENABLED "
 #define AP_EVENT_DISABLED "AP-DISABLED "
 
index ed24c192de56638593806078431257f35a3ecf14..0a0cf8ed4b8b20ee8a29267eb82fa99ee03516ec 100644 (file)
@@ -27,6 +27,7 @@ struct radius_das_data {
        void *ctx;
        enum radius_das_res (*disconnect)(void *ctx,
                                          struct radius_das_attrs *attr);
+       enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
 };
 
 
@@ -161,6 +162,10 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
                           abuf, from_port);
                error = 508;
                break;
+       case RADIUS_DAS_COA_FAILED:
+               /* not used with Disconnect-Request */
+               error = 405;
+               break;
        case RADIUS_DAS_SUCCESS:
                error = 0;
                break;
@@ -184,6 +189,195 @@ fail:
 }
 
 
+static struct radius_msg * radius_das_coa(struct radius_das_data *das,
+                                         struct radius_msg *msg,
+                                         const char *abuf, int from_port)
+{
+       struct radius_hdr *hdr;
+       struct radius_msg *reply;
+       u8 allowed[] = {
+               RADIUS_ATTR_USER_NAME,
+               RADIUS_ATTR_NAS_IP_ADDRESS,
+               RADIUS_ATTR_CALLING_STATION_ID,
+               RADIUS_ATTR_NAS_IDENTIFIER,
+               RADIUS_ATTR_ACCT_SESSION_ID,
+               RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+               RADIUS_ATTR_EVENT_TIMESTAMP,
+               RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+               RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_HS20
+               RADIUS_ATTR_VENDOR_SPECIFIC,
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_IPV6
+               RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
+               0
+       };
+       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);
+
+       if (!das->coa) {
+               wpa_printf(MSG_INFO, "DAS: CoA not supported");
+               goto fail;
+       }
+
+       attr = radius_msg_find_unlisted_attr(msg, allowed);
+       if (attr) {
+               wpa_printf(MSG_INFO,
+                          "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+                          attr, abuf, from_port);
+               error = 401;
+               goto fail;
+       }
+
+       os_memset(&attrs, 0, sizeof(attrs));
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+                                   &buf, &len, NULL) == 0) {
+               if (len != 4) {
+                       wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+                                  abuf, from_port);
+                       error = 407;
+                       goto fail;
+               }
+               attrs.nas_ip_addr = buf;
+       }
+
+#ifdef CONFIG_IPV6
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+                                   &buf, &len, NULL) == 0) {
+               if (len != 16) {
+                       wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+                                  abuf, from_port);
+                       error = 407;
+                       goto fail;
+               }
+               attrs.nas_ipv6_addr = buf;
+       }
+#endif /* CONFIG_IPV6 */
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+                                   &buf, &len, NULL) == 0) {
+               attrs.nas_identifier = buf;
+               attrs.nas_identifier_len = len;
+       }
+
+       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;
+       }
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+                                   &buf, &len, NULL) == 0) {
+               attrs.acct_multi_session_id = buf;
+               attrs.acct_multi_session_id_len = len;
+       }
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+                                   &buf, &len, NULL) == 0) {
+               attrs.cui = buf;
+               attrs.cui_len = len;
+       }
+
+#ifdef CONFIG_HS20
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                   &buf, &len, NULL) == 0) {
+               if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA ||
+                   buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING ||
+                   buf[5] < 6) {
+                       wpa_printf(MSG_INFO,
+                                  "DAS: Unsupported attribute %u in CoA-Request from %s:%d",
+                                  attr, abuf, from_port);
+                       error = 401;
+                       goto fail;
+               }
+               attrs.hs20_t_c_filtering = &buf[6];
+       }
+
+       if (!attrs.hs20_t_c_filtering) {
+                       wpa_printf(MSG_INFO,
+                                  "DAS: No supported authorization change attribute in CoA-Request from %s:%d",
+                                  abuf, from_port);
+                       error = 402;
+                       goto fail;
+       }
+#endif /* CONFIG_HS20 */
+
+       res = das->coa(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_MULTI_SESSION_MATCH:
+               wpa_printf(MSG_INFO,
+                          "DAS: Multiple sessions match for request from %s:%d",
+                          abuf, from_port);
+               error = 508;
+               break;
+       case RADIUS_DAS_COA_FAILED:
+               wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d",
+                          abuf, from_port);
+               error = 407;
+               break;
+       case RADIUS_DAS_SUCCESS:
+               error = 0;
+               break;
+       }
+
+fail:
+       reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK :
+                              RADIUS_CODE_COA_ACK, hdr->identifier);
+       if (!reply)
+               return NULL;
+
+       if (error &&
+           !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
+               radius_msg_free(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+
 static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
 {
        struct radius_das_data *das = eloop_ctx;
@@ -270,19 +464,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
                reply = radius_das_disconnect(das, msg, abuf, from_port);
                break;
        case RADIUS_CODE_COA_REQUEST:
-               /* TODO */
-               reply = radius_msg_new(RADIUS_CODE_COA_NAK,
-                                      hdr->identifier);
-               if (reply == NULL)
-                       break;
-
-               /* Unsupported Service */
-               if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
-                                              405)) {
-                       radius_msg_free(reply);
-                       reply = NULL;
-                       break;
-               }
+               reply = radius_das_coa(das, msg, abuf, from_port);
                break;
        default:
                wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
@@ -369,6 +551,7 @@ radius_das_init(struct radius_das_conf *conf)
                conf->require_message_authenticator;
        das->ctx = conf->ctx;
        das->disconnect = conf->disconnect;
+       das->coa = conf->coa;
 
        os_memcpy(&das->client_addr, conf->client_addr,
                  sizeof(das->client_addr));
index 9863fdc1eaca017989c06ac050194da12457ffab..233d662f631b09fcb46800d3bf3d2433f2e32d13 100644 (file)
@@ -16,6 +16,7 @@ enum radius_das_res {
        RADIUS_DAS_NAS_MISMATCH,
        RADIUS_DAS_SESSION_NOT_FOUND,
        RADIUS_DAS_MULTI_SESSION_MATCH,
+       RADIUS_DAS_COA_FAILED,
 };
 
 struct radius_das_attrs {
@@ -35,6 +36,9 @@ struct radius_das_attrs {
        size_t acct_multi_session_id_len;
        const u8 *cui;
        size_t cui_len;
+
+       /* Authorization changes */
+       const u8 *hs20_t_c_filtering;
 };
 
 struct radius_das_conf {
@@ -48,6 +52,7 @@ struct radius_das_conf {
        void *ctx;
        enum radius_das_res (*disconnect)(void *ctx,
                                          struct radius_das_attrs *attr);
+       enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr);
 };
 
 struct radius_das_data *