wpa.c revision d62bc4badc1c1f1549c961cfb8b420e650e1272b
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
* Sun elects to license this software under the BSD license.
* See README for more details.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/ethernet.h>
#include <fcntl.h>
#include <unistd.h>
#include "wpa_impl.h"
#include "wpa_enc.h"
#include "driver.h"
#include "eloop.h"
#include "l2_packet.h"
static void pmksa_cache_set_expiration(struct wpa_supplicant *);
/*
*/
static const int WPA_SELECTOR_LEN = 4;
static const uint8_t
static const uint8_t
/*
* WPA IE version 1
* 00-50-f2:1 (OUI:OUI type)
* 0x01 0x00 (version; little endian)
* (all following fields are optional:)
* Group Suite Selector (4 octets) (default: TKIP)
* Pairwise Suite Count (2 octets, little endian) (default: 1)
* Pairwise Suite List (4 * n octets) (default: TKIP)
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
* (default: unspec 802.1x)
* WPA Capabilities (2 octets, little endian) (default: 0)
*/
#pragma pack(1)
struct wpa_ie_hdr {
};
#pragma pack()
/*
*/
static const int RSN_SELECTOR_LEN = 4;
static const uint8_t
static const uint8_t
/*
* EAPOL-Key Key Data Encapsulation
* GroupKey and STAKey require encryption, otherwise, encryption is optional.
*/
/*
* 1/4: PMKID
* 2/4: RSN IE
* 3/4: one or two RSN IEs + GTK IE (encrypted)
* 4/4: empty
* 1/2: GTK IE (encrypted)
* 2/2: empty
*/
/*
* RSN IE version 1
* 0x01 0x00 (version; little endian)
* (all following fields are optional:)
* Group Suite Selector (4 octets) (default: CCMP)
* Pairwise Suite Count (2 octets, little endian) (default: 1)
* Pairwise Suite List (4 * n octets) (default: CCMP)
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
* (default: unspec 802.1x)
* RSN Capabilities (2 octets, little endian) (default: 0)
* PMKID Count (2 octets) (default: 0)
* PMKID List (16 * n octets)
*/
#pragma pack(1)
struct rsn_ie_hdr {
};
#pragma pack()
static int
{
int fd;
if (fd == -1) {
return (-1);
}
while (resid != 0) {
}
return (0);
}
static void
{
while (pos >= 0) {
break;
pos--;
}
}
static int
{
return (WPA_CIPHER_NONE);
return (WPA_CIPHER_WEP40);
return (WPA_CIPHER_TKIP);
return (WPA_CIPHER_CCMP);
return (WPA_CIPHER_WEP104);
return (0);
}
static int
{
return (WPA_KEY_MGMT_IEEE8021X);
0)
return (WPA_KEY_MGMT_PSK);
return (0);
}
static int
{
return (WPA_CIPHER_NONE);
return (WPA_CIPHER_WEP40);
return (WPA_CIPHER_TKIP);
return (WPA_CIPHER_CCMP);
return (WPA_CIPHER_WEP104);
return (0);
}
static int
{
return (WPA_KEY_MGMT_IEEE8021X);
0)
return (WPA_KEY_MGMT_PSK);
return (0);
}
static void
struct rsn_pmksa_cache *entry)
{
wpa_s->pmksa_count--;
}
}
/* ARGSUSED */
static void
{
}
}
static void
{
int sec;
return;
if (sec < 0)
sec = 0;
}
void
{
while (entry) {
}
}
struct rsn_pmksa_cache *
{
while (entry) {
return (entry);
}
return (NULL);
}
int
{
int i, j;
struct rsn_pmksa_cache *entry;
"Index / AA / PMKID / expiration (in seconds)\n");
i = 0;
while (entry) {
i++;
for (j = 0; j < PMKID_LEN; j++)
}
}
void
{
while (entry) {
}
}
/* ARGSUSED */
static int
{
struct wpa_ie_hdr *hdr;
int left;
int i, count;
data->capabilities = 0;
if (wpa_ie_len == 0) {
/* No WPA IE - fail silently */
return (-1);
}
if (wpa_ie_len < sizeof (struct wpa_ie_hdr)) {
"wpa_parse_wpa_ie_wpa", wpa_ie_len);
return (-1);
}
"wpa_parse_wpa_ie_wpa");
return (-1);
}
if (left >= WPA_SELECTOR_LEN) {
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
} else if (left > 0) {
"wpa_parse_wpa_ie_wpa", left);
return (-1);
}
if (left >= 2) {
data->pairwise_cipher = 0;
pos += 2;
left -= 2;
"count %u left %u",
return (-1);
}
for (i = 0; i < count; i++) {
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
}
} else if (left == 1) {
"wpa_parse_wpa_ie_wpa");
return (-1);
}
if (left >= 2) {
pos += 2;
left -= 2;
"count %u left %u",
return (-1);
}
for (i = 0; i < count; i++) {
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
}
} else if (left == 1) {
"wpa_parse_wpa_ie_wpa");
return (-1);
}
if (left >= 2) {
pos += 2;
left -= 2;
}
if (left > 0) {
"wpa_parse_wpa_ie_wpa", left);
return (-1);
}
return (0);
}
/* ARGSUSED */
static int
{
struct rsn_ie_hdr *hdr;
int left;
int i, count;
data->capabilities = 0;
if (rsn_ie_len == 0) {
/* No RSN IE - fail silently */
return (-1);
}
if (rsn_ie_len < sizeof (struct rsn_ie_hdr)) {
"wpa_parse_wpa_ie_rsn", rsn_ie_len);
return (-1);
}
"wpa_parse_wpa_ie_rsn");
return (-1);
}
if (left >= RSN_SELECTOR_LEN) {
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
} else if (left > 0) {
"wpa_parse_wpa_ie_rsn", left);
return (-1);
}
if (left >= 2) {
data->pairwise_cipher = 0;
pos += 2;
left -= 2;
"count %u left %u",
return (-1);
}
for (i = 0; i < count; i++) {
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
} else if (left == 1) {
"wpa_parse_wpa_ie_rsn");
return (-1);
}
if (left >= 2) {
pos += 2;
left -= 2;
"count %u left %u",
return (-1);
}
for (i = 0; i < count; i++) {
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
} else if (left == 1) {
"wpa_parse_wpa_ie_rsn");
return (-1);
}
if (left >= 2) {
pos += 2;
left -= 2;
}
if (left > 0) {
/*
* RSN IE could include PMKID data, but Authenticator should
* never include it, so no need to parse it in the Supplicant.
*/
"wpa_parse_wpa_ie_rsn", left);
}
return (0);
}
int
{
else
}
static int
{
struct wpa_ie_hdr *hdr;
} else {
return (-1);
}
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
} else {
return (-1);
}
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
} else {
return (-1);
}
pos += WPA_SELECTOR_LEN;
/*
* WPA Capabilities; use defaults, so no need to include it
*/
}
static int
{
struct rsn_ie_hdr *hdr;
} else {
return (-1);
}
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
} else {
return (-1);
}
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
} else {
return (-1);
}
pos += RSN_SELECTOR_LEN;
/* RSN Capabilities */
*pos++ = 0;
*pos++ = 0;
/* PMKID Count (2 octets, little endian) */
*pos++ = 1;
*pos++ = 0;
/* PMKID */
}
}
int
{
else
}
static void
{
/*
* PTK = PRF-X(PMK, "Pairwise key expansion",
* Min(AA, SA) || Max(AA, SA) ||
* Min(ANonce, SNonce) || Max(ANonce, SNonce))
*/
} else {
}
} else {
}
}
struct wpa_ssid *
{
int ssid_len;
if (ssid_len < 0) {
return (NULL);
}
return (NULL);
}
" driver len=%d ssid=%s",
return (entry);
return (NULL);
}
static void
{
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
}
}
void
{
int rlen;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *reply;
unsigned char *rbuf;
else
"request");
return;
}
return;
if (error)
if (pairwise)
reply->key_length = 0;
if (key_info & WPA_KEY_INFO_MIC) {
}
"pairwise=%d ptk_set=%d len=%d)",
}
static void
{
int rlen;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *reply;
unsigned char *rbuf;
int wpa_ie_len;
"WPA: No SSID info found (msg 1 of 4).");
return;
}
/* RSN: msg 1/4 should contain PMKID for the selected PMK */
"underflow (ie=%d len=%d)",
break;
}
if (pos[0] == GENERIC_INFO_ELEM &&
RSN_SELECTOR_LEN) == 0) {
break;
break;
}
}
wpa_ie = wpa_ie_buf;
if (wpa_ie_len < 0) {
"WPA IE (for msg 2 of 4).");
return;
}
return;
if (wpa_s->renew_snonce) {
"random data for SNonce");
return;
}
wpa_s->renew_snonce = 0;
}
/*
*/
}
static void
{
/*
* Ignore Tx bit in GTK IE if a pairwise key is used.
* One AP seemed to set this bit (incorrectly, since Tx
* is only when doing Group Key only APs) and without
* this workaround, the data connection does not work
* because wpa_supplicant configured non-zero keyidx to
* be used for unicast.
*/
"pairwise keys are used - ignore Tx bit");
tx = 0;
}
gtk += 2;
gtk_len -= 2;
switch (wpa_s->group_cipher) {
case WPA_CIPHER_CCMP:
if (gtk_len != 16) {
" Group Cipher key length %d.", gtk_len);
return;
}
key_rsc_len = 6;
alg = WPA_ALG_CCMP;
break;
case WPA_CIPHER_TKIP:
if (gtk_len != 32) {
" Group Cipher key length %d.", gtk_len);
return;
}
key_rsc_len = 6;
alg = WPA_ALG_TKIP;
break;
case WPA_CIPHER_WEP104:
if (gtk_len != 13) {
"WEP104 Group Cipher key length " "%d.", gtk_len);
return;
}
alg = WPA_ALG_WEP;
break;
case WPA_CIPHER_WEP40:
if (gtk_len != 5) {
"WEP40 Group Cipher key length %d.", gtk_len);
return;
}
alg = WPA_ALG_WEP;
break;
default:
return;
}
/*
*/
}
(uint8_t *)"\xff\xff\xff\xff\xff\xff",
"GTK to the driver (Group only).");
"the driver.");
}
}
static void
{
int rlen;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *reply;
unsigned char *rbuf;
"underflow (ie=%d len=%d)",
break;
}
if (*pos == RSN_INFO_ELEM) {
} else if (pos[0] == GENERIC_INFO_ELEM &&
RSN_SELECTOR_LEN) == 0) {
if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
"in unencrypted key data");
return;
}
break;
}
} else {
" ie_len=%d > extra_len=%d",
return;
}
}
wpa_supplicant_req_scan(wpa_s, 0, 0);
return;
}
"Handshake differs from 3 of 4-Way Handshake - drop"
return;
}
switch (wpa_s->pairwise_cipher) {
case WPA_CIPHER_CCMP:
if (keylen != 16) {
return;
}
break;
case WPA_CIPHER_TKIP:
if (keylen != 32) {
return;
}
break;
}
return;
/*
* SNonce was successfully used in msg 3/4, so mark it to be renewed
* for the next 4-Way Handshake. If msg 3 is received again, the old
* SNonce will still be used to avoid changing PTK.
*/
if (key_info & WPA_KEY_INFO_INSTALL) {
switch (wpa_s->pairwise_cipher) {
case WPA_CIPHER_CCMP:
alg = WPA_ALG_CCMP;
keylen = 16;
rsclen = 6;
break;
case WPA_CIPHER_TKIP:
alg = WPA_ALG_TKIP;
keylen = 32;
rsclen = 6;
break;
case WPA_CIPHER_NONE:
"NONE - do not use pairwise keys");
return;
default:
return;
}
} else {
}
" driver.");
}
}
if (gtk)
}
static void
{
int rlen;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *reply;
unsigned char *rbuf;
size_t gtk_ie_len = 0;
"underflow (ie=%d len=%d)",
break;
}
if (pos[0] == GENERIC_INFO_ELEM &&
RSN_SELECTOR_LEN) == 0) {
if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
"in unencrypted key data");
return;
}
break;
break;
}
}
"message 1/2");
return;
}
} else {
if (keydatalen > extra_len) {
" key_data_length=%d > extra_len=%d",
return;
}
if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
maxkeylen -= 8;
}
switch (wpa_s->group_cipher) {
case WPA_CIPHER_CCMP:
return;
}
key_rsc_len = 6;
alg = WPA_ALG_CCMP;
break;
case WPA_CIPHER_TKIP:
return;
}
alg = WPA_ALG_TKIP;
break;
case WPA_CIPHER_WEP104:
return;
}
alg = WPA_ALG_WEP;
break;
case WPA_CIPHER_WEP40:
return;
}
alg = WPA_ALG_WEP;
break;
default:
return;
}
"WPA: received GTK in group key handshake",
gtk_ie, gtk_ie_len);
return;
}
} else {
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (keydatalen % 8) {
"AES-WRAP len %d", keydatalen);
return;
}
"failed - could not decrypt GTK");
return;
}
}
/*
* Ignore Tx bit in Group Key message if a pairwise key
* is used. Some APs seem to setting this bit
* (incorrectly, since Tx is only when doing Group Key
* only APs) and without this workaround, the data
* connection does not work because wpa_supplicant
* configured non-zero keyidx to be used for unicast.
*/
"pairwise keys are used - ignore Tx bit");
tx = 0;
}
}
/*
*/
}
(uint8_t *)"\xff\xff\xff\xff\xff\xff",
" driver (Group only).");
(uint8_t *)"\xff\xff\xff\xff\xff\xff",
"driver.");
}
return;
}
static int
{
int ok = 0;
"when using TPTK - ignoring TPTK");
} else {
ok = 1;
}
}
"- dropping packet");
return (-1);
}
ok = 1;
}
if (!ok) {
"- dropping packet");
return (-1);
}
return (0);
}
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
static int
{
"cannot decrypt EAPOL-Key key data.");
return (-1);
}
/*
* Decrypt key data here so that this operation does not need
* to be implemented separately for each message type.
*/
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (keydatalen % 8) {
"AES-WRAP len %d", keydatalen);
return (-1);
}
"AES-UNWRAP buffer");
return (-1);
}
"could not decrypt EAPOL-Key key data");
return (-1);
}
}
return (0);
}
static void
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
"expecting at least %u",
return;
}
return;
}
return;
}
return;
}
}
if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
"version %d.", ver);
return;
}
"descriptor version (%d) is not 2.", ver);
!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
/*
* Earlier versions of IEEE 802.11i did not explicitly
* require version 2 descriptor for all EAPOL-Key
* packets, so allow group keys to use version 1 if
* CCMP is not used for them.
*/
"allow invalid version for non-CCMP group keys");
} else
return;
}
if (wpa_s->rx_replay_counter_set &&
WPA_REPLAY_COUNTER_LEN) <= 0) {
" increase - dropping packet");
return;
}
if (!(key_info & WPA_KEY_INFO_ACK)) {
return;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
"dropped");
return;
}
if ((key_info & WPA_KEY_INFO_MIC) &&
data_len)) {
return;
}
return;
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
"(Pairwise) with non-zero key index");
return;
}
if (key_info & WPA_KEY_INFO_MIC) {
/* 3/4 4-Way Handshake */
} else {
/* 1/4 4-Way Handshake */
ver);
}
} else {
if (key_info & WPA_KEY_INFO_MIC) {
/* 1/2 Group Key Handshake */
} else {
"without Mic bit - dropped");
}
}
}
void
{
if (wpa_s->eapol_received == 0) {
/* Timeout for completing IEEE 802.1X and WPA authentication */
70 : 10, 0);
}
wpa_s->eapol_received++;
/*
* Source address of the incoming EAPOL frame could be compared to the
* current BSSID. However, it is possible that a centralized
* Authenticator could be using another MAC address than the BSSID of
* an AP, so just allow any address to be used for now. The replies are
* still sent to the current BSSID (if available), though.
*/
}