VLAN assignment based on used WPA/WPA2 passphrase/PSK

Extend wpa_psk_file to allow an optional VLAN ID to be specified with
"vlanid=<VLAN ID>" prefix on the line. If VLAN ID is specified and the
particular wpa_psk_file entry is used for a station, that station is
bound to the specified VLAN. This can be used to operate a single
WPA2-Personal BSS with multiple VLANs based on the used passphrase/PSK.
This is similar to the WPA2-Enterprise case where the RADIUS server can
assign stations to different VLANs.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2019-02-14 13:34:33 +02:00 committed by Jouni Malinen
parent dd2aedeb55
commit dbfa691df4
10 changed files with 111 additions and 15 deletions

View file

@ -1174,6 +1174,8 @@ own_ip_addr=127.0.0.1
# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
# VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can
# be used to set static client MAC address to VLAN ID mapping.
# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2
# passphrase from wpa_psk_file.
# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used
# 1 = optional; use default interface if RADIUS server does not include VLAN ID
# 2 = required; reject authentication if RADIUS server does not include VLAN ID

View file

@ -5,8 +5,11 @@
# characters or as a 256-bit hex PSK (64 hex digits).
# An optional key identifier can be added by prefixing the line with
# keyid=<keyid_string>
# An optional VLAN ID can be specified by prefixing the line with
# vlanid=<VLAN ID>.
00:00:00:00:00:00 secret passphrase
00:11:22:33:44:55 another passphrase
00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
keyid=example_id 00:11:22:33:44:77 passphrase with keyid
vlanid=3 00:00:00:00:00:00 passphrase with vlanid
00:00:00:00:00:00 another passphrase for all STAs

View file

@ -279,6 +279,8 @@ static int hostapd_config_read_wpa_psk(const char *fname,
}
while (fgets(buf, sizeof(buf), f)) {
int vlan_id = 0;
line++;
if (buf[0] == '#')
@ -306,6 +308,8 @@ static int hostapd_config_read_wpa_psk(const char *fname,
value = "";
if (!os_strcmp(name, "keyid")) {
keyid = value;
} else if (!os_strcmp(name, "vlanid")) {
vlan_id = atoi(value);
} else {
wpa_printf(MSG_ERROR,
"Unrecognized '%s=%s' on line %d in '%s'",
@ -333,6 +337,7 @@ static int hostapd_config_read_wpa_psk(const char *fname,
ret = -1;
break;
}
psk->vlan_id = vlan_id;
if (is_zero_ether_addr(addr))
psk->group = 1;
else
@ -858,11 +863,14 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk)
const u8 *prev_psk, int *vlan_id)
{
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
if (vlan_id)
*vlan_id = 0;
if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
" p2p_dev_addr=" MACSTR " prev_psk=%p",
@ -880,8 +888,11 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
(addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
(!addr && p2p_dev_addr &&
os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
0)))
0))) {
if (vlan_id)
*vlan_id = psk->vlan_id;
return psk->psk;
}
if (psk->psk == prev_psk)
next_ok = 1;

View file

@ -152,6 +152,7 @@ struct hostapd_wpa_psk {
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
int vlan_id;
};
struct hostapd_eap_user {
@ -872,7 +873,7 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
int hostapd_rate_found(int *list, int rate);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk);
const u8 *prev_psk, int *vlan_id);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
int hostapd_vlan_valid(struct hostapd_vlan *vlan,
struct vlan_description *vlan_desc);

View file

@ -114,12 +114,13 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
const u8 *addr,
const u8 *p2p_dev_addr,
const u8 *prev_psk, size_t *psk_len)
const u8 *prev_psk, size_t *psk_len,
int *vlan_id)
{
if (wpa_auth->cb->get_psk == NULL)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
prev_psk, psk_len);
prev_psk, psk_len, vlan_id);
}
@ -251,6 +252,15 @@ static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
#endif /* CONFIG_OCV */
static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
const u8 *addr, int vlan_id)
{
if (!wpa_auth->cb->update_vlan)
return -1;
return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
}
static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
@ -852,13 +862,15 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
int ok = 0;
const u8 *pmk = NULL;
size_t pmk_len;
int vlan_id = 0;
os_memset(&PTK, 0, sizeof(PTK));
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
sm->p2p_dev_addr, pmk, &pmk_len);
sm->p2p_dev_addr, pmk, &pmk_len,
&vlan_id);
if (pmk == NULL)
break;
#ifdef CONFIG_IEEE80211R_AP
@ -897,6 +909,11 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
wpa_printf(MSG_DEBUG,
"WPA: Earlier SNonce resulted in matching MIC");
sm->alt_snonce_valid = 0;
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
return -1;
os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
sm->PTK_valid = TRUE;
@ -2024,7 +2041,7 @@ SM_STATE(WPA_PTK, INITPSK)
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
&psk_len);
&psk_len, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, psk_len);
sm->pmk_len = psk_len;
@ -2712,6 +2729,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
int vlan_id;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
@ -2727,7 +2745,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
sm->p2p_dev_addr, pmk, &pmk_len);
sm->p2p_dev_addr, pmk, &pmk_len,
&vlan_id);
if (pmk == NULL)
break;
psk_found = 1;
@ -2896,6 +2915,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
#endif /* CONFIG_IEEE80211R_AP */
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@ -3335,7 +3361,7 @@ SM_STEP(WPA_PTK)
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
NULL, NULL)) {
NULL, NULL, NULL)) {
SM_ENTER(WPA_PTK, PTKSTART);
#ifdef CONFIG_SAE
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {

View file

@ -254,7 +254,8 @@ struct wpa_auth_callbacks {
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk, size_t *psk_len);
const u8 *prev_psk, size_t *psk_len,
int *vlan_id);
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len);
@ -270,6 +271,7 @@ struct wpa_auth_callbacks {
int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
size_t data_len);
int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
int (*get_sta_tx_params)(void *ctx, const u8 *addr,
int ap_max_chanwidth, int ap_seg1_idx,
int *bandwidth, int *seg1_idx);

View file

@ -620,7 +620,7 @@ static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
if (wpa_auth->cb->get_psk == NULL)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
prev_psk, NULL);
prev_psk, NULL, NULL);
}

View file

@ -246,12 +246,15 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr,
const u8 *prev_psk, size_t *psk_len)
const u8 *prev_psk, size_t *psk_len,
int *vlan_id)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
const u8 *psk;
if (vlan_id)
*vlan_id = 0;
if (psk_len)
*psk_len = PMK_LEN;
@ -287,7 +290,8 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
}
#endif /* CONFIG_OWE */
psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
vlan_id);
/*
* This is about to iterate over all psks, prev_psk gives the last
* returned psk which should not be returned again.
@ -295,6 +299,9 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
*/
if (sta && sta->psk && !psk) {
struct hostapd_sta_wpa_psk_short *pos;
if (vlan_id)
*vlan_id = 0;
psk = sta->psk->psk;
for (pos = sta->psk; pos; pos = pos->next) {
if (pos->is_passphrase) {
@ -788,6 +795,45 @@ static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
}
static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
{
#ifndef CONFIG_NO_VLAN
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
struct vlan_description vlan_desc;
sta = ap_get_sta(hapd, addr);
if (!sta)
return -1;
os_memset(&vlan_desc, 0, sizeof(vlan_desc));
vlan_desc.notempty = 1;
vlan_desc.untagged = vlan_id;
if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file",
vlan_id);
return -1;
}
if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
wpa_printf(MSG_INFO,
"Failed to assign VLAN ID %d from wpa_psk_file to "
MACSTR, vlan_id, MAC2STR(sta->addr));
return -1;
}
wpa_printf(MSG_INFO,
"Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
vlan_id, MAC2STR(sta->addr));
if ((sta->flags & WLAN_STA_ASSOC) &&
ap_sta_bind_vlan(hapd, sta) < 0)
return -1;
#endif /* CONFIG_NO_VLAN */
return 0;
}
#ifdef CONFIG_OCV
static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
int ap_max_chanwidth, int ap_seg1_idx,
@ -1229,6 +1275,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
.send_ether = hostapd_wpa_auth_send_ether,
.send_oui = hostapd_wpa_auth_send_oui,
.channel_info = hostapd_channel_info,
.update_vlan = hostapd_wpa_auth_update_vlan,
#ifdef CONFIG_OCV
.get_sta_tx_params = hostapd_get_sta_tx_params,
#endif /* CONFIG_OCV */

View file

@ -260,12 +260,14 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
static const u8 * auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr, const u8 *prev_psk,
size_t *psk_len)
size_t *psk_len, int *vlan_id)
{
struct ibss_rsn *ibss_rsn = ctx;
if (psk_len)
*psk_len = PMK_LEN;
if (vlan_id)
*vlan_id = 0;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
__func__, MAC2STR(addr), prev_psk);
if (prev_psk)

View file

@ -76,7 +76,7 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
static const u8 *auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr, const u8 *prev_psk,
size_t *psk_len)
size_t *psk_len, int *vlan_id)
{
struct mesh_rsn *mesh_rsn = ctx;
struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
@ -84,6 +84,8 @@ static const u8 *auth_get_psk(void *ctx, const u8 *addr,
if (psk_len)
*psk_len = PMK_LEN;
if (vlan_id)
*vlan_id = 0;
wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
__func__, MAC2STR(addr), prev_psk);