From patchwork Wed Nov 30 06:49:03 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: michael-dev X-Patchwork-Id: 128425 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from maxx.maxx.shmoo.com (maxx.shmoo.com [205.134.188.171]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "maxx.shmoo.com", Issuer "CA Cert Signing Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 0D2851007D3 for ; Wed, 30 Nov 2011 17:49:23 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 8F97C9C188; Wed, 30 Nov 2011 01:49:20 -0500 (EST) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qXUIPyjTYgOa; Wed, 30 Nov 2011 01:49:20 -0500 (EST) Received: from maxx.shmoo.com (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id DB37F17C013; Wed, 30 Nov 2011 01:49:14 -0500 (EST) X-Original-To: mailman-post+hostap@maxx.shmoo.com Delivered-To: mailman-post+hostap@maxx.shmoo.com Received: from localhost (localhost [127.0.0.1]) by maxx.maxx.shmoo.com (Postfix) with ESMTP id 7499517C013 for ; Wed, 30 Nov 2011 01:49:13 -0500 (EST) X-Virus-Scanned: amavisd-new at maxx.shmoo.com Received: from maxx.maxx.shmoo.com ([127.0.0.1]) by localhost (maxx.shmoo.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hU2VYrxMX7k1 for ; Wed, 30 Nov 2011 01:49:09 -0500 (EST) Received: from mo-p00-ob.rzone.de (mo-p00-ob.rzone.de [81.169.146.161]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by maxx.maxx.shmoo.com (Postfix) with ESMTPS id 1D0F09C188 for ; Wed, 30 Nov 2011 01:49:09 -0500 (EST) X-RZG-AUTH: :P20JeEWkefDI1ODZs1HHtgV3eF0OpFsRaGIBAn03jOVhprQ5djOArpzBF1FFzgYXnj3eG4op64s= X-RZG-CLASS-ID: mo00 Received: from dynamic.fami-braun.de ([2a01:198:45f::254]) by smtp.strato.de (jimi mo49) (RZmta 26.10 AUTH) with (DHE-RSA-AES256-SHA encrypted) ESMTPA id Y050f7nAU3lS4Y for ; Wed, 30 Nov 2011 07:49:04 +0100 (MET) Received: from dynamic.fami-braun.de (localhost [127.0.0.1]) by dynamic.fami-braun.de (fami-braun.de) with ESMTP id 7C99A14E207 for ; Wed, 30 Nov 2011 07:49:03 +0100 (CET) Received: by dynamic.fami-braun.de (fami-braun.de, from userid 1001) id 5E526158244; Wed, 30 Nov 2011 07:49:03 +0100 (CET) Date: Wed, 30 Nov 2011 07:49:03 +0100 From: michael-dev@fami-braun.de To: hostap@lists.shmoo.com Subject: [RFC] [PATCHv3] Use radius supplied PSK / Passphrase for WPA-PSK Message-ID: <20111130064903.GA6892@dynamic.fami-braun.de> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.20 (2009-06-14) X-BeenThere: hostap@lists.shmoo.com X-Mailman-Version: 2.1.9 Precedence: list List-Id: HostAP Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: hostap-bounces@lists.shmoo.com Errors-To: hostap-bounces@lists.shmoo.com 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 39014 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 Changes since v2: * use free enterprise number commit 1a73664e17928a746d6995e029512672e0ea0c2e Author: mbr Date: Tue Nov 29 20:29:22 2011 +0100 add PSK-from-RADIUS support Signed-hostap: Michael Braun 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..d2bc235 --- /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 39014 + 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..a0dee46 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 39014 +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)