summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2008-09-27 15:16:10 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2008-09-27 15:16:10 +0000
commitb065efd30adfe94123c1b52f0767f407ce44033b (patch)
tree03b134eef33bed19b360c566708dc791c3141240
parent5f302c06db0af70022a22dff119bab0838242f86 (diff)
Initial implementation of PMKSA caching and pre-authentication.
This will be required for future WPA-Enterprise support (802.1X). Add ieee80211_needs_auth() function (not implemented yet) to notify the userland 802.1X PACP machine when an 802.1X port becomes enabled (that is after successfull 802.11 Open System authentication). Add SIOCS80211KEYRUN and SIOCS80211KEYAVAIL ioctls so that the PACP state machine can kick the 802.11 key state machine and install PMKs obtained from 802.1X (pre-)authentication. Enable SHA-256 based AKMPs by default while I'm here (TGw). This uses SHA-256 for key-derivation (instead of SHA1), AES-128-CMAC for data integrity, and AES Key Wrap for data protection of EAPOL-Key frames. An OpenBSD AP will always advertise this capability and an OpenBSD STA will always prefer SHA-256 based AKMPs over SHA1 based ones if both are supported by an AP.
-rw-r--r--sys/net80211/ieee80211_crypto.c148
-rw-r--r--sys/net80211/ieee80211_crypto.h41
-rw-r--r--sys/net80211/ieee80211_input.c37
-rw-r--r--sys/net80211/ieee80211_ioctl.c36
-rw-r--r--sys/net80211/ieee80211_ioctl.h17
-rw-r--r--sys/net80211/ieee80211_node.c136
-rw-r--r--sys/net80211/ieee80211_node.h24
-rw-r--r--sys/net80211/ieee80211_output.c20
-rw-r--r--sys/net80211/ieee80211_pae_input.c71
-rw-r--r--sys/net80211/ieee80211_pae_output.c14
-rw-r--r--sys/net80211/ieee80211_priv.h14
-rw-r--r--sys/net80211/ieee80211_proto.c55
-rw-r--r--sys/net80211/ieee80211_var.h13
13 files changed, 459 insertions, 167 deletions
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c
index 93c65db645c..109b1519e3f 100644
--- a/sys/net80211/ieee80211_crypto.c
+++ b/sys/net80211/ieee80211_crypto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_crypto.c,v 1.55 2008/08/27 09:05:04 damien Exp $ */
+/* $OpenBSD: ieee80211_crypto.c,v 1.56 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -54,21 +54,21 @@
void ieee80211_prf(const u_int8_t *, size_t, const u_int8_t *, size_t,
const u_int8_t *, size_t, u_int8_t *, size_t);
-#ifdef notyet
void ieee80211_kdf(const u_int8_t *, size_t, const u_int8_t *, size_t,
const u_int8_t *, size_t, u_int8_t *, size_t);
-void ieee80211_derive_pmkid(const u_int8_t *, size_t, const u_int8_t *,
- const u_int8_t *, u_int8_t *);
-#endif
+void ieee80211_derive_pmkid(enum ieee80211_akm, const u_int8_t *,
+ const u_int8_t *, const u_int8_t *, u_int8_t *);
void
ieee80211_crypto_attach(struct ifnet *ifp)
{
struct ieee80211com *ic = (void *)ifp;
+ TAILQ_INIT(&ic->ic_pmksa);
if (ic->ic_caps & IEEE80211_C_RSN) {
ic->ic_rsnprotos = IEEE80211_PROTO_WPA | IEEE80211_PROTO_RSN;
- ic->ic_rsnakms = IEEE80211_AKM_PSK | IEEE80211_AKM_8021X;
+ ic->ic_rsnakms = IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK |
+ IEEE80211_AKM_8021X | IEEE80211_AKM_SHA256_8021X;
ic->ic_rsnciphers = IEEE80211_CIPHER_TKIP |
IEEE80211_CIPHER_CCMP;
ic->ic_rsngroupcipher = IEEE80211_CIPHER_TKIP;
@@ -82,15 +82,25 @@ void
ieee80211_crypto_detach(struct ifnet *ifp)
{
struct ieee80211com *ic = (void *)ifp;
+ struct ieee80211_pmk *pmk;
int i;
- /* clear all keys from memory */
+ /* purge the PMKSA cache */
+ while ((pmk = TAILQ_FIRST(&ic->ic_pmksa)) != NULL) {
+ TAILQ_REMOVE(&ic->ic_pmksa, pmk, pmk_next);
+ memset(pmk, 0, sizeof(*pmk));
+ free(pmk, M_DEVBUF);
+ }
+
+ /* clear all group keys from memory */
for (i = 0; i < IEEE80211_GROUP_NKID; i++) {
struct ieee80211_key *k = &ic->ic_nw_keys[i];
if (k->k_cipher != IEEE80211_CIPHER_NONE)
(*ic->ic_delete_key)(ic, NULL, k);
memset(k, 0, sizeof(*k));
}
+
+ /* clear pre-shared key from memory */
memset(ic->ic_psk, 0, IEEE80211_PMK_LEN);
}
@@ -168,24 +178,6 @@ ieee80211_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni,
memset(k, 0, sizeof(*k));
}
-/*
- * Retrieve the pairwise master key configured for a given node.
- * When PSK AKMP is in use, the pairwise master key is the pre-shared key
- * and the node is not used.
- */
-const u_int8_t *
-ieee80211_get_pmk(struct ieee80211com *ic, struct ieee80211_node *ni,
- const u_int8_t *pmkid)
-{
- if (ni->ni_rsnakms == IEEE80211_AKM_PSK ||
- ni->ni_rsnakms == IEEE80211_AKM_SHA256_PSK)
- return ic->ic_psk; /* the PMK is the PSK */
-
- /* XXX find the PMK in the PMKSA cache using the PMKID */
-
- return NULL; /* not yet supported */
-}
-
struct ieee80211_key *
ieee80211_get_txkey(struct ieee80211com *ic, const struct ieee80211_frame *wh,
struct ieee80211_node *ni)
@@ -336,7 +328,6 @@ ieee80211_prf(const u_int8_t *key, size_t key_len, const u_int8_t *label,
/*
* SHA256-based Key Derivation Function (see 8.5.1.5.2).
*/
-#ifdef notyet
void
ieee80211_kdf(const u_int8_t *key, size_t key_len, const u_int8_t *label,
size_t label_len, const u_int8_t *context, size_t context_len,
@@ -365,7 +356,6 @@ ieee80211_kdf(const u_int8_t *key, size_t key_len, const u_int8_t *label,
len -= SHA256_DIGEST_LENGTH;
}
}
-#endif
/*
* Derive Pairwise Transient Key (PTK) (see 8.5.1.2).
@@ -375,6 +365,8 @@ ieee80211_derive_ptk(enum ieee80211_akm akm, const u_int8_t *pmk,
const u_int8_t *aa, const u_int8_t *spa, const u_int8_t *anonce,
const u_int8_t *snonce, struct ieee80211_ptk *ptk)
{
+ void (*kdf)(const u_int8_t *, size_t, const u_int8_t *, size_t,
+ const u_int8_t *, size_t, u_int8_t *, size_t);
u_int8_t buf[2 * IEEE80211_ADDR_LEN + 2 * EAPOL_KEY_NONCE_LEN];
int ret;
@@ -388,30 +380,55 @@ ieee80211_derive_ptk(enum ieee80211_akm akm, const u_int8_t *pmk,
memcpy(&buf[12], ret ? anonce : snonce, EAPOL_KEY_NONCE_LEN);
memcpy(&buf[44], ret ? snonce : anonce, EAPOL_KEY_NONCE_LEN);
- ieee80211_prf(pmk, IEEE80211_PMK_LEN, "Pairwise key expansion", 23,
+ kdf = ieee80211_is_sha256_akm(akm) ? ieee80211_kdf : ieee80211_prf;
+ (*kdf)(pmk, IEEE80211_PMK_LEN, "Pairwise key expansion", 23,
buf, sizeof buf, (u_int8_t *)ptk, sizeof(*ptk));
}
-/*
- * Derive Pairwise Master Key Identifier (PMKID) (see 8.5.1.2).
- */
-#ifdef notyet
-void
-ieee80211_derive_pmkid(const u_int8_t *pmk, size_t pmk_len, const u_int8_t *aa,
+static void
+ieee80211_pmkid_sha1(const u_int8_t *pmk, const u_int8_t *aa,
const u_int8_t *spa, u_int8_t *pmkid)
{
HMAC_SHA1_CTX ctx;
u_int8_t digest[SHA1_DIGEST_LENGTH];
- HMAC_SHA1_Init(&ctx, pmk, pmk_len);
+ HMAC_SHA1_Init(&ctx, pmk, IEEE80211_PMK_LEN);
HMAC_SHA1_Update(&ctx, "PMK Name", 8);
HMAC_SHA1_Update(&ctx, aa, IEEE80211_ADDR_LEN);
HMAC_SHA1_Update(&ctx, spa, IEEE80211_ADDR_LEN);
HMAC_SHA1_Final(digest, &ctx);
- /* use the first 128 bits of the HMAC-SHA1 */
+ /* use the first 128 bits of HMAC-SHA1 */
memcpy(pmkid, digest, IEEE80211_PMKID_LEN);
}
-#endif
+
+static void
+ieee80211_pmkid_sha256(const u_int8_t *pmk, const u_int8_t *aa,
+ const u_int8_t *spa, u_int8_t *pmkid)
+{
+ HMAC_SHA256_CTX ctx;
+ u_int8_t digest[SHA256_DIGEST_LENGTH];
+
+ HMAC_SHA256_Init(&ctx, pmk, IEEE80211_PMK_LEN);
+ HMAC_SHA256_Update(&ctx, "PMK Name", 8);
+ HMAC_SHA256_Update(&ctx, aa, IEEE80211_ADDR_LEN);
+ HMAC_SHA256_Update(&ctx, spa, IEEE80211_ADDR_LEN);
+ HMAC_SHA256_Final(digest, &ctx);
+ /* use the first 128 bits of HMAC-SHA-256 */
+ memcpy(pmkid, digest, IEEE80211_PMKID_LEN);
+}
+
+/*
+ * Derive Pairwise Master Key Identifier (PMKID) (see 8.5.1.2).
+ */
+void
+ieee80211_derive_pmkid(enum ieee80211_akm akm, const u_int8_t *pmk,
+ const u_int8_t *aa, const u_int8_t *spa, u_int8_t *pmkid)
+{
+ if (ieee80211_is_sha256_akm(akm))
+ ieee80211_pmkid_sha256(pmk, aa, spa, pmkid);
+ else
+ ieee80211_pmkid_sha1(pmk, aa, spa, pmkid);
+}
typedef union _ANY_CTX {
HMAC_MD5_CTX md5;
@@ -576,3 +593,60 @@ ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *key,
return 1; /* unknown Key Descriptor Version */
}
+
+/*
+ * Add a PMK entry to the PMKSA cache.
+ */
+struct ieee80211_pmk *
+ieee80211_pmksa_add(struct ieee80211com *ic, enum ieee80211_akm akm,
+ const u_int8_t *macaddr, const u_int8_t *key, u_int32_t lifetime)
+{
+ struct ieee80211_pmk *pmk;
+
+ /* check if an entry already exists for this (STA,AKMP) */
+ TAILQ_FOREACH(pmk, &ic->ic_pmksa, pmk_next) {
+ if (pmk->pmk_akm == akm &&
+ IEEE80211_ADDR_EQ(pmk->pmk_macaddr, macaddr))
+ break;
+ }
+ if (pmk == NULL) {
+ /* allocate a new PMKSA entry */
+ if ((pmk = malloc(sizeof(*pmk), M_DEVBUF, M_NOWAIT)) == NULL)
+ return NULL;
+ pmk->pmk_akm = akm;
+ IEEE80211_ADDR_COPY(pmk->pmk_macaddr, macaddr);
+ TAILQ_INSERT_TAIL(&ic->ic_pmksa, pmk, pmk_next);
+ }
+ memcpy(pmk->pmk_key, key, IEEE80211_PMK_LEN);
+ pmk->pmk_lifetime = lifetime; /* XXX not used yet */
+#ifndef IEEE80211_STA_ONLY
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ ieee80211_derive_pmkid(pmk->pmk_akm, pmk->pmk_key,
+ ic->ic_myaddr, macaddr, pmk->pmk_pmkid);
+ } else
+#endif
+ {
+ ieee80211_derive_pmkid(pmk->pmk_akm, pmk->pmk_key,
+ macaddr, ic->ic_myaddr, pmk->pmk_pmkid);
+ }
+ return pmk;
+}
+
+/*
+ * Check if we have a cached PMK entry for the specified node and PMKID.
+ */
+struct ieee80211_pmk *
+ieee80211_pmksa_find(struct ieee80211com *ic, struct ieee80211_node *ni,
+ const u_int8_t *pmkid)
+{
+ struct ieee80211_pmk *pmk;
+
+ TAILQ_FOREACH(pmk, &ic->ic_pmksa, pmk_next) {
+ if (pmk->pmk_akm == ni->ni_rsnakms &&
+ IEEE80211_ADDR_EQ(pmk->pmk_macaddr, ni->ni_macaddr) &&
+ (pmkid == NULL ||
+ memcmp(pmk->pmk_pmkid, pmkid, IEEE80211_PMKID_LEN) == 0))
+ break;
+ }
+ return pmk;
+}
diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h
index 56492203c3f..29c88d6b076 100644
--- a/sys/net80211/ieee80211_crypto.h
+++ b/sys/net80211/ieee80211_crypto.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_crypto.h,v 1.20 2008/08/27 09:05:04 damien Exp $ */
+/* $OpenBSD: ieee80211_crypto.h,v 1.21 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -43,12 +43,24 @@ enum ieee80211_akm {
IEEE80211_AKM_NONE = 0x00000000,
IEEE80211_AKM_8021X = 0x00000001,
IEEE80211_AKM_PSK = 0x00000002,
- IEEE80211_AKM_FBT_8021X = 0x00000004, /* 11r */
- IEEE80211_AKM_FBT_PSK = 0x00000008, /* 11r */
- IEEE80211_AKM_SHA256_8021X = 0x00000010, /* 11w */
- IEEE80211_AKM_SHA256_PSK = 0x00000020 /* 11w */
+ IEEE80211_AKM_SHA256_8021X = 0x00000004, /* 11w */
+ IEEE80211_AKM_SHA256_PSK = 0x00000008 /* 11w */
};
+static __inline int
+ieee80211_is_8021x_akm(enum ieee80211_akm akm)
+{
+ return akm == IEEE80211_AKM_8021X ||
+ akm == IEEE80211_AKM_SHA256_8021X;
+}
+
+static __inline int
+ieee80211_is_sha256_akm(enum ieee80211_akm akm)
+{
+ return akm == IEEE80211_AKM_SHA256_8021X ||
+ akm == IEEE80211_AKM_SHA256_PSK;
+}
+
#define IEEE80211_KEYBUF_SIZE 16
#define IEEE80211_TKIP_HDRLEN 8
@@ -75,6 +87,21 @@ struct ieee80211_key {
void *k_priv;
};
+/*
+ * Entry in the PMKSA cache.
+ */
+struct ieee80211_pmk {
+ enum ieee80211_akm pmk_akm;
+ u_int32_t pmk_lifetime;
+#define IEEE80211_PMK_INFINITE 0
+
+ u_int8_t pmk_pmkid[IEEE80211_PMKID_LEN];
+ u_int8_t pmk_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t pmk_key[IEEE80211_PMK_LEN];
+
+ TAILQ_ENTRY(ieee80211_pmk) pmk_next;
+};
+
/* forward references */
struct ieee80211com;
struct ieee80211_node;
@@ -107,7 +134,9 @@ void ieee80211_eapol_key_encrypt(struct ieee80211com *,
int ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *,
const u_int8_t *);
-const u_int8_t *ieee80211_get_pmk(struct ieee80211com *,
+struct ieee80211_pmk *ieee80211_pmksa_add(struct ieee80211com *,
+ enum ieee80211_akm, const u_int8_t *, const u_int8_t *, u_int32_t);
+struct ieee80211_pmk *ieee80211_pmksa_find(struct ieee80211com *,
struct ieee80211_node *, const u_int8_t *);
void ieee80211_derive_ptk(enum ieee80211_akm, const u_int8_t *,
const u_int8_t *, const u_int8_t *, const u_int8_t *,
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index 38428f009a0..24ef3ed4b6a 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_input.c,v 1.105 2008/09/27 15:00:08 damien Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.106 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
@@ -782,10 +782,6 @@ ieee80211_parse_rsn_akm(const u_int8_t selector[4])
return IEEE80211_AKM_8021X;
case 2: /* PSK */
return IEEE80211_AKM_PSK;
- case 3: /* Fast BSS Transition IEEE 802.1X */
- return IEEE80211_AKM_FBT_8021X;
- case 4: /* Fast BSS Transition PSK */
- return IEEE80211_AKM_FBT_PSK;
case 5: /* IEEE 802.1X with SHA256 KDF */
return IEEE80211_AKM_SHA256_8021X;
case 6: /* PSK with SHA256 KDF */
@@ -825,6 +821,7 @@ ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm,
rsn->rsn_akms = IEEE80211_AKM_8021X;
/* if RSN capabilities missing, default to 0 */
rsn->rsn_caps = 0;
+ rsn->rsn_npmkids = 0;
/* read Group Data Cipher Suite field */
if (frm + 4 > efrm)
@@ -879,15 +876,15 @@ ieee80211_parse_rsn_body(struct ieee80211com *ic, const u_int8_t *frm,
/* read PMKID Count field */
if (frm + 2 > efrm)
return 0;
- s = LE_READ_2(frm);
+ s = rsn->rsn_npmkids = LE_READ_2(frm);
frm += 2;
/* read PMKID List */
if (frm + s * IEEE80211_PMKID_LEN > efrm)
return IEEE80211_STATUS_IE_INVALID;
- while (s-- > 0) {
- /* ignore PMKIDs for now */
- frm += IEEE80211_PMKID_LEN;
+ if (s != 0) {
+ rsn->rsn_pmkids = frm;
+ frm += s * IEEE80211_PMKID_LEN;
}
/* read Group Management Cipher Suite field */
@@ -1613,6 +1610,28 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, struct mbuf *m0,
ni->ni_rsngroupcipher = ic->ic_bss->ni_rsngroupcipher;
ni->ni_rsngroupmgmtcipher = ic->ic_bss->ni_rsngroupmgmtcipher;
ni->ni_rsncaps = rsn.rsn_caps;
+
+ if (ieee80211_is_8021x_akm(ni->ni_rsnakms)) {
+ struct ieee80211_pmk *pmk = NULL;
+ const u_int8_t *pmkid = rsn.rsn_pmkids;
+ /*
+ * Check if we have a cached PMK entry matching one
+ * of the PMKIDs specified in the RSN IE.
+ */
+ while (rsn.rsn_npmkids-- > 0) {
+ pmk = ieee80211_pmksa_find(ic, ni, pmkid);
+ if (pmk != NULL)
+ break;
+ pmkid += IEEE80211_PMKID_LEN;
+ }
+ if (pmk != NULL) {
+ memcpy(ni->ni_pmk, pmk->pmk_key,
+ IEEE80211_PMK_LEN);
+ memcpy(ni->ni_pmkid, pmk->pmk_pmkid,
+ IEEE80211_PMKID_LEN);
+ ni->ni_flags |= IEEE80211_NODE_PMK;
+ }
+ }
} else
ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index 3ffcf858c46..aa5b2eedd82 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_ioctl.c,v 1.24 2008/08/29 12:14:53 damien Exp $ */
+/* $OpenBSD: ieee80211_ioctl.c,v 1.25 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_ioctl.c,v 1.15 2004/05/06 02:58:16 dyoung Exp $ */
/*-
@@ -52,6 +52,7 @@
#endif
#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_crypto.h>
#include <net80211/ieee80211_ioctl.h>
void ieee80211_node2req(struct ieee80211com *,
@@ -237,11 +238,15 @@ ieee80211_ioctl_setwpaparms(struct ieee80211com *ic,
ic->ic_rsnakms = 0;
if (wpa->i_akms & IEEE80211_WPA_AKM_PSK)
- ic->ic_rsnakms |= IEEE80211_AKM_PSK;
+ ic->ic_rsnakms |=
+ IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK;
if (wpa->i_akms & IEEE80211_WPA_AKM_IEEE8021X)
- ic->ic_rsnakms |= IEEE80211_AKM_8021X;
+ ic->ic_rsnakms |=
+ IEEE80211_AKM_8021X | IEEE80211_AKM_SHA256_8021X;
if (ic->ic_rsnakms == 0) /* set to default (PSK+802.1X) */
- ic->ic_rsnakms = IEEE80211_AKM_PSK | IEEE80211_AKM_8021X;
+ ic->ic_rsnakms =
+ IEEE80211_AKM_PSK | IEEE80211_AKM_8021X |
+ IEEE80211_AKM_SHA256_PSK | IEEE80211_AKM_SHA256_8021X;
if (wpa->i_groupcipher == IEEE80211_WPA_CIPHER_WEP40)
ic->ic_rsngroupcipher = IEEE80211_CIPHER_WEP40;
@@ -287,9 +292,11 @@ ieee80211_ioctl_getwpaparms(struct ieee80211com *ic,
wpa->i_protos |= IEEE80211_WPA_PROTO_WPA2;
wpa->i_akms = 0;
- if (ic->ic_rsnakms & IEEE80211_AKM_PSK)
+ if (ic->ic_rsnakms &
+ (IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK))
wpa->i_akms |= IEEE80211_WPA_AKM_PSK;
- if (ic->ic_rsnakms & IEEE80211_AKM_8021X)
+ if (ic->ic_rsnakms &
+ (IEEE80211_AKM_8021X | IEEE80211_AKM_SHA256_8021X))
wpa->i_akms |= IEEE80211_WPA_AKM_IEEE8021X;
if (ic->ic_rsngroupcipher == IEEE80211_CIPHER_WEP40)
@@ -323,6 +330,8 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
struct ieee80211_nwid nwid;
struct ieee80211_wpapsk *psk;
struct ieee80211_wmmparams *wmm;
+ struct ieee80211_keyavail *ka;
+ struct ieee80211_keyrun *kr;
struct ieee80211_power *power;
struct ieee80211_bssid *bssid;
struct ieee80211chanreq *chanreq;
@@ -435,6 +444,21 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
} else
psk->i_enabled = 0;
break;
+ case SIOCS80211KEYAVAIL:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ ka = (struct ieee80211_keyavail *)data;
+ (void)ieee80211_pmksa_add(ic, IEEE80211_AKM_8021X,
+ ka->i_macaddr, ka->i_key, ka->i_lifetime);
+ (void)ieee80211_pmksa_add(ic, IEEE80211_AKM_SHA256_8021X,
+ ka->i_macaddr, ka->i_key, ka->i_lifetime);
+ break;
+ case SIOCS80211KEYRUN:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ kr = (struct ieee80211_keyrun *)data;
+ error = ieee80211_keyrun(ic, kr->i_macaddr);
+ break;
case SIOCS80211POWER:
if ((error = suser(curproc, 0)) != 0)
break;
diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h
index 9f15c0fa7e6..bee876d2436 100644
--- a/sys/net80211/ieee80211_ioctl.h
+++ b/sys/net80211/ieee80211_ioctl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_ioctl.h,v 1.13 2008/08/12 16:51:39 damien Exp $ */
+/* $OpenBSD: ieee80211_ioctl.h,v 1.14 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */
/*-
@@ -227,6 +227,21 @@ struct ieee80211_wmmparams {
#define SIOCS80211WMMPARMS _IOW('i', 249, struct ieee80211_wmmparams)
#define SIOCG80211WMMPARMS _IOWR('i', 250, struct ieee80211_wmmparams)
+struct ieee80211_keyavail {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int8_t i_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t i_key[32];
+ u_int32_t i_lifetime;
+};
+
+struct ieee80211_keyrun {
+ char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */
+ u_int8_t i_macaddr[IEEE80211_ADDR_LEN];
+};
+
+#define SIOCS80211KEYAVAIL _IOW('i', 251, struct ieee80211_keyavail)
+#define SIOCS80211KEYRUN _IOW('i', 252, struct ieee80211_keyrun)
+
/* scan request (will block) */
#define IEEE80211_SCAN_TIMEOUT 30 /* timeout in seconds */
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 6c0741c5b4c..35c674701aa 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -1,9 +1,10 @@
-/* $OpenBSD: ieee80211_node.c,v 1.45 2008/08/29 12:14:53 damien Exp $ */
+/* $OpenBSD: ieee80211_node.c,v 1.46 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2008 Damien Bergamini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -73,6 +74,7 @@ struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_copy(struct ieee80211com *, struct ieee80211_node *,
const struct ieee80211_node *);
+void ieee80211_choose_rsnparams(struct ieee80211com *);
u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
const struct ieee80211_node *);
void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *,
@@ -80,6 +82,7 @@ void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *,
void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *);
struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *);
void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *);
#ifndef IEEE80211_STA_ONLY
void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
@@ -421,9 +424,12 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
fail |= 0x40;
if ((ni->ni_rsnakms & ic->ic_rsnakms) == 0)
fail |= 0x40;
- if ((ni->ni_rsnakms & ic->ic_rsnakms) == IEEE80211_AKM_PSK &&
- !(ic->ic_flags & IEEE80211_F_PSK))
- fail |= 0x40;
+ if ((ni->ni_rsnakms & ic->ic_rsnakms &
+ ~(IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK)) == 0) {
+ /* AP only supports PSK AKMPs */
+ if (!(ic->ic_flags & IEEE80211_F_PSK))
+ fail |= 0x40;
+ }
if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0)
fail |= 0x40;
@@ -592,39 +598,9 @@ ieee80211_end_scan(struct ifnet *ifp)
ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan);
ieee80211_reset_erp(ic);
- if (ic->ic_flags & IEEE80211_F_RSNON) {
- /* prefer RSN (WPA2) over WPA */
- ni->ni_rsnprotos &= ic->ic_rsnprotos;
- if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)
- ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
- else
- ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
- /*
- * If a pre-shared key is configured and AP supports PSK,
- * choose PSK as AKMP.
- */
- ni->ni_rsnakms &= ic->ic_rsnakms;
- if ((ni->ni_rsnakms & IEEE80211_AKM_PSK) &&
- (ic->ic_flags & IEEE80211_F_PSK))
- ni->ni_rsnakms = IEEE80211_AKM_PSK;
- else
- ni->ni_rsnakms = IEEE80211_AKM_8021X;
-
- /* prefer CCMP over TKIP if the AP supports it */
- ni->ni_rsnciphers &= ic->ic_rsnciphers;
- if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP)
- ni->ni_rsnciphers = IEEE80211_CIPHER_CCMP;
- else
- ni->ni_rsnciphers = IEEE80211_CIPHER_TKIP;
-
- ni->ni_rsncipher = ni->ni_rsnciphers;
-
- /* use MFP if we both support it */
- if ((ic->ic_caps & IEEE80211_C_MFP) &&
- (ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC))
- ni->ni_flags |= IEEE80211_NODE_MFP;
-
- } else if (ic->ic_flags & IEEE80211_F_WEPON)
+ if (ic->ic_flags & IEEE80211_F_RSNON)
+ ieee80211_choose_rsnparams(ic);
+ else if (ic->ic_flags & IEEE80211_F_WEPON)
ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP;
ieee80211_node_newstate(selbs, IEEE80211_STA_BSS);
@@ -648,6 +624,63 @@ ieee80211_end_scan(struct ifnet *ifp)
ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
}
+/*
+ * Autoselect the best RSN parameters (protocol, AKMP, pairwise cipher...)
+ * that are supported by both peers (STA mode only).
+ */
+void
+ieee80211_choose_rsnparams(struct ieee80211com *ic)
+{
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_pmk *pmk;
+
+ /* filter out unsupported protocol versions */
+ ni->ni_rsnprotos &= ic->ic_rsnprotos;
+ /* prefer RSN (aka WPA2) over WPA */
+ if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)
+ ni->ni_rsnprotos = IEEE80211_PROTO_RSN;
+ else
+ ni->ni_rsnprotos = IEEE80211_PROTO_WPA;
+
+ /* filter out unsupported AKMPs */
+ ni->ni_rsnakms &= ic->ic_rsnakms;
+ /* prefer SHA-256 based AKMPs */
+ if ((ic->ic_flags & IEEE80211_F_PSK) && (ni->ni_rsnakms &
+ (IEEE80211_AKM_PSK | IEEE80211_AKM_SHA256_PSK))) {
+ /* AP supports PSK AKMP and a PSK is configured */
+ if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)
+ ni->ni_rsnakms = IEEE80211_AKM_SHA256_PSK;
+ else
+ ni->ni_rsnakms = IEEE80211_AKM_PSK;
+ } else {
+ if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)
+ ni->ni_rsnakms = IEEE80211_AKM_SHA256_8021X;
+ else
+ ni->ni_rsnakms = IEEE80211_AKM_8021X;
+ /* check if we have a cached PMK for this AP */
+ if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN &&
+ (pmk = ieee80211_pmksa_find(ic, ni, NULL)) != NULL) {
+ memcpy(ni->ni_pmkid, pmk->pmk_pmkid,
+ IEEE80211_PMKID_LEN);
+ ni->ni_flags |= IEEE80211_NODE_PMKID;
+ }
+ }
+
+ /* filter out unsupported pairwise ciphers */
+ ni->ni_rsnciphers &= ic->ic_rsnciphers;
+ /* prefer CCMP over TKIP */
+ if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP)
+ ni->ni_rsnciphers = IEEE80211_CIPHER_CCMP;
+ else
+ ni->ni_rsnciphers = IEEE80211_CIPHER_TKIP;
+ ni->ni_rsncipher = ni->ni_rsnciphers;
+
+ /* use MFP if we both support it */
+ if ((ic->ic_caps & IEEE80211_C_MFP) &&
+ (ni->ni_rsncaps & IEEE80211_RSNCAP_MFPC))
+ ni->ni_flags |= IEEE80211_NODE_MFP;
+}
+
int
ieee80211_get_rate(struct ieee80211com *ic)
{
@@ -1150,6 +1183,22 @@ ieee80211_iserp_sta(const struct ieee80211_node *ni)
}
/*
+ * This function is called to notify the 802.1X PACP machine that a new
+ * 802.1X port is enabled and must be authenticated. For 802.11, a port
+ * becomes enabled whenever a STA successfully completes Open System
+ * authentication with an AP.
+ */
+void
+ieee80211_needs_auth(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ /*
+ * XXX this could be done via the route socket of via a dedicated
+ * EAP socket or another kernel->userland notification mechanism.
+ * The notification should include the MAC address (ni_macaddr).
+ */
+}
+
+/*
* Handle a station joining an RSN network.
*/
void
@@ -1176,9 +1225,17 @@ ieee80211_node_join_rsn(struct ieee80211com *ic, struct ieee80211_node *ni)
/* generate a new authenticator nonce (ANonce) */
arc4random_buf(ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
- /* initiate 4-way handshake */
- if (ni->ni_rsnakms == IEEE80211_AKM_PSK)
+ if (!ieee80211_is_8021x_akm(ni->ni_rsnakms)) {
+ memcpy(ni->ni_pmk, ic->ic_psk, IEEE80211_PMK_LEN);
+ ni->ni_flags |= IEEE80211_NODE_PMK;
+ (void)ieee80211_send_4way_msg1(ic, ni);
+ } else if (ni->ni_flags & IEEE80211_NODE_PMK) {
+ /* skip 802.1X auth if a cached PMK was found */
(void)ieee80211_send_4way_msg1(ic, ni);
+ } else {
+ /* no cached PMK found, needs full 802.1X auth */
+ ieee80211_needs_auth(ic, ni);
+ }
}
/*
@@ -1298,6 +1355,7 @@ ieee80211_node_leave_rsn(struct ieee80211com *ic, struct ieee80211_node *ni)
ieee80211_setkeysdone(ic);
ni->ni_flags &= ~IEEE80211_NODE_REKEY;
+ ni->ni_flags &= ~IEEE80211_NODE_PMK;
ni->ni_rsn_gstate = RSNA_IDLE;
timeout_del(&ni->ni_rsn_timeout);
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 65e2f1ed022..cf4c9dca0d2 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_node.h,v 1.32 2008/08/29 12:14:53 damien Exp $ */
+/* $OpenBSD: ieee80211_node.h,v 1.33 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
/*-
@@ -156,6 +156,8 @@ struct ieee80211_node {
u_int16_t ni_rsncaps;
enum ieee80211_cipher ni_rsncipher;
u_int8_t ni_nonce[EAPOL_KEY_NONCE_LEN];
+ u_int8_t ni_pmk[IEEE80211_PMK_LEN];
+ u_int8_t ni_pmkid[IEEE80211_PMKID_LEN];
u_int64_t ni_replaycnt;
u_int8_t ni_replaycnt_ok;
u_int64_t ni_reqreplaycnt;
@@ -177,17 +179,19 @@ struct ieee80211_node {
int ni_txrate; /* index to ni_rates[] */
int ni_state;
- u_int8_t ni_flags; /* special-purpose state */
-#define IEEE80211_NODE_ERP 0x01
-#define IEEE80211_NODE_QOS 0x02
-#define IEEE80211_NODE_REKEY 0x04 /* GTK rekeying in progress */
-#define IEEE80211_NODE_RXPROT 0x08 /* RX protection ON */
-#define IEEE80211_NODE_TXPROT 0x10 /* TX protection ON */
+ u_int16_t ni_flags; /* special-purpose state */
+#define IEEE80211_NODE_ERP 0x0001
+#define IEEE80211_NODE_QOS 0x0002
+#define IEEE80211_NODE_REKEY 0x0004 /* GTK rekeying in progress */
+#define IEEE80211_NODE_RXPROT 0x0008 /* RX protection ON */
+#define IEEE80211_NODE_TXPROT 0x0010 /* TX protection ON */
#define IEEE80211_NODE_TXRXPROT \
(IEEE80211_NODE_TXPROT | IEEE80211_NODE_RXPROT)
-#define IEEE80211_NODE_RXMGMTPROT 0x20 /* RX MMPDU protection ON */
-#define IEEE80211_NODE_TXMGMTPROT 0x40 /* TX MMPDU protection ON */
-#define IEEE80211_NODE_MFP 0x80 /* MFP negotiated */
+#define IEEE80211_NODE_RXMGMTPROT 0x0020 /* RX MMPDU protection ON */
+#define IEEE80211_NODE_TXMGMTPROT 0x0040 /* TX MMPDU protection ON */
+#define IEEE80211_NODE_MFP 0x0080 /* MFP negotiated */
+#define IEEE80211_NODE_PMK 0x0100 /* ni_pmk set */
+#define IEEE80211_NODE_PMKID 0x0200 /* ni_pmkid set */
};
RB_HEAD(ieee80211_tree, ieee80211_node);
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index b508e606597..bda6a3cb115 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_output.c,v 1.78 2008/09/27 15:00:08 damien Exp $ */
+/* $OpenBSD: ieee80211_output.c,v 1.79 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */
/*-
@@ -919,12 +919,12 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic,
*frm++ = 2;
count++;
}
- if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X) {
+ if (!wpa && (ni->ni_rsnakms & IEEE80211_AKM_SHA256_8021X)) {
memcpy(frm, oui, 3); frm += 3;
*frm++ = 5;
count++;
}
- if (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK) {
+ if (!wpa && (ni->ni_rsnakms & IEEE80211_AKM_SHA256_PSK)) {
memcpy(frm, oui, 3); frm += 3;
*frm++ = 6;
count++;
@@ -938,12 +938,20 @@ ieee80211_add_rsn_body(u_int8_t *frm, struct ieee80211com *ic,
/* write RSN Capabilities field */
LE_WRITE_2(frm, ni->ni_rsncaps); frm += 2;
+ if (ni->ni_flags & IEEE80211_NODE_PMKID) {
+ /* write PMKID Count field */
+ LE_WRITE_2(frm, 1); frm += 2;
+ /* write PMKID List (only 1) */
+ memcpy(frm, ni->ni_pmkid, IEEE80211_PMKID_LEN);
+ frm += IEEE80211_PMKID_LEN;
+ } else {
+ /* no PMKID (PMKID Count=0) */
+ LE_WRITE_2(frm, 0); frm += 2;
+ }
+
if (!(ic->ic_caps & IEEE80211_C_MFP))
return frm;
- /* no PMKID List for now */
- LE_WRITE_2(frm, 0); frm += 2;
-
/* write Group Integrity Cipher Suite field */
memcpy(frm, oui, 3); frm += 3;
switch (ic->ic_rsngroupmgmtcipher) {
diff --git a/sys/net80211/ieee80211_pae_input.c b/sys/net80211/ieee80211_pae_input.c
index 0577d360db6..9bff593eca9 100644
--- a/sys/net80211/ieee80211_pae_input.c
+++ b/sys/net80211/ieee80211_pae_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_pae_input.c,v 1.12 2008/08/27 09:05:04 damien Exp $ */
+/* $OpenBSD: ieee80211_pae_input.c,v 1.13 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -131,8 +131,7 @@ ieee80211_eapol_key_input(struct ieee80211com *ic, struct mbuf *m0,
if (desc < EAPOL_KEY_DESC_V1 || desc > EAPOL_KEY_DESC_V3)
goto done;
- if (ni->ni_rsnakms == IEEE80211_AKM_SHA256_8021X ||
- ni->ni_rsnakms == IEEE80211_AKM_SHA256_PSK) {
+ if (ieee80211_is_sha256_akm(ni->ni_rsnakms)) {
if (desc != EAPOL_KEY_DESC_V3)
goto done;
} else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
@@ -193,9 +192,9 @@ ieee80211_recv_4way_msg1(struct ieee80211com *ic,
struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
{
struct ieee80211_ptk tptk;
+ struct ieee80211_pmk *pmk;
const u_int8_t *frm, *efrm;
const u_int8_t *pmkid;
- const u_int8_t *pmk;
#ifndef IEEE80211_STA_ONLY
if (ic->ic_opmode != IEEE80211_M_STA &&
@@ -232,22 +231,31 @@ ieee80211_recv_4way_msg1(struct ieee80211com *ic,
frm += 2 + frm[1];
}
/* check that the PMKID KDE is valid (if present) */
- if (pmkid != NULL && pmkid[1] < 4 + 16)
+ if (pmkid != NULL && pmkid[1] != 4 + 16)
return;
- /* retrieve PMK */
- if ((pmk = ieee80211_get_pmk(ic, ni, &pmkid[6])) == NULL) {
- /* no PMK configured for this STA/PMKID */
- return;
- }
+ if (ieee80211_is_8021x_akm(ni->ni_rsnakms)) {
+ /* retrieve the PMK for this (AP,PMKID) */
+ pmk = ieee80211_pmksa_find(ic, ni,
+ (pmkid != NULL) ? &pmkid[6] : NULL);
+ if (pmk == NULL) {
+ DPRINTF(("no PMK available for %s\n",
+ ether_sprintf(ni->ni_macaddr)));
+ return;
+ }
+ memcpy(ni->ni_pmk, pmk->pmk_key, IEEE80211_PMK_LEN);
+ } else /* use pre-shared key */
+ memcpy(ni->ni_pmk, ic->ic_psk, IEEE80211_PMK_LEN);
+ ni->ni_flags |= IEEE80211_NODE_PMK;
+
/* save authenticator's nonce (ANonce) */
memcpy(ni->ni_nonce, key->nonce, EAPOL_KEY_NONCE_LEN);
/* generate supplicant's nonce (SNonce) */
arc4random_buf(ic->ic_nonce, EAPOL_KEY_NONCE_LEN);
- /* derive TPTK */
- ieee80211_derive_ptk(ni->ni_rsnakms, pmk, ni->ni_macaddr,
+ /* TPTK = CalcPTK(PMK, ANonce, SNonce) */
+ ieee80211_derive_ptk(ni->ni_rsnakms, ni->ni_pmk, ni->ni_macaddr,
ic->ic_myaddr, ni->ni_nonce, ic->ic_nonce, &tptk);
if (ic->ic_if.if_flags & IFF_DEBUG)
@@ -269,7 +277,6 @@ ieee80211_recv_4way_msg2(struct ieee80211com *ic,
const u_int8_t *rsnie)
{
struct ieee80211_ptk tptk;
- const u_int8_t *pmk;
if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
ic->ic_opmode != IEEE80211_M_IBSS)
@@ -283,14 +290,10 @@ ieee80211_recv_4way_msg2(struct ieee80211com *ic,
}
ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING;
- /* replay counter has already been verified by caller */
+ /* NB: replay counter has already been verified by caller */
- /* retrieve PMK and derive TPTK */
- if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) {
- /* no PMK configured for this STA */
- return; /* will timeout.. */
- }
- ieee80211_derive_ptk(ni->ni_rsnakms, pmk, ic->ic_myaddr,
+ /* PTK = CalcPTK(ANonce, SNonce) */
+ ieee80211_derive_ptk(ni->ni_rsnakms, ni->ni_pmk, ic->ic_myaddr,
ni->ni_macaddr, ni->ni_nonce, key->nonce, &tptk);
/* check Key MIC field using KCK */
@@ -340,7 +343,6 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
struct ieee80211_key *k;
const u_int8_t *frm, *efrm;
const u_int8_t *rsnie1, *rsnie2, *gtk, *igtk;
- const u_int8_t *pmk;
u_int16_t info, reason = 0;
int keylen;
@@ -354,18 +356,19 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
ic->ic_stats.is_rx_eapol_replay++;
return;
}
-
- /* check that ANonce matches that of message 1 */
- if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0) {
- DPRINTF(("ANonce does not match msg 1/4\n"));
+ /* make sure that a PMK as been selected */
+ if (!(ni->ni_flags & IEEE80211_NODE_PMK)) {
+ DPRINTF(("no PMK found for %s\n",
+ ether_sprintf(ni->ni_macaddr)));
return;
}
- /* retrieve PMK and derive TPTK */
- if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) {
- /* no PMK configured for this STA */
+ /* check that ANonce matches that of Message 1 */
+ if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0) {
+ DPRINTF(("ANonce does not match msg 1/4\n"));
return;
}
- ieee80211_derive_ptk(ni->ni_rsnakms, pmk, ni->ni_macaddr,
+ /* TPTK = CalcPTK(PMK, ANonce, SNonce) */
+ ieee80211_derive_ptk(ni->ni_rsnakms, ni->ni_pmk, ni->ni_macaddr,
ic->ic_myaddr, key->nonce, ic->ic_nonce, &tptk);
info = BE_READ_2(key->info);
@@ -451,6 +454,12 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
DPRINTF(("IGTK KDE found but GTK KDE missing\n"));
return;
}
+ /* check that the Install bit is set if using pairwise keys */
+ if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP &&
+ !(info & EAPOL_KEY_INSTALL)) {
+ DPRINTF(("pairwise cipher but !Install\n"));
+ return;
+ }
/*
* Check that first WPA/RSN IE is identical to the one received in
@@ -496,7 +505,7 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic,
if (ieee80211_send_4way_msg4(ic, ni) != 0)
return; /* ..authenticator will retry */
- if (info & EAPOL_KEY_INSTALL) {
+ if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) {
u_int64_t prsc;
/* check that key length matches that of pairwise cipher */
@@ -619,7 +628,7 @@ ieee80211_recv_4way_msg4(struct ieee80211com *ic,
return;
}
- /* replay counter has already been verified by caller */
+ /* NB: replay counter has already been verified by caller */
/* check Key MIC field using KCK */
if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) {
diff --git a/sys/net80211/ieee80211_pae_output.c b/sys/net80211/ieee80211_pae_output.c
index cf5f4af2614..b6f27393d5b 100644
--- a/sys/net80211/ieee80211_pae_output.c
+++ b/sys/net80211/ieee80211_pae_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_pae_output.c,v 1.13 2008/08/27 09:05:04 damien Exp $ */
+/* $OpenBSD: ieee80211_pae_output.c,v 1.14 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -91,8 +91,7 @@ ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m,
info = BE_READ_2(key->info);
/* use V3 descriptor if KDF is SHA256-based */
- if (ni->ni_rsnakms == IEEE80211_AKM_SHA256_8021X ||
- ni->ni_rsnakms == IEEE80211_AKM_SHA256_PSK)
+ if (ieee80211_is_sha256_akm(ni->ni_rsnakms))
info |= EAPOL_KEY_DESC_V3;
/* use V2 descriptor if pairwise or group cipher is CCMP */
else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
@@ -296,13 +295,10 @@ ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
BE_WRITE_2(key->keylen, keylen);
frm = (u_int8_t *)&key[1];
- /* WPA does not have PMKID KDE */
+ /* NB: WPA does not have PMKID KDE */
if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN &&
- (ni->ni_rsnakms == IEEE80211_AKM_8021X ||
- ni->ni_rsnakms == IEEE80211_AKM_SHA256_8021X)) {
- /* XXX retrieve PMKID from the PMKSA cache */
- /* frm = ieee80211_add_pmkid_kde(frm, pmkid); */
- }
+ ieee80211_is_8021x_akm(ni->ni_rsnakms))
+ frm = ieee80211_add_pmkid_kde(frm, ni->ni_pmkid);
m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
diff --git a/sys/net80211/ieee80211_priv.h b/sys/net80211/ieee80211_priv.h
index 29e1072faa2..f1dab4ec26c 100644
--- a/sys/net80211/ieee80211_priv.h
+++ b/sys/net80211/ieee80211_priv.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_priv.h,v 1.3 2008/08/12 19:05:39 damien Exp $ */
+/* $OpenBSD: ieee80211_priv.h,v 1.4 2008/09/27 15:16:09 damien Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -59,6 +59,18 @@ extern int ieee80211_debug;
2 + /* AKM Suite List Count */ \
4 * 2) /* AKM Suite List (max 2) */
+struct ieee80211_rsnparams {
+ u_int16_t rsn_nakms;
+ u_int32_t rsn_akms;
+ u_int16_t rsn_nciphers;
+ u_int32_t rsn_ciphers;
+ enum ieee80211_cipher rsn_groupcipher;
+ enum ieee80211_cipher rsn_groupmgmtcipher;
+ u_int16_t rsn_caps;
+ u_int8_t rsn_npmkids;
+ const u_int8_t *rsn_pmkids;
+};
+
/* unaligned big endian access */
#define BE_READ_2(p) \
((u_int16_t) \
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 34d91fbdad7..30107f4dad4 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_proto.c,v 1.35 2008/08/29 12:14:53 damien Exp $ */
+/* $OpenBSD: ieee80211_proto.c,v 1.36 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
/*-
@@ -364,6 +364,59 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int on)
ic->ic_updateslot(ic);
}
+/*
+ * This function is called by the 802.1X PACP machine (via an ioctl) when
+ * the transmit key machine (4-Way Handshake for 802.11) should run.
+ */
+int
+ieee80211_keyrun(struct ieee80211com *ic, u_int8_t *macaddr)
+{
+#ifndef IEEE80211_STA_ONLY
+ struct ieee80211_node *ni;
+ struct ieee80211_pmk *pmk;
+#endif
+
+ /* STA must be associated or AP must be ready */
+ if (ic->ic_state != IEEE80211_S_RUN ||
+ !(ic->ic_flags & IEEE80211_F_RSNON))
+ return ENETDOWN;
+
+#ifndef IEEE80211_STA_ONLY
+ if (ic->ic_opmode == IEEE80211_M_STA)
+#endif
+ return 0; /* supplicant only, do nothing */
+
+#ifndef IEEE80211_STA_ONLY
+ /* find the STA with which we must start the key exchange */
+ if ((ni = ieee80211_find_node(ic, macaddr)) == NULL) {
+ DPRINTF(("no node found for %s\n", ether_sprintf(macaddr)));
+ return EINVAL;
+ }
+ /* check that the STA is in the correct state */
+ if (ni->ni_state != IEEE80211_STA_ASSOC ||
+ ni->ni_rsn_state != RSNA_AUTHENTICATION_2) {
+ DPRINTF(("unexpected in state %d\n", ni->ni_rsn_state));
+ return EINVAL;
+ }
+ ni->ni_rsn_state = RSNA_INITPMK;
+
+ /* make sure a PMK is available for this STA, otherwise deauth it */
+ if ((pmk = ieee80211_pmksa_find(ic, ni, NULL)) == NULL) {
+ DPRINTF(("no PMK available for %s\n", ether_sprintf(macaddr)));
+ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ ieee80211_node_leave(ic, ni);
+ return EINVAL;
+ }
+ memcpy(ni->ni_pmk, pmk->pmk_key, IEEE80211_PMK_LEN);
+ memcpy(ni->ni_pmkid, pmk->pmk_pmkid, IEEE80211_PMKID_LEN);
+ ni->ni_flags |= IEEE80211_NODE_PMK;
+
+ /* initiate key exchange (4-Way Handshake) with STA */
+ return ieee80211_send_4way_msg1(ic, ni);
+#endif /* IEEE80211_STA_ONLY */
+}
+
#ifndef IEEE80211_STA_ONLY
/*
* Initiate a group key handshake with a node.
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index b5a7de31267..45123db4414 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_var.h,v 1.53 2008/09/08 13:13:01 jsg Exp $ */
+/* $OpenBSD: ieee80211_var.h,v 1.54 2008/09/27 15:16:09 damien Exp $ */
/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
/*-
@@ -171,16 +171,6 @@ struct ieee80211_edca_ac_params {
#define IEEE80211_PROTO_RSN (1 << 0)
#define IEEE80211_PROTO_WPA (1 << 1)
-struct ieee80211_rsnparams {
- u_int16_t rsn_nakms;
- u_int32_t rsn_akms;
- u_int16_t rsn_nciphers;
- u_int32_t rsn_ciphers;
- enum ieee80211_cipher rsn_groupcipher;
- enum ieee80211_cipher rsn_groupmgmtcipher;
- u_int16_t rsn_caps;
-};
-
struct ieee80211_rxinfo {
u_int32_t rxi_flags;
u_int32_t rxi_tstamp;
@@ -288,6 +278,7 @@ struct ieee80211com {
u_int16_t ic_rsn_keydonesta;
int ic_tkip_micfail;
+ TAILQ_HEAD(, ieee80211_pmk) ic_pmksa; /* PMKSA cache */
u_int ic_rsnprotos;
u_int ic_rsnakms;
u_int ic_rsnciphers;