diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite index 826db349c..1ee184328 100644 --- a/hostapd/hostapd.eap_user_sqlite +++ b/hostapd/hostapd.eap_user_sqlite @@ -3,7 +3,8 @@ CREATE TABLE users( methods TEXT, password TEXT, remediation TEXT, - phase2 INTEGER + phase2 INTEGER, + t_c_timestamp INTEGER ); CREATE TABLE wildcards( diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 7f24e6f68..35ccd21e6 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -169,6 +169,7 @@ struct hostapd_eap_user { unsigned int macacl:1; int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct hostapd_radius_attr { diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index a20f49f28..be69d83b2 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -83,6 +83,7 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + user->t_c_timestamp = eap_user->t_c_timestamp; rv = 0; out: diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 082d0f531..fab307f7a 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -91,6 +91,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) set_user_methods(user, argv[i]); } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { user->remediation = strlen(argv[i]) > 0; + } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { + user->t_c_timestamp = strtol(argv[i], 0, 10); } } diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index c67fa82f6..f9ee7619f 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -38,6 +38,7 @@ struct eap_user { int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct eap_eapol_interface { diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 0a8f448e7..cc7720477 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -92,8 +92,11 @@ struct radius_session { unsigned int remediation:1; unsigned int macacl:1; + unsigned int t_c_filtering:1; struct hostapd_radius_attr *accept_attr; + + u32 t_c_timestamp; /* Last read T&C timestamp from user DB */ }; /** @@ -821,6 +824,16 @@ radius_server_encapsulate_eap(struct radius_server_data *data, RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); } } + + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { + u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ + + 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"); + } + } #endif /* CONFIG_HS20 */ if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { @@ -1003,6 +1016,51 @@ static int radius_server_reject(struct radius_server_data *data, } +static void radius_server_hs20_t_c_check(struct radius_session *sess, + struct radius_msg *msg) +{ +#ifdef CONFIG_HS20 + u8 *buf, *pos, *end, type, sublen, *timestamp = NULL; + size_t len; + + buf = NULL; + for (;;) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + break; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) { + timestamp = pos; + break; + } + } + + if (!timestamp) + return; + RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp)); + if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) { + RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering"); + sess->t_c_filtering = 1; + } +#endif /* CONFIG_HS20 */ +} + + static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, @@ -1138,6 +1196,9 @@ static int radius_server_request(struct radius_server_data *data, else if (sess->eap_if->eapSuccess) srv_log(sess, "EAP authentication succeeded"); + if (sess->eap_if->eapSuccess) + radius_server_hs20_t_c_check(sess, msg); + reply = radius_server_encapsulate_eap(data, client, sess, msg); send_reply: @@ -2059,6 +2120,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->accept_attr = user->accept_attr; sess->remediation = user->remediation; sess->macacl = user->macacl; + sess->t_c_timestamp = user->t_c_timestamp; } if (ret) {