RADIUS DAS: Check for single session match for Disconnect-Request
Previously, the first matching STA was picked. That is not really the design in RFC 5176, so extend this matching code to go through all specified session identification attributes and verify that all of them match. In addition, check for a possible case of multiple sessions matching. If such a case is detected, return with Disconnect-NAK and Error-Code 508 (multiple session selection not supported). Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
201c9ad77f
commit
861beb7269
4 changed files with 132 additions and 20 deletions
142
src/ap/hostapd.c
142
src/ap/hostapd.c
|
@ -614,51 +614,145 @@ static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
|
|||
|
||||
|
||||
static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
|
||||
struct radius_das_attrs *attr)
|
||||
struct radius_das_attrs *attr,
|
||||
int *multi)
|
||||
{
|
||||
struct sta_info *sta = NULL;
|
||||
struct sta_info *selected, *sta;
|
||||
char buf[128];
|
||||
int num_attr = 0;
|
||||
int count;
|
||||
|
||||
if (attr->sta_addr)
|
||||
*multi = 0;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next)
|
||||
sta->radius_das_match = 1;
|
||||
|
||||
if (attr->sta_addr) {
|
||||
num_attr++;
|
||||
sta = ap_get_sta(hapd, attr->sta_addr);
|
||||
if (!sta) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: No Calling-Station-Id match");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sta == NULL && attr->acct_session_id &&
|
||||
attr->acct_session_id_len == 17) {
|
||||
selected = sta;
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (sta != selected)
|
||||
sta->radius_das_match = 0;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
|
||||
}
|
||||
|
||||
if (attr->acct_session_id) {
|
||||
num_attr++;
|
||||
if (attr->acct_session_id_len != 17) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: Acct-Session-Id cannot match");
|
||||
return NULL;
|
||||
}
|
||||
count = 0;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (!sta->radius_das_match)
|
||||
continue;
|
||||
os_snprintf(buf, sizeof(buf), "%08X-%08X",
|
||||
sta->acct_session_id_hi,
|
||||
sta->acct_session_id_lo);
|
||||
if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
|
||||
break;
|
||||
if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
|
||||
sta->radius_das_match = 0;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: No matches remaining after Acct-Session-Id check");
|
||||
return NULL;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
|
||||
}
|
||||
|
||||
if (sta == NULL && attr->cui) {
|
||||
if (attr->cui) {
|
||||
num_attr++;
|
||||
count = 0;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
struct wpabuf *cui;
|
||||
|
||||
if (!sta->radius_das_match)
|
||||
continue;
|
||||
cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
|
||||
if (cui && wpabuf_len(cui) == attr->cui_len &&
|
||||
if (!cui || wpabuf_len(cui) != attr->cui_len ||
|
||||
os_memcmp(wpabuf_head(cui), attr->cui,
|
||||
attr->cui_len) == 0)
|
||||
break;
|
||||
attr->cui_len) != 0)
|
||||
sta->radius_das_match = 0;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
|
||||
return NULL;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: Chargeable-User-Identity match");
|
||||
}
|
||||
|
||||
if (sta == NULL && attr->user_name) {
|
||||
if (attr->user_name) {
|
||||
num_attr++;
|
||||
count = 0;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
u8 *identity;
|
||||
size_t identity_len;
|
||||
|
||||
if (!sta->radius_das_match)
|
||||
continue;
|
||||
identity = ieee802_1x_get_identity(sta->eapol_sm,
|
||||
&identity_len);
|
||||
if (identity &&
|
||||
identity_len == attr->user_name_len &&
|
||||
if (!identity ||
|
||||
identity_len != attr->user_name_len ||
|
||||
os_memcmp(identity, attr->user_name, identity_len)
|
||||
== 0)
|
||||
break;
|
||||
!= 0)
|
||||
sta->radius_das_match = 0;
|
||||
else
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: No matches remaining after User-Name check");
|
||||
return NULL;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: User-Name match");
|
||||
}
|
||||
|
||||
if (num_attr == 0) {
|
||||
/*
|
||||
* In theory, we could match all current associations, but it
|
||||
* seems safer to just reject requests that do not include any
|
||||
* session identification attributes.
|
||||
*/
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: No session identification attributes included");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
selected = NULL;
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (sta->radius_das_match) {
|
||||
if (selected) {
|
||||
*multi = 1;
|
||||
return NULL;
|
||||
}
|
||||
selected = sta;
|
||||
}
|
||||
}
|
||||
|
||||
return sta;
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
|
@ -667,14 +761,24 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
|
|||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
struct sta_info *sta;
|
||||
int multi;
|
||||
|
||||
if (hostapd_das_nas_mismatch(hapd, attr))
|
||||
return RADIUS_DAS_NAS_MISMATCH;
|
||||
|
||||
sta = hostapd_das_find_sta(hapd, attr);
|
||||
if (sta == NULL)
|
||||
sta = hostapd_das_find_sta(hapd, attr, &multi);
|
||||
if (sta == NULL) {
|
||||
if (multi) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RADIUS DAS: Multiple sessions match - not supported");
|
||||
return RADIUS_DAS_MULTI_SESSION_MATCH;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
|
||||
return RADIUS_DAS_SESSION_NOT_FOUND;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
|
||||
" - disconnecting", MAC2STR(sta->addr));
|
||||
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
|
||||
|
||||
hostapd_drv_sta_deauth(hapd, sta->addr,
|
||||
|
|
|
@ -85,6 +85,7 @@ struct sta_info {
|
|||
unsigned int remediation:1;
|
||||
unsigned int hs20_deauth_requested:1;
|
||||
unsigned int session_timeout_set:1;
|
||||
unsigned int radius_das_match:1;
|
||||
|
||||
u16 auth_alg;
|
||||
|
||||
|
|
|
@ -147,6 +147,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
|
|||
"%s:%d", abuf, from_port);
|
||||
error = 503;
|
||||
break;
|
||||
case RADIUS_DAS_MULTI_SESSION_MATCH:
|
||||
wpa_printf(MSG_INFO,
|
||||
"DAS: Multiple sessions match for request from %s:%d",
|
||||
abuf, from_port);
|
||||
error = 508;
|
||||
break;
|
||||
case RADIUS_DAS_SUCCESS:
|
||||
error = 0;
|
||||
break;
|
||||
|
|
|
@ -14,7 +14,8 @@ struct radius_das_data;
|
|||
enum radius_das_res {
|
||||
RADIUS_DAS_SUCCESS,
|
||||
RADIUS_DAS_NAS_MISMATCH,
|
||||
RADIUS_DAS_SESSION_NOT_FOUND
|
||||
RADIUS_DAS_SESSION_NOT_FOUND,
|
||||
RADIUS_DAS_MULTI_SESSION_MATCH,
|
||||
};
|
||||
|
||||
struct radius_das_attrs {
|
||||
|
|
Loading…
Reference in a new issue