diff --git a/hostapd/config_file.c b/hostapd/config_file.c index c611551f4..bd52f4ae7 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -3634,6 +3634,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->t_c_filename = os_strdup(pos); } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) { bss->t_c_timestamp = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { + os_free(bss->t_c_server_url); + bss->t_c_server_url = os_strdup(pos); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO } else if (os_strcmp(buf, "mbo") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index f65c00435..b5a271866 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -2169,6 +2169,13 @@ own_ip_addr=127.0.0.1 # of seconds since January 1, 1970 00:00 UTC showing the time when the file was # last modified. #hs20_t_c_timestamp=1234567 +# +# hs20_t_c_server_url contains a template for the Terms and Conditions server +# URL. This template is used to generate the URL for a STA that needs to +# acknowledge Terms and Conditions. +# Macros: +# @1@ = MAC address of the STA (colon separated hex octets) +#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 # OSU and Operator icons # ::::: diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 6de5d40e9..d9f50b1f9 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -621,6 +621,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) } os_free(conf->subscr_remediation_url); os_free(conf->t_c_filename); + os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 95eb3d4b5..7f24e6f68 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -587,6 +587,7 @@ struct hostapd_bss_config { u8 subscr_remediation_method; char *t_c_filename; u32 t_c_timestamp; + char *t_c_server_url; #endif /* CONFIG_HS20 */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ diff --git a/src/ap/hs20.c b/src/ap/hs20.c index d7909fad4..79523f91e 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -175,3 +175,46 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, return ret; } + + +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr) +{ + struct wpabuf *buf; + int ret; + const char *url = hapd->conf->t_c_server_url, *pos; + size_t url_len; + + if (!url) + return -1; + pos = os_strstr(url, "@1@"); + if (!pos) + return -1; + + url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; + buf = wpabuf_alloc(4 + 7 + url_len); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Terms and Conditions Acceptance subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + 1 + url_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); + wpabuf_put_u8(buf, url_len); + wpabuf_put_data(buf, url, pos - url); + wpabuf_printf(buf, MACSTR, MAC2STR(addr)); + wpabuf_put_str(buf, pos + 3); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h index 152439f4d..70ecc9473 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -18,5 +18,7 @@ int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, const u8 *addr, const struct wpabuf *payload); +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr); #endif /* HS20_H */ diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 9663bd757..630787de4 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1622,6 +1622,26 @@ static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, ap_sta_session_warning_timeout(hapd, sta, warning_time); } + +static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len) +{ + if (len < 4) + return; /* Malformed information */ + 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; + } +} + #endif /* CONFIG_HS20 */ @@ -1669,6 +1689,9 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, session_timeout); break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING: + ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); + break; } } #endif /* CONFIG_HS20 */ @@ -2739,6 +2762,13 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) hs20_send_wnm_notification_deauth_req(hapd, sta->addr, sta->hs20_deauth_req); } + + if (sta->hs20_t_c_filtering) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Terms and Conditions filtering", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_t_c(hapd, sta->addr); + } } #endif /* CONFIG_HS20 */ @@ -2763,7 +2793,8 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success && (sta->remediation || sta->hs20_deauth_req)) { + if (success && (sta->remediation || sta->hs20_deauth_req || + sta->hs20_t_c_filtering)) { wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " MACSTR " in 100 ms", MAC2STR(sta->addr)); eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 74910d1d2..0459549eb 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -116,6 +116,7 @@ struct sta_info { unsigned int pending_wds_enable:1; unsigned int power_capab:1; unsigned int agreed_to_steer:1; + unsigned int hs20_t_c_filtering:1; u16 auth_alg; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 45bbead39..ff2912518 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1338,6 +1338,7 @@ enum wmm_ac { /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 +#define HS20_WNM_T_C_ACCEPTANCE 2 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 diff --git a/src/radius/radius.h b/src/radius/radius.h index 3d26cf735..55185dfb3 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -201,6 +201,7 @@ enum { RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM = 6, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7, RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9, }; #ifdef _MSC_VER