diff mbox

[RFC,PATCHv2] Use radius supplied PSK / Passphrase for WPA-PSK

Message ID 20111129203418.GA4795@dynamic.fami-braun.de
State Superseded
Headers show

Commit Message

michael-dev Nov. 29, 2011, 8:34 p.m. UTC
Hi,                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                              
I wanted to use the per-device-PSK (WPA) feature in conjunction with a radius server that does the authorization checking and should supply the psk.                                                                                          
I found RouterOS to have a feature like this (Miktronik-Wireless-PSK or so radius attribute) but no source and a hint on this mailing list                                                                                                    
that it should not be difficult to implement.                                                                                                                                                                                                 
Please find a patch against git head attached that compiles fine and is currently under testing.                                                                                                                                              
                                                                                                                                                                                                                                              
To use this, one needs to enable the macaddr_acl = RADIUS setting and have wpa_psk_radius=1.                                                                                                                                                  
For Freeradius, one needs to add                                                                                                                                                                                                              
 VENDOR          Hostapd        7492                                                                                                                                                                                                          
 ATTRIBUTE       Hostapd-PSK          1    integer             Hostapd                                                                                                                                                                        
 ATTRIBUTE       Hostapd-Passphrase          2    string             Hostapd                                                                                                                                                                  
to the dictionary file and make sure that either Hostapd-Passphrase or Hostapd-PSK (the latter has higher priority) is in the radius reply.                                                                                                   
The PSK should be supplied hex encoded, the passphrase is turned into a psk by hostapd.                                                                                                                                                       
                                                                                                                                                                                                                                              
Regards,                                                                                                                                                                                                                                      
 M. Braun                                                                                                                                                                                                                                     
--
Changes since v1:
 * sent wrong file, changes only apply to documentation part

Comments

Jouni Malinen Nov. 29, 2011, 9:40 p.m. UTC | #1
On Tue, Nov 29, 2011 at 09:34:19PM +0100, michael-dev@fami-braun.de wrote:
> For Freeradius, one needs to add 
>  VENDOR          Hostapd        7492 

Where does that Vendor ID come from? 7492 is not "Hostapd" since there
is no such allocation and 7492 seems to be allocated to in
Kommunikationsnetz Franken e.V. in the IANA registry.
michael-dev Nov. 30, 2011, 6:46 a.m. UTC | #2
Hi,

On Tue, Nov 29, 2011 at 11:40:29PM +0200, Jouni Malinen wrote:
> On Tue, Nov 29, 2011 at 09:34:19PM +0100, michael-dev@fami-braun.de wrote:
> > For Freeradius, one needs to add 
> >  VENDOR          Hostapd        7492 
> 
> Where does that Vendor ID come from? 7492 is not "Hostapd" since there
> is no such allocation and 7492 seems to be allocated to in
> Kommunikationsnetz Franken e.V. in the IANA registry.

I just googled for a radius vendor list and found this number to be free.
PATCHv3 now uses 39014, which is free according to iana.org/assignments/enterprise-numbers.

Regards
 M. Braun
Alan DeKok Nov. 30, 2011, 8:04 a.m. UTC | #3
Jouni Malinen wrote:
> On Tue, Nov 29, 2011 at 09:34:19PM +0100, michael-dev@fami-braun.de wrote:
>> For Freeradius, one needs to add 
>>  VENDOR          Hostapd        7492 
> 
> Where does that Vendor ID come from? 7492 is not "Hostapd" since there
> is no such allocation and 7492 seems to be allocated to in
> Kommunikationsnetz Franken e.V. in the IANA registry.

  It's not from FreeRADIUS.  There's no such dictionary in the stock
distribution.

  Alan DeKok.
Alan DeKok Nov. 30, 2011, 8:07 a.m. UTC | #4
Michael Braun wrote:
> I just googled for a radius vendor list and found this number to be free.

  Don't do that.  Ever.

> PATCHv3 now uses 39014, which is free according to iana.org/assignments/enterprise-numbers.

  Don't do that, either.

  There's a reason IANA exists.  They manage number allocation.  Simply
picking "unused" values yourself is socially unwelcome.

  Please use the correct allocation method.  Or, get permission to use
someone elses number.

  Alan DeKok.
Jouni Malinen Nov. 30, 2011, 9:29 a.m. UTC | #5
On Wed, Nov 30, 2011 at 07:46:15AM +0100, Michael Braun wrote:
> I just googled for a radius vendor list and found this number to be free.
> PATCHv3 now uses 39014, which is free according to iana.org/assignments/enterprise-numbers.

Those are not free. The previous one was assigned and the new one is
reserved. There is no such thing as a free Vendor ID number that you
could pick for this type of use.

I can request a Vendor ID properly for hostapd use (and did now). The
proper way of handling this in an RFC patch is to have a comment
pointing out that a number needs to be allocated. I'll fill that in once
the allocation is completed.
diff mbox

Patch

commit 5b8d988bc867c426c6e352539afa3a2a27149625
Author: mbr <mbr@wlan-nfs.fem.tu-ilmenau.de>
Date:   Tue Nov 29 20:29:22 2011 +0100

    add PSK-from-RADIUS support
    
    Signed-hostap: Michael Braun <michael-dev@fami-braun.de>

diff --git a/hostapd/README b/hostapd/README
index a211cdd..a94bc2a 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -350,6 +350,11 @@  TODO
 #wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
 #wpa_passphrase=secret passphrase
 
+# Optionally, WPA PSKs can be read from RADIUS (to be used with macaddr_acl set to RADIUS)
+# 0 no
+# 1 may (falls back to the above if no radius attribute is found)
+#wpa_psk_radius=0
+
 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
 # entries are separated with a space.
 #wpa_key_mgmt=WPA-PSK WPA-EAP
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 107d37a..7fe10eb 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1629,6 +1629,8 @@  struct hostapd_config * hostapd_config_read(const char *fname)
 				hostapd_config_parse_key_mgmt(line, pos);
 			if (bss->wpa_key_mgmt == -1)
 				errors++;
+		} else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+			bss->wpa_psk_radius = atoi(pos);
 		} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
 			bss->wpa_pairwise =
 				hostapd_config_parse_cipher(line, pos);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 7571f45..2b11e89 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -700,6 +700,11 @@  own_ip_addr=127.0.0.1
 # configuration reloads.
 #wpa_psk_file=/etc/hostapd.wpa_psk
 
+# Optionally, WPA PSKs can be read from RADIUS (to be used with macaddr_acl set to RADIUS)
+# 0 no
+# 1 may (falls back to the above if no radius attribute is found)
+#wpa_psk_radius=0
+
 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
 # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
 # added to enable SHA256-based stronger algorithms.
diff --git a/hostapd/hostapd.wpa_psk_radius b/hostapd/hostapd.wpa_psk_radius
new file mode 100644
index 0000000..8075dc3
--- /dev/null
+++ b/hostapd/hostapd.wpa_psk_radius
@@ -0,0 +1,10 @@ 
+To fetch PSK from radius, set macaddr_acl to radius and wpa_psk_radius=1 in hostapd.conf
+Add the following definition to freeradius vendor list
+
+ VENDOR          Hostapd        7492
+ ATTRIBUTE       Hostapd-PSK          1    integer             Hostapd
+ ATTRIBUTE       Hostapd-Passphrase          2    string             Hostapd
+
+and add either a Hostapd-PSK or Hostapd-Passphrase to the radius reply (PSK overriders Passphrase).
+PSK is the hex encoded 64 character dump of the psk.
+
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index cfb6b2d..bba436a 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -57,6 +57,8 @@  void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 	bss->broadcast_key_idx_max = 2;
 	bss->eap_reauth_period = 3600;
 
+	bss->wpa_psk_radius = 0;
+
 	bss->wpa_group_rekey = 600;
 	bss->wpa_gmk_rekey = 86400;
 	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 32c8292..75b8cc9 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -219,6 +219,7 @@  struct hostapd_bss_config {
 	/* dot11AssociationSAQueryRetryTimeout (in TUs) */
 	int assoc_sa_query_retry_timeout;
 #endif /* CONFIG_IEEE80211W */
+	int wpa_psk_radius;
 	int wpa_pairwise;
 	int wpa_group;
 	int wpa_group_rekey;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 35a47f1..9834a24 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -313,6 +313,8 @@  static void handle_auth(struct hostapd_data *hapd,
 	const u8 *challenge = NULL;
 	u32 session_timeout, acct_interim_interval;
 	int vlan_id = 0;
+        u8 psk[PMK_LEN];
+	int has_psk = 0;
 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 	size_t resp_ies_len = 0;
 
@@ -375,7 +377,7 @@  static void handle_auth(struct hostapd_data *hapd,
 
 	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
 				      &session_timeout,
-				      &acct_interim_interval, &vlan_id);
+				      &acct_interim_interval, &vlan_id, &psk, &has_psk);
 	if (res == HOSTAPD_ACL_REJECT) {
 		printf("Station " MACSTR " not allowed to authenticate.\n",
 		       MAC2STR(mgmt->sa));
@@ -412,6 +414,12 @@  static void handle_auth(struct hostapd_data *hapd,
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
 	}
+	if (has_psk) {
+		sta->psk = os_zalloc(PMK_LEN);
+		os_memcpy(sta->psk, psk, PMK_LEN);
+	} else {
+		sta->psk = 0;
+	}
 
 	sta->flags &= ~WLAN_STA_PREAUTH;
 	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 9b558fa..e32d103 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -40,6 +40,8 @@  struct hostapd_cached_radius_acl {
 	u32 session_timeout;
 	u32 acct_interim_interval;
 	int vlan_id;
+        int with_psk;
+        u8 psk[PMK_LEN];
 };
 
 
@@ -68,7 +70,7 @@  static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
 
 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 				 u32 *session_timeout,
-				 u32 *acct_interim_interval, int *vlan_id)
+				 u32 *acct_interim_interval, int *vlan_id, int* with_psk, u8* psk[], int* has_psk)
 {
 	struct hostapd_cached_radius_acl *entry;
 	struct os_time now;
@@ -89,6 +91,10 @@  static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 					entry->acct_interim_interval;
 			if (vlan_id)
 				*vlan_id = entry->vlan_id;
+                        if (psk)
+		                os_memcpy(*psk, entry->psk, PMK_LEN);
+			if (has_psk)
+				*has_psk = entry->has_psk;
 			return entry->accepted;
 		}
 
@@ -214,7 +220,7 @@  static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
  */
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
-			    u32 *acct_interim_interval, int *vlan_id)
+			    u32 *acct_interim_interval, int *vlan_id, u8* psk[], int* has_psk)
 {
 	if (session_timeout)
 		*session_timeout = 0;
@@ -222,6 +228,10 @@  int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 		*acct_interim_interval = 0;
 	if (vlan_id)
 		*vlan_id = 0;
+        if (has_psk)
+		*hash_psk = 0;
+	if (psk)
+		os_memset(psk, 0, PMK_LEN);
 
 	if (hostapd_maclist_found(hapd->conf->accept_mac,
 				  hapd->conf->num_accept_mac, addr, vlan_id))
@@ -246,7 +256,7 @@  int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 		/* Check whether ACL cache has an entry for this station */
 		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
 						acct_interim_interval,
-						vlan_id);
+						vlan_id, psk, has_psk);
 		if (res == HOSTAPD_ACL_ACCEPT ||
 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 			return res;
@@ -396,6 +406,7 @@  hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
 			void *data)
 {
 	struct hostapd_data *hapd = data;
+        struct hostapd_ssid *ssid = &(hapd->conf->ssid);
 	struct hostapd_acl_query_data *query, *prev;
 	struct hostapd_cached_radius_acl *cache;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -456,6 +467,7 @@  hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
 		}
 
 		cache->vlan_id = radius_msg_get_vlanid(msg);
+                cache->has_psk = radius_msg_get_psk(msg, cache->psk, ssid);
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
 	cache->next = hapd->acl_cache;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index b2971e5..1c98f67 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -24,7 +24,7 @@  enum {
 
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
-			    u32 *acct_interim_interval, int *vlan_id);
+			    u32 *acct_interim_interval, int *vlan_id, u8* psk[], int* has_psk);
 int hostapd_acl_init(struct hostapd_data *hapd);
 void hostapd_acl_deinit(struct hostapd_data *hapd);
 
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index a9981cc..bcea28e 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -228,6 +228,7 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	wpabuf_free(sta->p2p_ie);
 
 	os_free(sta->ht_capabilities);
+	os_free(sta->psk);
 
 	os_free(sta);
 }
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 3ad00e2..7514af6 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -98,6 +98,7 @@  struct sta_info {
 	struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
 
 	int vlan_id;
+	u8* psk;
 
 	struct ieee80211_ht_capabilities *ht_capabilities;
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index ce2751e..9af82dd 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -132,6 +132,7 @@  struct wpa_auth_config {
 	int wpa;
 	int wpa_key_mgmt;
 	int wpa_pairwise;
+	int wpa_psk_radius;
 	int wpa_group;
 	int wpa_group_rekey;
 	int wpa_strict_rekey;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 1e9d422..7ef0c07 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -39,6 +39,7 @@  static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 	wconf->wpa = conf->wpa;
 	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
 	wconf->wpa_pairwise = conf->wpa_pairwise;
+	wconf->wpa_psk_radius = conf->wpa_psk_radius;
 	wconf->wpa_group = conf->wpa_group;
 	wconf->wpa_group_rekey = conf->wpa_group_rekey;
 	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
@@ -186,7 +187,10 @@  static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
 					   const u8 *prev_psk)
 {
 	struct hostapd_data *hapd = ctx;
-	return hostapd_get_psk(hapd->conf, addr, prev_psk);
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	if (sta == NULL || sta->psk == NULL)
+		return hostapd_get_psk(hapd->conf, addr, prev_psk);
+	return sta->psk;
 }
 
 
diff --git a/src/radius/radius.c b/src/radius/radius.c
index fb03a25..0a39fad 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -17,9 +17,10 @@ 
 #include "utils/common.h"
 #include "utils/wpabuf.h"
 #include "crypto/md5.h"
+#include "crypto/sha1.h"
 #include "crypto/crypto.h"
 #include "radius.h"
-
+#include "ap/ap_config.h"
 
 /**
  * struct radius_msg - RADIUS message structure for new and parsed messages
@@ -1275,6 +1276,40 @@  int radius_msg_get_vlanid(struct radius_msg *msg)
 }
 
 
+/**
+ * radius_msg_get_psk - Parse RADIUS attributes for PSK/Passphrase (WPA)
+ * @msg: RADIUS message
+ * @psk: buffer for PSK (len: PMK_LEN)
+ * Returns: 1 if psk found, 0 else
+ */
+int radius_msg_get_psk(struct radius_msg *msg, u8* psk, struct hostapd_ssid *ssid)
+{
+	char* key;
+        size_t keylen;
+
+        key = (char*) radius_msg_get_vendor_attr( msg, RADIUS_VENDOR_ID_HOSTAPD, RADIUS_VENDOR_ATTR_HAP_PSK, &keylen );
+        if (key) {
+		// PSK found, expect hex encoding
+                int len = os_strlen(key);
+                if (len == 64 && hexstr2bin(key, psk, PMK_LEN) == 0) {
+		  os_free(key); key=NULL;
+                  return 1;
+                }
+		// PSK had wrong length...
+		os_free(key); key=NULL;
+	}
+
+        key = (char*) radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_HOSTAPD, RADIUS_VENDOR_ATTR_HAP_PASSPHRASE, &keylen );
+        if (key) {
+		// Passphrase found
+		pbkdf2_sha1(key, ssid->ssid, ssid->ssid_len, 4096, psk, PMK_LEN);
+		os_free(key); key=NULL;
+		return 1;
+	} 
+	return 0;
+}
+
+
 void radius_free_class(struct radius_class_data *c)
 {
 	size_t i;
diff --git a/src/radius/radius.h b/src/radius/radius.h
index a3cdac0..bb8fe32 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -15,6 +15,8 @@ 
 #ifndef RADIUS_H
 #define RADIUS_H
 
+struct hostapd_ssid;
+
 /* RFC 2865 - RADIUS */
 
 #ifdef _MSC_VER
@@ -172,6 +174,11 @@  struct radius_ms_mppe_keys {
 	size_t recv_len;
 };
 
+/* hostapd specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_HOSTAPD 7492
+enum {  RADIUS_VENDOR_ATTR_HAP_PSK = 1,
+        RADIUS_VENDOR_ATTR_HAP_PASSPHRASE = 2
+};
 
 struct radius_msg;
 
@@ -231,6 +238,7 @@  radius_msg_add_attr_user_password(struct radius_msg *msg,
 				  const u8 *secret, size_t secret_len);
 int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
 int radius_msg_get_vlanid(struct radius_msg *msg);
+int radius_msg_get_psk(struct radius_msg *msg, u8* psk, struct hostapd_ssid *ssid);
 
 static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
 					    u32 value)