[PATCHv2 1/2] SAE: add RADIUS Tunnel-Password support

michael-dev at fami-braun.de michael-dev at fami-braun.de
Fri Apr 16 12:03:22 BST 2021


From: Michael Braun <michael-dev at fami-braun.de>

If no matching sae_password is configured in hostapd, use the RADIUS
supplied Tunnel-Password as SAE password. Though, that is still subject to
the length constraint of WPA passphrase (8..63 chars) to avoid ambiguation.

The SAE password identifier can be optionally supplied using
Tunnel-Client-Auth-ID.

Signed-off-by: Michael Braun <michael-dev at fami-braun.de>
---
 hostapd/hostapd.conf     |   6 ++
 src/ap/ap_config.c       |   8 ++
 src/ap/ap_config.h       |   9 +-
 src/ap/ieee802_11.c      |  61 +++++++++++++
 src/ap/ieee802_11_auth.c | 145 ++++++++++++++++++++++--------
 src/ap/wpa_auth_glue.c   |  28 ++++--
 src/radius/radius.c      | 188 +++++++++++++++++++++++++--------------
 src/radius/radius.h      |   7 +-
 8 files changed, 335 insertions(+), 117 deletions(-)

diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 7932cb862..7ccad8912 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1867,6 +1867,12 @@ own_ip_addr=127.0.0.1
 # WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK
 # uses the wpa_passphrase value.
 #
+# If no sae_password matches, RADIUS supplied Tunnel-Password are used with
+# higher priority than wpa_passphrase if wpa_psk_radius > 0 except. Thought,
+# the WPA-PSK constraints (8..63 chars) apply here was well.
+# The corresponding SAE password identifier can optionally be supplied using
+# Tunnel-Client-Auth-ID.
+#
 # Each sae_password entry is added to a list of available passwords. This
 # corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value
 # starts with the password (dot11RSNAConfigPasswordCredential). That value can
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 7b6249bbe..5357f08d2 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1588,6 +1588,11 @@ int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
 	if (conf->ssid.wpa_passphrase)
 		without_id = 1;
 
+	if (conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+		with_id = 1;
+		without_id = 1;
+	}
+
 	for (pw = conf->sae_passwords; pw; pw = pw->next) {
 		if (pw->identifier)
 			with_id = 1;
@@ -1627,6 +1632,9 @@ bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
 	if (conf->ssid.wpa_passphrase)
 		return false;
 
+	if (conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
+		return false;
+
 	for (pw = conf->sae_passwords; pw; pw = pw->next) {
 		if (!pw->pk)
 			return false;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 95bd79873..4778b3937 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -148,9 +148,12 @@ struct hostapd_vlan {
 #define MAX_PASSPHRASE_LEN 63
 struct hostapd_sta_wpa_psk_short {
 	struct hostapd_sta_wpa_psk_short *next;
-	unsigned int is_passphrase:1;
-	u8 psk[PMK_LEN];
-	char passphrase[MAX_PASSPHRASE_LEN + 1];
+	u8 *psk;
+	char *passphrase;
+#ifdef CONFIG_SAE
+	char *sae_identifier;
+	struct sae_pt *sae_pt;
+#endif /* CONFIG_SAE */
 	int ref; /* (number of references held) - 1 */
 };
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index b404e84af..90f8cef67 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -487,6 +487,63 @@ static void sae_set_state(struct sta_info *sta, enum sae_state state,
 }
 
 
+static void sae_derive_pt_from_psk(struct hostapd_data *hapd,
+				   struct hostapd_sta_wpa_psk_short *psk)
+{
+	struct hostapd_ssid *ssid;
+
+	if (!psk->passphrase)
+		return;
+	if (psk->sae_pt)
+		return;
+
+	ssid = &hapd->conf->ssid;
+
+	psk->sae_pt = sae_derive_pt(hapd->conf->sae_groups, ssid->ssid,
+				    ssid->ssid_len, (u8 *) psk->passphrase,
+				    os_strlen(psk->passphrase),
+				    psk->sae_identifier);
+
+}
+
+
+static const char * sae_get_password_from_psk(struct hostapd_data *hapd,
+					      struct sta_info *sta,
+					      const char *rx_id,
+					      struct sae_pt **s_pt)
+{
+	struct hostapd_sta_wpa_psk_short *pos;
+	wpa_printf(MSG_DEBUG, "SAE: get password for peer " MACSTR
+		   " with rx_id=%s", MAC2STR(sta->addr),
+		   (rx_id ? rx_id : "<NULL>"));
+
+	for (pos = sta->psk; pos; pos = pos->next) {
+		wpa_printf(MSG_DEBUG, "SAE: rx_id=%s pos->id=%s", (rx_id ? rx_id : "NULL"), (pos->sae_identifier ? pos->sae_identifier : "NULL"));
+		if ((rx_id && !pos->sae_identifier) ||
+		    (!rx_id && pos->sae_identifier))
+			continue;
+		if (rx_id && pos->sae_identifier &&
+		    os_strcmp(rx_id, pos->sae_identifier) != 0)
+			continue;
+		wpa_printf(MSG_DEBUG, "SAE: found with id matching");
+
+		sae_derive_pt_from_psk(hapd, pos);
+
+		wpa_printf(MSG_DEBUG, "SAE: found with id matching got sae_pt=%p", pos->sae_pt);
+
+		if (!pos->sae_pt)
+			continue;
+
+		if (s_pt)
+			*s_pt = pos->sae_pt;
+
+		return pos->passphrase;
+	}
+
+	return NULL;
+}
+
+
 static const char * sae_get_password(struct hostapd_data *hapd,
 				     struct sta_info *sta,
 				     const char *rx_id,
@@ -514,6 +571,10 @@ static const char * sae_get_password(struct hostapd_data *hapd,
 			pk = pw->pk;
 		break;
 	}
+
+	if (!password)
+		password = sae_get_password_from_psk(hapd, sta, rx_id, &pt);
+
 	if (!password) {
 		password = hapd->conf->ssid.wpa_passphrase;
 		pt = hapd->conf->ssid.pt;
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 783ee6dea..7023ae103 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -23,6 +23,7 @@
 #include "ieee802_11.h"
 #include "ieee802_1x.h"
 #include "ieee802_11_auth.h"
+#include "common/sae.h"
 
 #define RADIUS_ACL_TIMEOUT 30
 
@@ -368,72 +369,133 @@ void hostapd_acl_expire(struct hostapd_data *hapd)
 	hostapd_acl_expire_queries(hapd, &now);
 }
 
+#ifdef CONFIG_SAE
+static int get_tunnel_sae_id(struct radius_msg *msg, u8 tag, u8** buf,
+			      size_t *len, size_t idx, size_t *next_idx)
+{
+	const u8 type = RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID;
 
-static void decode_tunnel_passwords(struct hostapd_data *hapd,
-				    const u8 *shared_secret,
-				    size_t shared_secret_len,
-				    struct radius_msg *msg,
-				    struct radius_msg *req,
-				    struct hostapd_cached_radius_acl *cache)
+	return radius_msg_get_attr_tag_ptr(msg, type, tag, buf, len, idx,
+					   next_idx);
+}
+#endif /* CONFIG_SAE */
+
+
+static void decode_tunnel_password(struct hostapd_data *hapd,
+				   const u8 *shared_secret,
+				   size_t shared_secret_len,
+				   struct radius_msg *msg,
+				   struct radius_msg *req,
+				   struct hostapd_cached_radius_acl *cache,
+				   size_t psk_idx, size_t *psk_next)
 {
 	int passphraselen;
 	char *passphrase;
-	size_t i;
 	struct hostapd_sta_wpa_psk_short *psk;
-
+	int has_sae_id = 0;
+#ifdef CONFIG_SAE
+	u8 tag;
+	u8 *sae_id;
+	size_t sae_id_len;
+	size_t sae_idx = 0, sae_idx_next;
+#endif /* CONFIG_SAE */
+
+	passphrase = radius_msg_get_tunnel_password(
+		msg, &passphraselen, shared_secret, shared_secret_len,
+		req, psk_idx, &tag, psk_next);
 	/*
-	 * Decode all tunnel passwords as PSK and save them into a linked list.
+	 * Passphrase is NULL iff there is no i-th Tunnel-Password
+	 * attribute in msg.
 	 */
-	for (i = 0; ; i++) {
-		passphrase = radius_msg_get_tunnel_password(
-			msg, &passphraselen, shared_secret, shared_secret_len,
-			req, i);
-		/*
-		 * Passphrase is NULL iff there is no i-th Tunnel-Password
-		 * attribute in msg.
-		 */
-		if (passphrase == NULL)
-			break;
+	if (passphrase == NULL) {
+		*psk_next = 0;
+		return;
+	}
 
-		/*
-		 * Passphase should be 8..63 chars (to be hashed with SSID)
-		 * or 64 chars hex string (no separate hashing with SSID).
-		 */
+	/*
+	 * Passphase should be 8..63 chars (to be hashed with SSID)
+	 * or 64 chars hex string (no separate hashing with SSID).
+	 */
 
-		if (passphraselen < MIN_PASSPHRASE_LEN ||
-		    passphraselen > MAX_PASSPHRASE_LEN + 1)
-			goto free_pass;
+	if (passphraselen < MIN_PASSPHRASE_LEN ||
+	    passphraselen > MAX_PASSPHRASE_LEN + 1)
+		goto free_pass;
 
+	do {
 		/*
 		 * passphrase does not contain the NULL termination.
 		 * Add it here as pbkdf2_sha1() requires it.
 		 */
 		psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
-		if (psk) {
-			if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
-			    (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
+		if (!psk)
+			goto skip;
+		if (passphraselen == MAX_PASSPHRASE_LEN + 1) {
+			psk->psk = os_zalloc(PMK_LEN);
+			if (!psk->psk)
+				goto skip;
+			if (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0) {
 				hostapd_logger(hapd, cache->addr,
 					       HOSTAPD_MODULE_RADIUS,
 					       HOSTAPD_LEVEL_WARNING,
 					       "invalid hex string (%d chars) in Tunnel-Password",
 					       passphraselen);
 				goto skip;
-			} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
-				os_memcpy(psk->passphrase, passphrase,
-					  passphraselen);
-				psk->is_passphrase = 1;
 			}
-			psk->next = cache->info.psk;
-			cache->info.psk = psk;
-			psk = NULL;
+		} else {
+			psk->passphrase = os_zalloc(passphraselen + 1);
+			os_memcpy(psk->passphrase, passphrase, passphraselen);
+#ifdef CONFIG_SAE
+			/*
+			 * Lookup Tunnel-Client-Auth-ID by tag
+			 */
+			has_sae_id = !get_tunnel_sae_id(msg, tag, &sae_id,
+							&sae_id_len, sae_idx,
+							&sae_idx_next);
+			if (has_sae_id) {
+				psk->sae_identifier = os_zalloc(sae_id_len+1);
+				if (!psk->sae_identifier)
+					goto skip;
+				os_memcpy(psk->sae_identifier, sae_id, sae_id_len);
+				sae_idx = sae_idx_next;
+			} else if (sae_idx > 0)
+				goto skip;
+#endif /* CONFIG_SAE */
 		}
+		psk->next = cache->info.psk;
+		cache->info.psk = psk;
+		psk = NULL;
+	} while (has_sae_id);
+
 skip:
+	if (psk) {
+		os_free(psk->psk);
+		os_free(psk->passphrase);
 		os_free(psk);
-free_pass:
-		os_free(passphrase);
 	}
+free_pass:
+	os_free(passphrase);
 }
 
+static void decode_tunnel_passwords(struct hostapd_data *hapd,
+				    const u8 *shared_secret,
+				    size_t shared_secret_len,
+				    struct radius_msg *msg,
+				    struct radius_msg *req,
+				    struct hostapd_cached_radius_acl *cache)
+{
+	size_t psk_idx = 0, psk_next;
+
+	/*
+	 * Decode all tunnel passwords as PSK and save them into a linked list.
+	 */
+	while (1) {
+		decode_tunnel_password(hapd, shared_secret, shared_secret_len,
+				       msg, req, cache, psk_idx, &psk_next);
+		if (psk_next == 0)
+			break;
+		psk_idx = psk_next;
+	}
+}
 
 /**
  * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
@@ -646,6 +708,13 @@ void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
 	while (psk) {
 		struct hostapd_sta_wpa_psk_short *prev = psk;
 		psk = psk->next;
+#ifdef CONFIG_SAE
+		if (prev->sae_pt)
+			sae_deinit_pt(prev->sae_pt);
+		os_free(prev->sae_identifier);
+		os_free(prev->psk);
+		os_free(prev->passphrase);
+#endif /* CONFIG_SAE */
 		os_free(prev);
 	}
 }
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index c3b2e81e2..c88552db0 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -381,23 +381,35 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
 	 * logic list (all hostapd_get_psk; all sta->psk)
 	 */
 	if (sta && sta->psk && !psk) {
-		struct hostapd_sta_wpa_psk_short *pos;
+		struct hostapd_sta_wpa_psk_short *pos, *prev = NULL;
 
 		if (vlan_id)
 			*vlan_id = 0;
-		psk = sta->psk->psk;
+
 		for (pos = sta->psk; pos; pos = pos->next) {
-			if (pos->is_passphrase) {
+			if (pos->passphrase && !pos->psk) {
+				if (prev && prev->psk && prev->passphrase &&
+				    !os_strcmp(pos->passphrase, prev->passphrase))
+					continue;
+
+				pos->psk = os_zalloc(PMK_LEN);
+				if (!pos->psk)
+					continue;
+
 				pbkdf2_sha1(pos->passphrase,
 					    hapd->conf->ssid.ssid,
 					    hapd->conf->ssid.ssid_len, 4096,
 					    pos->psk, PMK_LEN);
-				pos->is_passphrase = 0;
-			}
-			if (pos->psk == prev_psk) {
-				psk = pos->next ? pos->next->psk : NULL;
-				break;
+			} else if (!pos->psk)
+				continue;
+
+			if (prev_psk && (!prev || prev->psk != prev_psk)) {
+				prev = pos;
+				continue;
 			}
+
+			psk = pos->psk;
+			break;
 		}
 	}
 	return psk;
diff --git a/src/radius/radius.c b/src/radius/radius.c
index be16e27b9..64396d319 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -165,101 +165,108 @@ struct radius_attr_type {
 		RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
 		RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
 	} data_type;
+	enum {
+		RADIUS_ATTR_F_TAGGED = BIT(0),
+		RADIUS_ATTR_F_TAGGED_LIM = BIT(1),
+	} flags;
 };
 
 static const struct radius_attr_type radius_attrs[] =
 {
-	{ RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
-	{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
-	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
-	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
-	{ RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
-	{ RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
-	{ RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST, 0},
+	{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP, 0},
+	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP, 0},
+	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST, 0},
+	{ RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST, 0},
+	{ RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST, 0},
+	{ RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
-	  RADIUS_ATTR_TEXT },
+	  RADIUS_ATTR_TEXT, 0},
 	{ RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
-	  RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
+	  RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST, 0},
 	{ RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
-	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
-	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
-	  RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
-	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP },
-	{ RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP, 0},
+	{ RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP,
+	  RADIUS_ATTR_F_TAGGED},
 	{ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_F_TAGGED},
 	{ RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
-	  RADIUS_ATTR_UNDIST },
-	{ RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+	  RADIUS_ATTR_UNDIST, RADIUS_ATTR_F_TAGGED},
+	{ RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST, 0},
 	{ RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
-	  RADIUS_ATTR_UNDIST },
+	  RADIUS_ATTR_UNDIST, 0},
 	{ RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_F_TAGGED_LIM },
 	{ RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
-	  RADIUS_ATTR_TEXT },
-	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
-	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
-	{ RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT },
+	  RADIUS_ATTR_TEXT, 0},
+	{ RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID, "Tunnel-Client-Auth-ID",
+	  RADIUS_ATTR_TEXT, RADIUS_ATTR_F_TAGGED},
+	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6, 0},
+	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP, 0},
+	{ RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT, 0},
 	{ RADIUS_ATTR_LOCATION_INFO, "Location-Information",
-	  RADIUS_ATTR_HEXDUMP },
-	{ RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, 0},
+	{ RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP, 0},
 	{ RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES,
-	  "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+	  "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP, 0},
 	{ RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES,
-	  "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
-	{ RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 },
+	  "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP, 0},
+	{ RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
-	  RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+	  RADIUS_ATTR_INT32, 0},
+	{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT, 0},
 	{ RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code",
-	  RADIUS_ATTR_INT32 },
+	  RADIUS_ATTR_INT32, 0},
 	{ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, 0},
 	{ RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, 0},
 	{ RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, 0},
 	{ RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
-	  RADIUS_ATTR_HEXDUMP },
+	  RADIUS_ATTR_HEXDUMP, 0},
 };
 #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
@@ -295,6 +302,18 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 	len = hdr->length - sizeof(struct radius_attr_hdr);
 	pos = (unsigned char *) (hdr + 1);
 
+	if (len > 0 && (attr->flags & RADIUS_ATTR_F_TAGGED)) {
+		wpa_printf(MSG_INFO, "      Tag: %u", pos[0]);
+		pos++;
+		len--;
+	}
+	if (len > 0 && (attr->flags & RADIUS_ATTR_F_TAGGED_LIM) &&
+	    pos[0] <= 0x1F) {
+		wpa_printf(MSG_INFO, "      Tag: %u", pos[0]);
+		pos++;
+		len--;
+	}
+
 	switch (attr->data_type) {
 	case RADIUS_ATTR_TEXT:
 		printf_encode(buf, sizeof(buf), pos, len);
@@ -1379,6 +1398,39 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
 }
 
 
+int radius_msg_get_attr_tag_ptr(struct radius_msg *msg, u8 type, u8 tag,
+				u8 **buf, size_t *len, size_t idx,
+				size_t *next_idx)
+{
+	size_t i;
+	struct radius_attr_hdr *attr;
+
+	for (i = idx; i < msg->attr_used; i++) {
+		attr = radius_get_attr_hdr(msg, i);
+		if (attr->type != type ||
+		    attr->length <= sizeof(*attr))
+			continue;
+
+		*buf = (u8 *) (attr + 1);
+		*len = attr->length - sizeof(*attr) - 1;
+
+		if (*buf[0] != tag)
+			continue;
+
+		(*buf)++; // skip tag from output
+		if (next_idx)
+			*next_idx = i + 1;
+
+		return 0;
+	}
+
+	*buf = NULL;
+	*len = 0;
+
+	return -1;
+}
+
+
 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
 			    size_t *len, const u8 *start)
 {
@@ -1554,7 +1606,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged,
  */
 char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 				      const u8 *secret, size_t secret_len,
-				      struct radius_msg *sent_msg, size_t n)
+				      struct radius_msg *sent_msg, size_t idx,
+				      u8 *out_tag, size_t *next_idx)
 {
 	u8 *buf = NULL;
 	size_t buflen;
@@ -1564,7 +1617,7 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 	size_t len[3];
 	u8 hash[16];
 	u8 *pos;
-	size_t i, j = 0;
+	size_t i;
 	struct radius_attr_hdr *attr;
 	const u8 *data;
 	size_t dlen;
@@ -1573,7 +1626,7 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 	char *ret = NULL;
 
 	/* find n-th valid Tunnel-Password attribute */
-	for (i = 0; i < msg->attr_used; i++) {
+	for (i = idx; i < msg->attr_used; i++) {
 		attr = radius_get_attr_hdr(msg, i);
 		if (attr == NULL ||
 		    attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
@@ -1585,9 +1638,8 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 		dlen = attr->length - sizeof(*attr);
 		if (dlen <= 3 || dlen % 16 != 3)
 			continue;
-		j++;
-		if (j <= n)
-			continue;
+		if (next_idx)
+			*next_idx = i + 1;
 
 		fdata = data;
 		fdlen = dlen;
@@ -1603,6 +1655,8 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 	buflen = fdlen;
 
 	/* init pointers */
+	if (out_tag)
+		*out_tag = *buf;
 	salt = buf + 1;
 	str = buf + 3;
 
diff --git a/src/radius/radius.h b/src/radius/radius.h
index fb8148180..f2b5e8540 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -92,6 +92,7 @@ enum { RADIUS_ATTR_USER_NAME = 1,
        RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
        RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
        RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
+       RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID = 90,
        RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
        RADIUS_ATTR_ERROR_CAUSE = 101,
        RADIUS_ATTR_EAP_KEY_NAME = 102,
@@ -297,7 +298,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged,
 			  int *tagged);
 char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 				      const u8 *secret, size_t secret_len,
-				      struct radius_msg *sent_msg, size_t n);
+				      struct radius_msg *sent_msg, size_t idx,
+				      u8 *out_tag, size_t *next_idx);
 
 static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
 					    u32 value)
@@ -321,6 +323,9 @@ static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
 int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
 			    size_t *len, const u8 *start);
 int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+int radius_msg_get_attr_tag_ptr(struct radius_msg *msg, u8 type, u8 tag,
+				u8 **buf, size_t *len, size_t idx,
+				size_t *next_idx);
 
 
 struct radius_attr_data {
-- 
2.20.1




More information about the Hostap mailing list