From d4e39c51f8bb5812aea95c38d11e8f9169fda2ee Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 21 Jun 2018 20:50:54 +0300 Subject: [PATCH] HS 2.0: Move Terms and Conditions Server URL generation from AP to AS This makes it more convenient to generate the URL in a way that interoperates between different vendors. The AP is simply copying the already constructed URL as-is from Access-Accept to WNM-Notification. This means that the HO AAA can generate the URL in a manner that works for the associated T&C Server without having to coordinate with each AP. Signed-off-by: Jouni Malinen --- hostapd/hostapd.conf | 3 ++- src/ap/authsrv.c | 1 + src/ap/hs20.c | 24 +++++++++--------- src/ap/hs20.h | 2 +- src/ap/ieee802_1x.c | 21 +++++++++++++++- src/ap/sta_info.c | 1 + src/ap/sta_info.h | 1 + src/radius/radius.h | 1 + src/radius/radius_server.c | 50 ++++++++++++++++++++++++++++++++++++++ src/radius/radius_server.h | 2 ++ 10 files changed, 92 insertions(+), 14 deletions(-) diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 64dd8c28b..8834ccfbf 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -2191,7 +2191,8 @@ own_ip_addr=127.0.0.1 # # 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. +# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this +# parameter is used on the authentication server, not the AP. # 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 diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 315c30bbe..66d255c74 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; diff --git a/src/ap/hs20.c b/src/ap/hs20.c index 9770c34d2..98d016d96 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -180,20 +180,24 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, - const u8 *addr) + const u8 *addr, const char *url) { struct wpabuf *buf; int ret; - const char *url = hapd->conf->t_c_server_url, *pos; - size_t url_len; + size_t url_len = os_strlen(url); - if (!url) - return -1; - pos = os_strstr(url, "@1@"); - if (!pos) + if (!url) { + wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); return -1; + } + + if (5 + url_len > 255) { + wpa_printf(MSG_INFO, + "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", + url); + return -1; + } - url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; buf = wpabuf_alloc(4 + 7 + url_len); if (!buf) return -1; @@ -209,9 +213,7 @@ int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, 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); + wpabuf_put_str(buf, url); ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, wpabuf_head(buf), wpabuf_len(buf)); diff --git a/src/ap/hs20.h b/src/ap/hs20.h index bf3980628..e99e26e91 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -19,7 +19,7 @@ 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); + const u8 *addr, const char *url); void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, int enabled); diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index c7d15a526..985f8b787 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1635,6 +1635,20 @@ static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd, hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0)); } + +static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, size_t len) +{ + os_free(sta->t_c_url); + sta->t_c_url = os_malloc(len + 1); + if (!sta->t_c_url) + return; + os_memcpy(sta->t_c_url, pos, len); + sta->t_c_url[len] = '\0'; + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions URL %s", sta->t_c_url); +} + #endif /* CONFIG_HS20 */ @@ -1685,6 +1699,9 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING: ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL: + ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen); + break; } } #endif /* CONFIG_HS20 */ @@ -2767,7 +2784,9 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) 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); + hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url); + os_free(sta->t_c_url); + sta->t_c_url = NULL; } } #endif /* CONFIG_HS20 */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 5e2f055d3..179cf43b6 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -332,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->identity); os_free(sta->radius_cui); os_free(sta->remediation_url); + os_free(sta->t_c_url); wpabuf_free(sta->hs20_deauth_req); os_free(sta->hs20_session_info_url); diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 0459549eb..9cac6f157 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -187,6 +187,7 @@ struct sta_info { struct wpabuf *roaming_consortium; u8 remediation_method; char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ + char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; diff --git a/src/radius/radius.h b/src/radius/radius.h index 55185dfb3..630c0f9d0 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -202,6 +202,7 @@ enum { 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, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10, }; #ifdef _MSC_VER diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 17c90878d..27e2050c1 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -350,6 +350,8 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + char *t_c_server_url; + #ifdef CONFIG_SQLITE sqlite3 *db; #endif /* CONFIG_SQLITE */ @@ -884,12 +886,56 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ + const char *url = data->t_c_server_url, *pos; + char *url2, *end2, *pos2; + size_t url_len; if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, buf, sizeof(buf))) { RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); + radius_msg_free(msg); + return NULL; } + + if (!url) { + RADIUS_DEBUG("No t_c_server_url configured"); + radius_msg_free(msg); + return NULL; + } + + pos = os_strstr(url, "@1@"); + if (!pos) { + RADIUS_DEBUG("No @1@ macro in t_c_server_url"); + radius_msg_free(msg); + return NULL; + } + + url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; + url2 = os_malloc(url_len); + if (!url2) { + RADIUS_DEBUG("Failed to allocate room for T&C Server URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + pos2 = url2; + end2 = url2 + url_len; + os_memcpy(pos2, url, pos - url); + pos2 += pos - url; + os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr)); + pos2 += ETH_ALEN * 3 - 1; + os_memcpy(pos2, pos + 3, os_strlen(pos + 3)); + if (!radius_msg_add_wfa(msg, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL, + (const u8 *) url2, url_len)) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + os_free(url2); + radius_srv_hs20_t_c_pending(sess); } #endif /* CONFIG_HS20 */ @@ -1894,6 +1940,9 @@ radius_server_init(struct radius_server_conf *conf) } data->subscr_remediation_method = conf->subscr_remediation_method; + if (conf->t_c_server_url) + data->t_c_server_url = os_strdup(conf->t_c_server_url); + #ifdef CONFIG_SQLITE if (conf->sqlite_file) { if (sqlite3_open(conf->sqlite_file, &data->db)) { @@ -2010,6 +2059,7 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE if (data->db) diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 996f00eed..298f3c105 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -233,6 +233,8 @@ struct radius_server_conf { char *subscr_remediation_url; u8 subscr_remediation_method; + + char *t_c_server_url; };