commit 2571bea9fcd7b4dddf4f10b9083217d1e2436299
Author: Michael Braun <michael-dev@fami-braun.de>
Date: Tue Dec 6 17:41:23 2011 +0100
Enabled hostapd to fetch WPA Passphrase using RADIUS Tunnel-Password attribute.
Signed-hostap: Michael Braun <michael-dev@fami-braun.de>
@@ -334,6 +334,7 @@ TODO
# Enable WPA. Setting this variable configures the AP to require WPA (either
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
# RADIUS authentication server must be configured, and WPA-EAP must be included
# in wpa_key_mgmt.
@@ -350,6 +351,13 @@ TODO
#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#wpa_passphrase=secret passphrase
+# Optionally, WPA Passphrase can be read from RADIUS
+# Requires macaddr_acl to be set to 2 (RADIUS)
+# 0 = disabled (default)
+# 1 = option; use default passphrase/psk if RADIUS server does not include Tunnel-Passord
+# 2 = required; reject authentication if RADIUS server does not include Tunnel-Password
+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
@@ -1050,9 +1050,14 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
return -1;
}
+ if (bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED && bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no radius checking (macaddr_acl = 2) enabled.");
+ return -1;
+ }
+
if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
- bss->ssid.wpa_psk_file == NULL) {
+ bss->ssid.wpa_psk_file == NULL && (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
"is not configured.");
return -1;
@@ -1629,6 +1634,15 @@ 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);
+ if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+ bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+ wpa_printf(MSG_ERROR, "Line %d: unknown "
+ "wpa_psk_radius %d",
+ line, bss->wpa_psk_radius);
+ }
} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
bss->wpa_pairwise =
hostapd_config_parse_cipher(line, pos);
@@ -676,6 +676,7 @@ own_ip_addr=127.0.0.1
# Enable WPA. Setting this variable configures the AP to require WPA (either
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
# RADIUS authentication server must be configured, and WPA-EAP must be included
# in wpa_key_mgmt.
@@ -700,6 +701,13 @@ own_ip_addr=127.0.0.1
# configuration reloads.
#wpa_psk_file=/etc/hostapd.wpa_psk
+# Optionally, WPA Passphrase can be read from RADIUS
+# Requires macaddr_acl to be set to 2 (RADIUS)
+# 0 = disabled (default)
+# 1 = option; use default passphrase/psk if RADIUS server does not include Tunnel-Passord
+# 2 = required; reject authentication if RADIUS server does not include Tunnel-Password
+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.
@@ -219,6 +219,11 @@ struct hostapd_bss_config {
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
int assoc_sa_query_retry_timeout;
#endif /* CONFIG_IEEE80211W */
+ enum {
+ PSK_RADIUS_IGNORED = 0,
+ PSK_RADIUS_ACCEPTED = 1,
+ PSK_RADIUS_REQUIRED = 2
+ } wpa_psk_radius;
int wpa_pairwise;
int wpa_group;
int wpa_group_rekey;
@@ -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,8 @@ 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 +415,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 && hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+ 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);
@@ -28,6 +28,7 @@
#include "ap_drv_ops.h"
#include "ieee802_11.h"
#include "ieee802_11_auth.h"
+#include "crypto/sha1.h"
#define RADIUS_ACL_TIMEOUT 30
@@ -40,6 +41,8 @@ struct hostapd_cached_radius_acl {
u32 session_timeout;
u32 acct_interim_interval;
int vlan_id;
+ int has_psk;
+ u8 psk[PMK_LEN];
};
@@ -68,7 +71,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, u8* psk, int* has_psk)
{
struct hostapd_cached_radius_acl *entry;
struct os_time now;
@@ -89,6 +92,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;
}
@@ -184,6 +191,12 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
goto fail;
}
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+ RADIUS_SERVICE_TYPE_OUTBOUND)) {
+ wpa_printf(MSG_DEBUG, "Could not add Service-Type");
+ goto fail;
+ }
+
os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
(u8 *) buf, os_strlen(buf))) {
@@ -214,7 +227,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 +235,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)
+ *has_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 +263,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;
@@ -456,6 +473,21 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
}
cache->vlan_id = radius_msg_get_vlanid(msg);
+
+ int passphraselen;
+ char* passphrase = radius_msg_get_tunnel_password(msg, &passphraselen, hapd->conf->radius->auth_server->shared_secret, hapd->conf->radius->auth_server->shared_secret_len, req);
+ cache->has_psk = (passphrase != NULL);
+ if (passphrase != NULL) {
+ /** passphrase does not contain the NULL termination. Add it here as pbkdf2_sha1 requires it. */
+ char* strpassphrase = os_zalloc(passphraselen+1);
+ os_memcpy(strpassphrase, passphrase, passphraselen);
+ pbkdf2_sha1(strpassphrase, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, 4096, cache->psk, PMK_LEN);
+ os_free(passphrase); passphrase = NULL;
+ os_free(strpassphrase); strpassphrase = NULL;
+ }
+
+ if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && cache->psk == NULL)
+ cache->accepted = HOSTAPD_ACL_REJECT;
} else
cache->accepted = HOSTAPD_ACL_REJECT;
cache->next = hapd->acl_cache;
@@ -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);
@@ -509,6 +509,12 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
goto fail;
}
+ if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+ RADIUS_SERVICE_TYPE_FRAMED)) {
+ printf("Could not add Service-Type\n");
+ goto fail;
+ }
+
if (sta->flags & WLAN_STA_PREAUTH) {
os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
sizeof(buf));
@@ -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);
}
@@ -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;
@@ -186,7 +186,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;
}
@@ -173,6 +173,7 @@ static struct radius_attr_type radius_attrs[] =
{ 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_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
@@ -218,6 +219,8 @@ static struct radius_attr_type radius_attrs[] =
{ RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
{ RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
RADIUS_ATTR_HEXDUMP },
+ { 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_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
@@ -1275,6 +1278,126 @@ int radius_msg_get_vlanid(struct radius_msg *msg)
}
+/**
+ * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password with lowest tag
+ * @msg: RADIUS message
+ * @passwordlen: length of returned password
+ * Returns: pointer to password (free with os_free) or NULL
+ */
+char* radius_msg_get_tunnel_password(struct radius_msg *msg, int* keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg)
+{
+ /* generic radius fields */
+ u8 *buf = NULL;
+ size_t buflen;
+
+ /* fields of tunnel-password */
+ const u8 *salt;
+ u8* str;
+
+ /* helper vars for md5 */
+ const u8 *addr[3];
+ size_t len[3];
+ u8 hash[16];
+
+ /* helper var for iteration blocks */
+ u8* pos;
+ size_t i;
+
+ /* helper vars for attribute iteration */
+ struct radius_attr_hdr *attr = NULL;
+ const u8 *data;
+ size_t dlen;
+ const u8 *fdata = NULL; // points to found item
+ size_t fdlen = -1;
+
+ /* default return value */
+ char* ret = NULL;
+
+ /* find packet with lowest tag and check it */
+ for (i = 0; i < msg->attr_used; i++) {
+ attr = radius_get_attr_hdr(msg, i);
+ if (attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
+ continue;
+ }
+ if (attr->length <= 5) {
+ continue;
+ }
+ data = (const u8 *) (attr + 1);
+ dlen = attr->length - sizeof(*attr);
+ if ((dlen <= 3) || (dlen % 16 != 3)) {
+ continue;
+ } else if ((fdata != NULL) && (fdata[0] <= data[0])) {
+ continue;
+ }
+
+ fdata = data;
+ fdlen = dlen;
+ }
+ if (fdata == NULL) {
+ goto out;
+ }
+
+ /* alloc writable memory for decryption */
+ buf = os_zalloc(fdlen);
+ os_memcpy(buf, fdata, fdlen);
+ buflen = fdlen;
+
+ /* init pointers */
+ salt = buf + 1;
+ str = buf + 3;
+
+ /* decrypt blocks */
+ pos = buf + buflen - 16; // last block
+ while (pos >= str + 16) { // all but the first block
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = pos - 16;
+ len[1] = 16;
+ md5_vector(2, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ pos[i] ^= hash[i];
+
+ pos -= 16;
+ }
+
+ /* decrypt first block */
+ if (str != pos) {
+ goto out;
+ }
+ addr[0] = secret;
+ len[0] = secret_len;
+ addr[1] = sent_msg->hdr->authenticator;
+ len[1] = 16;
+ addr[2] = salt;
+ len[2] = 2;
+ md5_vector(3, addr, len, hash);
+
+ for (i = 0; i < 16; i++)
+ pos[i] ^= hash[i];
+
+ /* derive plaintext length from first subfield */
+ *keylen = (unsigned char) str[0];
+ if ((u8*) (str + *keylen) >= (u8*) (buf + buflen)) {
+ // decryption error - invalid key length
+ goto out;
+ }
+ if (*keylen == 0) {
+ // empty password
+ goto out;
+ }
+
+ /* copy passphrase into new buffer */
+ ret = os_zalloc(*keylen);
+ os_memcpy(ret, str + 1, *keylen);
+
+out:
+ /* return new buffer */
+ os_free(buf);
+ return ret;
+}
+
+
void radius_free_class(struct radius_class_data *c)
{
size_t i;
@@ -52,6 +52,7 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_USER_PASSWORD = 2,
RADIUS_ATTR_NAS_IP_ADDRESS = 4,
RADIUS_ATTR_NAS_PORT = 5,
+ RADIUS_ATTR_SERVICE_TYPE = 6,
RADIUS_ATTR_FRAMED_MTU = 12,
RADIUS_ATTR_REPLY_MESSAGE = 18,
RADIUS_ATTR_STATE = 24,
@@ -82,6 +83,7 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_NAS_PORT_TYPE = 61,
RADIUS_ATTR_TUNNEL_TYPE = 64,
RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+ RADIUS_ATTR_TUNNEL_PASSWORD = 69,
RADIUS_ATTR_CONNECT_INFO = 77,
RADIUS_ATTR_EAP_MESSAGE = 79,
RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
@@ -145,6 +147,19 @@ enum { RADIUS_ATTR_USER_NAME = 1,
#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+/* Service-Type */
+#define RADIUS_SERVICE_TYPE_LOGIN 1
+#define RADIUS_SERVICE_TYPE_FRAMED 2
+#define RADIUS_SERVICE_TYPE_CALLBACK_LOGIN 3
+#define RADIUS_SERVICE_TYPE_CALLBACK_FRAMED 4
+#define RADIUS_SERVICE_TYPE_OUTBOUND 5
+#define RADIUS_SERVICE_TYPE_ADMINISTRATIVE 6
+#define RADIUS_SERVICE_TYPE_NAS_PROMPT 7
+#define RADIUS_SERVICE_TYPE_AUTHENTICATE_ONLY 8
+#define RADIUS_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
+#define RADIUS_SERVICE_TYPE_CALL_CHECK 10
+#define RADIUS_SERVICE_TYPE_CALLBACK ADMINISTRATIVE 11
+
struct radius_attr_vendor {
u8 vendor_type;
@@ -231,6 +246,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);
+char* radius_msg_get_tunnel_password(struct radius_msg *msg, int* keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg);
static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
u32 value)