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 <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2018-06-21 20:50:54 +03:00 committed by Jouni Malinen
parent e6f8042d17
commit d4e39c51f8
10 changed files with 92 additions and 14 deletions

View file

@ -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 # 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 # 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: # Macros:
# @1@ = MAC address of the STA (colon separated hex octets) # @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 #hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123

View file

@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
#ifdef CONFIG_HS20 #ifdef CONFIG_HS20
srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method; srv.subscr_remediation_method = conf->subscr_remediation_method;
srv.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp; srv.erp = conf->eap_server_erp;
srv.erp_domain = conf->erp_domain; srv.erp_domain = conf->erp_domain;

View file

@ -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, int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd,
const u8 *addr) const u8 *addr, const char *url)
{ {
struct wpabuf *buf; struct wpabuf *buf;
int ret; int ret;
const char *url = hapd->conf->t_c_server_url, *pos; size_t url_len = os_strlen(url);
size_t url_len;
if (!url) if (!url) {
return -1; wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
pos = os_strstr(url, "@1@");
if (!pos)
return -1; 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); buf = wpabuf_alloc(4 + 7 + url_len);
if (!buf) if (!buf)
return -1; 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_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE);
wpabuf_put_u8(buf, url_len); wpabuf_put_u8(buf, url_len);
wpabuf_put_data(buf, url, pos - url); wpabuf_put_str(buf, url);
wpabuf_printf(buf, MACSTR, MAC2STR(addr));
wpabuf_put_str(buf, pos + 3);
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
wpabuf_head(buf), wpabuf_len(buf)); wpabuf_head(buf), wpabuf_len(buf));

View file

@ -19,7 +19,7 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *addr,
const struct wpabuf *payload); const struct wpabuf *payload);
int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, 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, void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta,
int enabled); int enabled);

View file

@ -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)); 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 */ #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: case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING:
ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen);
break; break;
case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL:
ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen);
break;
} }
} }
#endif /* CONFIG_HS20 */ #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 " wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
MACSTR " to indicate Terms and Conditions filtering", MACSTR " to indicate Terms and Conditions filtering",
MAC2STR(sta->addr)); 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 */ #endif /* CONFIG_HS20 */

View file

@ -332,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->identity); os_free(sta->identity);
os_free(sta->radius_cui); os_free(sta->radius_cui);
os_free(sta->remediation_url); os_free(sta->remediation_url);
os_free(sta->t_c_url);
wpabuf_free(sta->hs20_deauth_req); wpabuf_free(sta->hs20_deauth_req);
os_free(sta->hs20_session_info_url); os_free(sta->hs20_session_info_url);

View file

@ -187,6 +187,7 @@ struct sta_info {
struct wpabuf *roaming_consortium; struct wpabuf *roaming_consortium;
u8 remediation_method; u8 remediation_method;
char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ 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; struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url; char *hs20_session_info_url;
int hs20_disassoc_timer; int hs20_disassoc_timer;

View file

@ -202,6 +202,7 @@ enum {
RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7,
RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8, RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8,
RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9,
RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10,
}; };
#ifdef _MSC_VER #ifdef _MSC_VER

View file

@ -350,6 +350,8 @@ struct radius_server_data {
char *subscr_remediation_url; char *subscr_remediation_url;
u8 subscr_remediation_method; u8 subscr_remediation_method;
char *t_c_server_url;
#ifdef CONFIG_SQLITE #ifdef CONFIG_SQLITE
sqlite3 *db; sqlite3 *db;
#endif /* CONFIG_SQLITE */ #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) { if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ 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( if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING,
buf, sizeof(buf))) { buf, sizeof(buf))) {
RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); 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); radius_srv_hs20_t_c_pending(sess);
} }
#endif /* CONFIG_HS20 */ #endif /* CONFIG_HS20 */
@ -1894,6 +1940,9 @@ radius_server_init(struct radius_server_conf *conf)
} }
data->subscr_remediation_method = conf->subscr_remediation_method; 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 #ifdef CONFIG_SQLITE
if (conf->sqlite_file) { if (conf->sqlite_file) {
if (sqlite3_open(conf->sqlite_file, &data->db)) { 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); os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */ #endif /* CONFIG_RADIUS_TEST */
os_free(data->subscr_remediation_url); os_free(data->subscr_remediation_url);
os_free(data->t_c_server_url);
#ifdef CONFIG_SQLITE #ifdef CONFIG_SQLITE
if (data->db) if (data->db)

View file

@ -233,6 +233,8 @@ struct radius_server_conf {
char *subscr_remediation_url; char *subscr_remediation_url;
u8 subscr_remediation_method; u8 subscr_remediation_method;
char *t_c_server_url;
}; };