summaryrefslogtreecommitdiff
path: root/sys/net80211
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net80211')
-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;