diff options
Diffstat (limited to 'sys/net80211')
-rw-r--r-- | sys/net80211/ieee80211_crypto.c | 148 | ||||
-rw-r--r-- | sys/net80211/ieee80211_crypto.h | 41 | ||||
-rw-r--r-- | sys/net80211/ieee80211_input.c | 37 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 36 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.h | 17 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 136 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 24 | ||||
-rw-r--r-- | sys/net80211/ieee80211_output.c | 20 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_input.c | 71 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_output.c | 14 | ||||
-rw-r--r-- | sys/net80211/ieee80211_priv.h | 14 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 55 | ||||
-rw-r--r-- | sys/net80211/ieee80211_var.h | 13 |
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; |