#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/errno.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_llc.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_priv.h>
int ieee80211_send_eapol_key(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, const struct ieee80211_ptk *);
#ifndef IEEE80211_STA_ONLY
u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, struct ieee80211_node *,
const struct ieee80211_key *);
u_int8_t *ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *);
u_int8_t *ieee80211_add_igtk_kde(u_int8_t *,
const struct ieee80211_key *);
#endif
struct mbuf *ieee80211_get_eapol_key(int, int, u_int);
int
ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m,
struct ieee80211_node *ni, const struct ieee80211_ptk *ptk)
{
struct ifnet *ifp = &ic->ic_if;
struct ether_header *eh;
struct ieee80211_eapol_key *key;
u_int16_t info;
int len, error;
M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
if (m == NULL)
return ENOMEM;
eh = mtod(m, struct ether_header *);
eh->ether_type = htons(ETHERTYPE_EAPOL);
IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr);
IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr);
key = (struct ieee80211_eapol_key *)&eh[1];
key->version = EAPOL_VERSION;
key->type = EAPOL_KEY;
key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ?
EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA;
info = BE_READ_2(key->info);
if (ieee80211_is_sha256_akm(ni->ni_rsnakms))
info |= EAPOL_KEY_DESC_V3;
else if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP ||
ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP)
info |= EAPOL_KEY_DESC_V2;
else
info |= EAPOL_KEY_DESC_V1;
BE_WRITE_2(key->info, info);
len = m->m_len - sizeof(struct ether_header);
BE_WRITE_2(key->paylen, len - sizeof(*key));
BE_WRITE_2(key->len, len - 4);
#ifndef IEEE80211_STA_ONLY
if (info & EAPOL_KEY_ENCRYPTED) {
if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
info &= ~EAPOL_KEY_ENCRYPTED;
BE_WRITE_2(key->info, info);
}
ieee80211_eapol_key_encrypt(ic, key, ptk->kek);
if ((info & EAPOL_KEY_VERSION_MASK) != EAPOL_KEY_DESC_V1) {
m->m_pkthdr.len = m->m_len =
sizeof(*eh) + 4 + BE_READ_2(key->len);
}
}
#endif
if (info & EAPOL_KEY_KEYMIC)
ieee80211_eapol_key_mic(key, ptk->kck);
#ifndef IEEE80211_STA_ONLY
if (info & EAPOL_KEY_KEYACK)
timeout_add_msec(&ni->ni_eapol_to, 100);
#endif
error = ifq_enqueue(&ifp->if_snd, m);
if (error)
return (error);
if_start(ifp);
return 0;
}
#ifndef IEEE80211_STA_ONLY
void
ieee80211_eapol_timeout(void *arg)
{
struct ieee80211_node *ni = arg;
struct ieee80211com *ic = ni->ni_ic;
int s;
DPRINTF(("no answer from station %s in state %d\n",
ether_sprintf(ni->ni_macaddr), ni->ni_rsn_state));
s = splnet();
switch (ni->ni_rsn_state) {
case RSNA_PTKSTART:
case RSNA_PTKCALCNEGOTIATING:
(void)ieee80211_send_4way_msg1(ic, ni);
break;
case RSNA_PTKINITNEGOTIATING:
(void)ieee80211_send_4way_msg3(ic, ni);
break;
}
switch (ni->ni_rsn_gstate) {
case RSNA_REKEYNEGOTIATING:
(void)ieee80211_send_group_msg1(ic, ni);
break;
}
splx(s);
}
u_int8_t *
ieee80211_add_gtk_kde(u_int8_t *frm, struct ieee80211_node *ni,
const struct ieee80211_key *k)
{
KASSERT(k->k_flags & IEEE80211_KEY_GROUP);
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 6 + k->k_len;
memcpy(frm, IEEE80211_OUI, 3); frm += 3;
*frm++ = IEEE80211_KDE_GTK;
*frm = k->k_id & 3;
if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP)
*frm |= 1 << 2;
frm++;
*frm++ = 0;
memcpy(frm, k->k_key, k->k_len);
return frm + k->k_len;
}
u_int8_t *
ieee80211_add_pmkid_kde(u_int8_t *frm, const u_int8_t *pmkid)
{
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 20;
memcpy(frm, IEEE80211_OUI, 3); frm += 3;
*frm++ = IEEE80211_KDE_PMKID;
memcpy(frm, pmkid, IEEE80211_PMKID_LEN);
return frm + IEEE80211_PMKID_LEN;
}
u_int8_t *
ieee80211_add_igtk_kde(u_int8_t *frm, const struct ieee80211_key *k)
{
KASSERT(k->k_flags & IEEE80211_KEY_IGTK);
*frm++ = IEEE80211_ELEMID_VENDOR;
*frm++ = 4 + 24;
memcpy(frm, IEEE80211_OUI, 3); frm += 3;
*frm++ = IEEE80211_KDE_IGTK;
LE_WRITE_2(frm, k->k_id); frm += 2;
LE_WRITE_6(frm, k->k_tsc); frm += 6;
memcpy(frm, k->k_key, 16);
return frm + 16;
}
#endif
struct mbuf *
ieee80211_get_eapol_key(int flags, int type, u_int pktlen)
{
struct mbuf *m;
pktlen += sizeof(struct ieee80211_frame) + LLC_SNAPFRAMELEN +
sizeof(struct ieee80211_eapol_key);
if (pktlen > MCLBYTES)
panic("EAPOL-Key frame too large: %u", pktlen);
MGETHDR(m, flags, type);
if (m == NULL)
return NULL;
if (pktlen > MHLEN) {
MCLGET(m, flags);
if (!(m->m_flags & M_EXT))
return m_free(m);
}
m->m_data += sizeof(struct ieee80211_frame) + LLC_SNAPFRAMELEN;
return m;
}
#ifndef IEEE80211_STA_ONLY
int
ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
{
struct ieee80211_eapol_key *key;
struct mbuf *m;
u_int16_t info, keylen;
u_int8_t *frm;
ni->ni_rsn_state = RSNA_PTKSTART;
if (++ni->ni_rsn_retries > 3) {
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_4WAY_TIMEOUT);
ieee80211_node_leave(ic, ni);
return 0;
}
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
(ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? 2 + 20 : 0);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK;
BE_WRITE_2(key->info, info);
memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
BE_WRITE_2(key->keylen, keylen);
frm = (u_int8_t *)&key[1];
if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN &&
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;
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 1, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
ni->ni_replaycnt++;
BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
return ieee80211_send_eapol_key(ic, m, ni, NULL);
}
#endif
int
ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni,
const u_int8_t *replaycnt, const struct ieee80211_ptk *tptk)
{
struct ieee80211_eapol_key *key;
struct mbuf *m;
u_int16_t info;
u_int8_t *frm;
ni->ni_rsn_supp_state = RSNA_SUPP_PTKNEGOTIATING;
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
(ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ?
2 + IEEE80211_WPAIE_MAXLEN :
2 + IEEE80211_RSNIE_MAXLEN);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC;
BE_WRITE_2(key->info, info);
memcpy(key->replaycnt, replaycnt, 8);
memcpy(key->nonce, ic->ic_nonce, EAPOL_KEY_NONCE_LEN);
frm = (u_int8_t *)&key[1];
if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
int keylen;
frm = ieee80211_add_wpa(frm, ic, ni);
keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
BE_WRITE_2(key->keylen, keylen);
} else
frm = ieee80211_add_rsn(frm, ic, ni);
m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 2, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, tptk);
}
#ifndef IEEE80211_STA_ONLY
int
ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni)
{
struct ieee80211_eapol_key *key;
struct ieee80211_key *k = NULL;
struct mbuf *m;
u_int16_t info, keylen;
u_int8_t *frm;
ni->ni_rsn_state = RSNA_PTKINITNEGOTIATING;
if (++ni->ni_rsn_retries > 3) {
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_4WAY_TIMEOUT);
ieee80211_node_leave(ic, ni);
return 0;
}
if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
k = &ic->ic_nw_keys[ic->ic_def_txkey];
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
2 + IEEE80211_RSNIE_MAXLEN + 2 + 6 + k->k_len + 15 +
((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0));
} else {
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
2 + IEEE80211_WPAIE_MAXLEN +
((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0));
}
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC;
if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP)
info |= EAPOL_KEY_INSTALL;
memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
ni->ni_replaycnt++;
BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
BE_WRITE_2(key->keylen, keylen);
frm = (u_int8_t *)&key[1];
if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) {
frm = ieee80211_add_rsn(frm, ic, ic->ic_bss);
frm = ieee80211_add_gtk_kde(frm, ni, k);
LE_WRITE_6(key->rsc, k->k_tsc);
if (ni->ni_flags & IEEE80211_NODE_MFP) {
frm = ieee80211_add_igtk_kde(frm,
&ic->ic_nw_keys[ic->ic_igtk_kid]);
}
info |= EAPOL_KEY_ENCRYPTED | EAPOL_KEY_SECURE;
} else
frm = ieee80211_add_wpa(frm, ic, ic->ic_bss);
BE_WRITE_2(key->info, info);
m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 3, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
#endif
int
ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni)
{
struct ieee80211_eapol_key *key;
struct mbuf *m;
u_int16_t info;
ni->ni_rsn_supp_state = RSNA_SUPP_PTKDONE;
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC;
BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
int keylen;
keylen = ieee80211_cipher_keylen(ni->ni_rsncipher);
BE_WRITE_2(key->keylen, keylen);
} else
info |= EAPOL_KEY_SECURE;
BE_WRITE_2(key->info, info);
m->m_pkthdr.len = m->m_len = sizeof(*key);
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 4, 4, "4-way",
ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
#ifndef IEEE80211_STA_ONLY
int
ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
{
struct ieee80211_eapol_key *key;
const struct ieee80211_key *k;
struct mbuf *m;
u_int16_t info;
u_int8_t *frm;
u_int8_t kid;
ni->ni_rsn_gstate = RSNA_REKEYNEGOTIATING;
if (++ni->ni_rsn_retries > 3) {
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
IEEE80211_REASON_GROUP_TIMEOUT);
ieee80211_node_leave(ic, ni);
return 0;
}
if (ni->ni_flags & IEEE80211_NODE_REKEY)
kid = (ic->ic_def_txkey == 1) ? 2 : 1;
else
kid = ic->ic_def_txkey;
k = &ic->ic_nw_keys[kid];
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
((ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ?
k->k_len : 2 + 6 + k->k_len) +
((ni->ni_flags & IEEE80211_NODE_MFP) ? 2 + 28 : 0) +
15);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE |
EAPOL_KEY_ENCRYPTED;
ni->ni_replaycnt++;
BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
frm = (u_int8_t *)&key[1];
if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
BE_WRITE_2(key->keylen, k->k_len);
memcpy(frm, k->k_key, k->k_len);
frm += k->k_len;
info |= (k->k_id & 0x3) << EAPOL_KEY_WPA_KID_SHIFT;
if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP)
info |= EAPOL_KEY_WPA_TX;
} else {
frm = ieee80211_add_gtk_kde(frm, ni, k);
if (ni->ni_flags & IEEE80211_NODE_MFP) {
if (ni->ni_flags & IEEE80211_NODE_REKEY)
kid = (ic->ic_igtk_kid == 4) ? 5 : 4;
else
kid = ic->ic_igtk_kid;
frm = ieee80211_add_igtk_kde(frm,
&ic->ic_nw_keys[kid]);
}
}
LE_WRITE_6(key->rsc, k->k_tsc);
BE_WRITE_2(key->info, info);
m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 1, 2, "group key",
ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
#endif
int
ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni,
const struct ieee80211_key *k)
{
struct ieee80211_eapol_key *key;
u_int16_t info;
struct mbuf *m;
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info = EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE;
BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) {
BE_WRITE_2(key->keylen, k->k_len);
info |= (k->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT;
}
BE_WRITE_2(key->info, info);
m->m_pkthdr.len = m->m_len = sizeof(*key);
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending msg %d/%d of the %s handshake to %s\n",
ic->ic_if.if_xname, 2, 2, "group key",
ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}
int
ieee80211_send_eapol_key_req(struct ieee80211com *ic,
struct ieee80211_node *ni, u_int16_t info, u_int64_t tsc)
{
struct ieee80211_eapol_key *key;
struct mbuf *m;
m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0);
if (m == NULL)
return ENOMEM;
key = mtod(m, struct ieee80211_eapol_key *);
memset(key, 0, sizeof(*key));
info |= EAPOL_KEY_REQUEST;
BE_WRITE_2(key->info, info);
if (info & EAPOL_KEY_ERROR)
LE_WRITE_6(key->rsc, tsc);
BE_WRITE_8(key->replaycnt, ni->ni_reqreplaycnt);
ni->ni_reqreplaycnt++;
m->m_pkthdr.len = m->m_len = sizeof(*key);
if (ic->ic_if.if_flags & IFF_DEBUG)
printf("%s: sending EAPOL-Key request to %s\n",
ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk);
}