From 8760b9848cf43185f93c1f4d19798c0d9c835922 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 24 Apr 2018 00:05:44 +0300 Subject: [PATCH] HS 2.0: Send Terms and Conditions Acceptance notification This extends hostapd Access-Accept processing to check if the RADIUS server indicated that Terms and Conditions Acceptance is required. The new hs20_t_c_server_url parameter is used to specify the server URL template that the STA is requested to visit. This commit does not enable any kind of filtering, i.e., only the part of forwarding a request from Access-Accept to the STA using WNM-Notification is covered. Signed-off-by: Jouni Malinen --- hostapd/config_file.c | 3 +++ hostapd/hostapd.conf | 7 ++++++ src/ap/ap_config.c | 1 + src/ap/ap_config.h | 1 + src/ap/hs20.c | 43 ++++++++++++++++++++++++++++++++++++ src/ap/hs20.h | 2 ++ src/ap/ieee802_1x.c | 33 ++++++++++++++++++++++++++- src/ap/sta_info.h | 1 + src/common/ieee802_11_defs.h | 1 + src/radius/radius.h | 1 + 10 files changed, 92 insertions(+), 1 deletion(-) 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